mirror of
https://github.com/corda/corda.git
synced 2025-02-06 19:19:19 +00:00
Merge branch 'release/os/4.12' into merge-release/os/4.11-release/os/4.12-2023-12-15-79
This commit is contained in:
commit
b47d5ec5c7
3865
.ci/api-current.txt
3865
.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 */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
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
|
* the branch name of origin branch, it should match the current branch
|
||||||
* and it acts as a fail-safe inside {@code forwardMerger} pipeline
|
* and it acts as a fail-safe inside {@code forwardMerger} pipeline
|
||||||
*/
|
*/
|
||||||
String originBranch = 'release/os/4.11'
|
String originBranch = 'release/os/4.12'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* the branch name of target branch, it should be the branch with the next version
|
* the branch name of target branch, it should be the branch with the next version
|
||||||
* after the one in current branch.
|
* after the one in current branch.
|
||||||
*/
|
*/
|
||||||
String targetBranch = 'release/os/4.12'
|
String targetBranch = 'release/os/4.13'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Forward merge any changes between #originBranch and #targetBranch
|
* Forward merge any changes between #originBranch and #targetBranch
|
||||||
|
1
.ci/dev/nightly-regression/Jenkinsfile
vendored
1
.ci/dev/nightly-regression/Jenkinsfile
vendored
@ -45,6 +45,7 @@ pipeline {
|
|||||||
CORDA_ARTIFACTORY_PASSWORD = "${env.ARTIFACTORY_CREDENTIALS_PSW}"
|
CORDA_ARTIFACTORY_PASSWORD = "${env.ARTIFACTORY_CREDENTIALS_PSW}"
|
||||||
CORDA_ARTIFACTORY_USERNAME = "${env.ARTIFACTORY_CREDENTIALS_USR}"
|
CORDA_ARTIFACTORY_USERNAME = "${env.ARTIFACTORY_CREDENTIALS_USR}"
|
||||||
CORDA_USE_CACHE = "corda-remotes"
|
CORDA_USE_CACHE = "corda-remotes"
|
||||||
|
JAVA_HOME = "/usr/lib/jvm/java-17-amazon-corretto"
|
||||||
}
|
}
|
||||||
|
|
||||||
stages {
|
stages {
|
||||||
|
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')
|
ARTIFACTORY_CREDENTIALS = credentials('artifactory-credentials')
|
||||||
CORDA_ARTIFACTORY_USERNAME = "${env.ARTIFACTORY_CREDENTIALS_USR}"
|
CORDA_ARTIFACTORY_USERNAME = "${env.ARTIFACTORY_CREDENTIALS_USR}"
|
||||||
CORDA_ARTIFACTORY_PASSWORD = "${env.ARTIFACTORY_CREDENTIALS_PSW}"
|
CORDA_ARTIFACTORY_PASSWORD = "${env.ARTIFACTORY_CREDENTIALS_PSW}"
|
||||||
|
JAVA_HOME = "/usr/lib/jvm/java-17-amazon-corretto"
|
||||||
}
|
}
|
||||||
|
|
||||||
stages {
|
stages {
|
||||||
|
@ -35,6 +35,7 @@ pipeline {
|
|||||||
ARTIFACTORY_BUILD_NAME = "Corda / Publish / Publish Nightly to Artifactory"
|
ARTIFACTORY_BUILD_NAME = "Corda / Publish / Publish Nightly to Artifactory"
|
||||||
.replaceAll("/", " :: ")
|
.replaceAll("/", " :: ")
|
||||||
DOCKER_URL = "https://index.docker.io/v1/"
|
DOCKER_URL = "https://index.docker.io/v1/"
|
||||||
|
JAVA_HOME = "/usr/lib/jvm/java-17-amazon-corretto"
|
||||||
}
|
}
|
||||||
|
|
||||||
stages {
|
stages {
|
||||||
|
@ -24,6 +24,7 @@ pipeline {
|
|||||||
// in the name
|
// in the name
|
||||||
ARTIFACTORY_BUILD_NAME = "Corda / Publish / Publish Preview to Artifactory"
|
ARTIFACTORY_BUILD_NAME = "Corda / Publish / Publish Preview to Artifactory"
|
||||||
.replaceAll("/", " :: ")
|
.replaceAll("/", " :: ")
|
||||||
|
JAVA_HOME = "/usr/lib/jvm/java-17-amazon-corretto"
|
||||||
}
|
}
|
||||||
|
|
||||||
stages {
|
stages {
|
||||||
|
1
.ci/dev/regression/Jenkinsfile
vendored
1
.ci/dev/regression/Jenkinsfile
vendored
@ -70,6 +70,7 @@ pipeline {
|
|||||||
SNYK_API_KEY = "c4-os-snyk" //Jenkins credential type: Snyk Api token
|
SNYK_API_KEY = "c4-os-snyk" //Jenkins credential type: Snyk Api token
|
||||||
SNYK_TOKEN = credentials('c4-os-snyk-api-token-secret') //Jenkins credential type: Secret text
|
SNYK_TOKEN = credentials('c4-os-snyk-api-token-secret') //Jenkins credential type: Secret text
|
||||||
C4_OS_SNYK_ORG_ID = credentials('corda4-os-snyk-org-id')
|
C4_OS_SNYK_ORG_ID = credentials('corda4-os-snyk-org-id')
|
||||||
|
JAVA_HOME = "/usr/lib/jvm/java-17-amazon-corretto"
|
||||||
}
|
}
|
||||||
|
|
||||||
stages {
|
stages {
|
||||||
|
19
Jenkinsfile
vendored
19
Jenkinsfile
vendored
@ -53,6 +53,7 @@ pipeline {
|
|||||||
CORDA_ARTIFACTORY_USERNAME = "${env.ARTIFACTORY_CREDENTIALS_USR}"
|
CORDA_ARTIFACTORY_USERNAME = "${env.ARTIFACTORY_CREDENTIALS_USR}"
|
||||||
CORDA_GRADLE_SCAN_KEY = credentials('gradle-build-scans-key')
|
CORDA_GRADLE_SCAN_KEY = credentials('gradle-build-scans-key')
|
||||||
CORDA_USE_CACHE = "corda-remotes"
|
CORDA_USE_CACHE = "corda-remotes"
|
||||||
|
JAVA_HOME="/usr/lib/jvm/java-17-amazon-corretto"
|
||||||
}
|
}
|
||||||
|
|
||||||
stages {
|
stages {
|
||||||
@ -119,6 +120,24 @@ pipeline {
|
|||||||
].join(' ')
|
].join(' ')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
stage('Smoke Test') {
|
||||||
|
steps {
|
||||||
|
sh script: [
|
||||||
|
'./gradlew',
|
||||||
|
COMMON_GRADLE_PARAMS,
|
||||||
|
'smokeTest'
|
||||||
|
].join(' ')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stage('Slow Integration Test') {
|
||||||
|
steps {
|
||||||
|
sh script: [
|
||||||
|
'./gradlew',
|
||||||
|
COMMON_GRADLE_PARAMS,
|
||||||
|
'slowIntegrationTest'
|
||||||
|
].join(' ')
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
stage('Same agent') {
|
stage('Same agent') {
|
||||||
|
@ -1,11 +1,55 @@
|
|||||||
|
plugins {
|
||||||
|
id 'groovy-gradle-plugin'
|
||||||
|
}
|
||||||
|
|
||||||
Properties constants = new Properties()
|
Properties constants = new Properties()
|
||||||
file("$rootDir/../constants.properties").withInputStream { constants.load(it) }
|
file("$rootDir/../constants.properties").withInputStream { constants.load(it) }
|
||||||
|
|
||||||
|
def internalPublishVersion = constants.getProperty('internalPublishVersion')
|
||||||
|
def artifactoryContextUrl = constants.getProperty('artifactoryContextUrl')
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
mavenCentral()
|
def cordaUseCache = System.getenv("CORDA_USE_CACHE")
|
||||||
|
if (cordaUseCache != null) {
|
||||||
|
maven {
|
||||||
|
url = "${artifactoryContextUrl}/${cordaUseCache}"
|
||||||
|
name = "R3 Maven remote repositories"
|
||||||
|
authentication {
|
||||||
|
basic(BasicAuthentication)
|
||||||
|
}
|
||||||
|
credentials {
|
||||||
|
username = findProperty('cordaArtifactoryUsername') ?: System.getenv('CORDA_ARTIFACTORY_USERNAME')
|
||||||
|
password = findProperty('cordaArtifactoryPassword') ?: System.getenv('CORDA_ARTIFACTORY_PASSWORD')
|
||||||
|
}
|
||||||
|
metadataSources {
|
||||||
|
mavenPom()
|
||||||
|
artifact()
|
||||||
|
ignoreGradleMetadataRedirection()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
maven {
|
||||||
|
url "${artifactoryContextUrl}/engineering-tools-maven"
|
||||||
|
authentication {
|
||||||
|
basic(BasicAuthentication)
|
||||||
|
}
|
||||||
|
credentials {
|
||||||
|
username = findProperty('cordaArtifactoryUsername') ?: System.getenv('CORDA_ARTIFACTORY_USERNAME')
|
||||||
|
password = findProperty('cordaArtifactoryPassword') ?: System.getenv('CORDA_ARTIFACTORY_PASSWORD')
|
||||||
|
}
|
||||||
|
content {
|
||||||
|
includeGroupByRegex 'com\\.r3\\.internal(\\..*)?'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
gradlePluginPortal()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
compile group: 'com.github.docker-java', name: 'docker-java', version: constants.dockerJavaVersion
|
implementation 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 group: 'com.github.docker-java', name: 'docker-java-transport-httpclient5', version: constants.dockerJavaVersion
|
||||||
|
|
||||||
|
if (System.getenv('CORDA_ARTIFACTORY_USERNAME') != null || project.hasProperty('cordaArtifactoryUsername')) {
|
||||||
|
implementation "com.r3.internal.gradle.plugins:publish:$internalPublishVersion"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
60
buildSrc/src/main/groovy/corda.common-publishing.gradle
Normal file
60
buildSrc/src/main/groovy/corda.common-publishing.gradle
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
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')
|
||||||
|
} 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')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
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,35 @@
|
|||||||
apply plugin: 'java'
|
apply plugin: 'java'
|
||||||
apply plugin: 'kotlin'
|
apply plugin: 'org.jetbrains.kotlin.jvm'
|
||||||
apply plugin: 'net.corda.plugins.publish-utils'
|
|
||||||
apply plugin: 'net.corda.plugins.api-scanner'
|
apply plugin: 'net.corda.plugins.api-scanner'
|
||||||
apply plugin: 'com.jfrog.artifactory'
|
apply plugin: 'corda.common-publishing'
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
compile project(':serialization')
|
api project(':core')
|
||||||
|
|
||||||
|
implementation project(':serialization')
|
||||||
|
|
||||||
compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
|
|
||||||
// Jackson and its plugins: parsing to/from JSON and other textual formats.
|
// Jackson and its plugins: parsing to/from JSON and other textual formats.
|
||||||
compile("com.fasterxml.jackson.module:jackson-module-kotlin:$jackson_kotlin_version") {
|
implementation("com.fasterxml.jackson.module:jackson-module-kotlin:$jackson_kotlin_version") {
|
||||||
exclude module: "jackson-databind"
|
exclude module: "jackson-databind"
|
||||||
}
|
}
|
||||||
// Yaml is useful for parsing strings to method calls.
|
// Yaml is useful for parsing strings to method calls.
|
||||||
compile "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:$jackson_version"
|
implementation "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:$jackson_version"
|
||||||
// This adds support for java.time types.
|
// This adds support for java.time types.
|
||||||
compile "com.fasterxml.jackson.datatype:jackson-datatype-jsr310:$jackson_version"
|
implementation "com.fasterxml.jackson.datatype:jackson-datatype-jsr310:$jackson_version"
|
||||||
compile "com.google.guava:guava:$guava_version"
|
implementation "com.google.guava:guava:$guava_version"
|
||||||
|
|
||||||
testCompile project(':test-utils')
|
// Bouncy castle support needed for X509 certificate manipulation
|
||||||
testCompile project(path: ':core', configuration: 'testArtifacts')
|
implementation "org.bouncycastle:bcprov-jdk18on:${bouncycastle_version}"
|
||||||
|
implementation "org.bouncycastle:bcpkix-jdk18on:${bouncycastle_version}"
|
||||||
|
implementation "org.slf4j:slf4j-api:$slf4j_version"
|
||||||
|
|
||||||
|
testImplementation project(':finance:workflows')
|
||||||
|
testImplementation project(':node-api')
|
||||||
|
testImplementation project(':test-common')
|
||||||
|
testImplementation project(':core-test-utils')
|
||||||
|
testImplementation project(':test-utils')
|
||||||
|
testImplementation project(":node-driver")
|
||||||
|
testImplementation project(path: ':core', configuration: 'testArtifacts')
|
||||||
|
|
||||||
testImplementation "org.junit.jupiter:junit-jupiter-api:${junit_jupiter_version}"
|
testImplementation "org.junit.jupiter:junit-jupiter-api:${junit_jupiter_version}"
|
||||||
testImplementation "junit:junit:$junit_version"
|
testImplementation "junit:junit:$junit_version"
|
||||||
@ -28,7 +38,7 @@ dependencies {
|
|||||||
testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:${junit_jupiter_version}"
|
testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:${junit_jupiter_version}"
|
||||||
testRuntimeOnly "org.junit.platform:junit-platform-launcher:${junit_platform_version}"
|
testRuntimeOnly "org.junit.platform:junit-platform-launcher:${junit_platform_version}"
|
||||||
|
|
||||||
testCompile "org.jetbrains.kotlin:kotlin-test:$kotlin_version"
|
testImplementation "org.jetbrains.kotlin:kotlin-test:$kotlin_version"
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -39,6 +49,11 @@ jar {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
publish {
|
publishing {
|
||||||
name jar.baseName
|
publications {
|
||||||
|
maven(MavenPublication) {
|
||||||
|
artifactId jar.baseName
|
||||||
|
from components.java
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
@ -315,7 +315,8 @@ object JacksonSupport {
|
|||||||
|
|
||||||
private class CertPathSerializer : JsonSerializer<CertPath>() {
|
private class CertPathSerializer : JsonSerializer<CertPath>() {
|
||||||
override fun serialize(value: CertPath, gen: JsonGenerator, serializers: SerializerProvider) {
|
override fun serialize(value: CertPath, gen: JsonGenerator, serializers: SerializerProvider) {
|
||||||
gen.writeObject(CertPathWrapper(value.type, uncheckedCast(value.certificates)))
|
val certificates = value.certificates as List<X509Certificate>
|
||||||
|
gen.writeObject(CertPathWrapper(value.type, certificates))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,27 +8,36 @@ import com.fasterxml.jackson.databind.node.ObjectNode
|
|||||||
import com.fasterxml.jackson.databind.node.TextNode
|
import com.fasterxml.jackson.databind.node.TextNode
|
||||||
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory
|
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory
|
||||||
import com.fasterxml.jackson.module.kotlin.convertValue
|
import com.fasterxml.jackson.module.kotlin.convertValue
|
||||||
import com.nhaarman.mockito_kotlin.any
|
|
||||||
import com.nhaarman.mockito_kotlin.doReturn
|
|
||||||
import com.nhaarman.mockito_kotlin.mock
|
|
||||||
import com.nhaarman.mockito_kotlin.whenever
|
|
||||||
import com.nhaarman.mockito_kotlin.spy
|
|
||||||
import net.corda.client.jackson.internal.childrenAs
|
import net.corda.client.jackson.internal.childrenAs
|
||||||
import net.corda.client.jackson.internal.valueAs
|
import net.corda.client.jackson.internal.valueAs
|
||||||
import net.corda.core.contracts.*
|
import net.corda.core.contracts.Amount
|
||||||
|
import net.corda.core.contracts.Command
|
||||||
|
import net.corda.core.contracts.LinearState
|
||||||
|
import net.corda.core.contracts.PrivacySalt
|
||||||
|
import net.corda.core.contracts.StateRef
|
||||||
|
import net.corda.core.contracts.TimeWindow
|
||||||
|
import net.corda.core.contracts.TransactionState
|
||||||
|
import net.corda.core.contracts.UniqueIdentifier
|
||||||
import net.corda.core.cordapp.CordappProvider
|
import net.corda.core.cordapp.CordappProvider
|
||||||
import net.corda.core.crypto.*
|
|
||||||
import net.corda.core.crypto.CompositeKey
|
import net.corda.core.crypto.CompositeKey
|
||||||
|
import net.corda.core.crypto.Crypto
|
||||||
|
import net.corda.core.crypto.DigestService
|
||||||
|
import net.corda.core.crypto.DigitalSignature
|
||||||
|
import net.corda.core.crypto.PartialMerkleTree
|
||||||
import net.corda.core.crypto.PartialMerkleTree.PartialTree
|
import net.corda.core.crypto.PartialMerkleTree.PartialTree
|
||||||
import net.corda.core.identity.*
|
import net.corda.core.crypto.SecureHash
|
||||||
import net.corda.core.internal.AbstractAttachment
|
import net.corda.core.crypto.SignatureMetadata
|
||||||
|
import net.corda.core.crypto.SignatureScheme
|
||||||
|
import net.corda.core.crypto.TransactionSignature
|
||||||
|
import net.corda.core.crypto.secureRandomBytes
|
||||||
|
import net.corda.core.identity.AbstractParty
|
||||||
|
import net.corda.core.identity.AnonymousParty
|
||||||
|
import net.corda.core.identity.CordaX500Name
|
||||||
|
import net.corda.core.identity.Party
|
||||||
|
import net.corda.core.identity.PartyAndCertificate
|
||||||
import net.corda.core.internal.DigitalSignatureWithCert
|
import net.corda.core.internal.DigitalSignatureWithCert
|
||||||
import net.corda.core.node.NodeInfo
|
import net.corda.core.node.NodeInfo
|
||||||
import net.corda.core.node.ServiceHub
|
import net.corda.core.node.ServiceHub
|
||||||
import net.corda.core.node.services.AttachmentStorage
|
|
||||||
import net.corda.core.node.services.IdentityService
|
|
||||||
import net.corda.core.node.services.NetworkParametersService
|
|
||||||
import net.corda.core.node.services.TransactionStorage
|
|
||||||
import net.corda.core.serialization.CordaSerializable
|
import net.corda.core.serialization.CordaSerializable
|
||||||
import net.corda.core.serialization.SerializedBytes
|
import net.corda.core.serialization.SerializedBytes
|
||||||
import net.corda.core.serialization.deserialize
|
import net.corda.core.serialization.deserialize
|
||||||
@ -37,14 +46,27 @@ import net.corda.core.transactions.CoreTransaction
|
|||||||
import net.corda.core.transactions.SignedTransaction
|
import net.corda.core.transactions.SignedTransaction
|
||||||
import net.corda.core.transactions.TransactionBuilder
|
import net.corda.core.transactions.TransactionBuilder
|
||||||
import net.corda.core.transactions.WireTransaction
|
import net.corda.core.transactions.WireTransaction
|
||||||
import net.corda.core.utilities.*
|
import net.corda.core.utilities.ByteSequence
|
||||||
|
import net.corda.core.utilities.NetworkHostAndPort
|
||||||
|
import net.corda.core.utilities.OpaqueBytes
|
||||||
|
import net.corda.core.utilities.days
|
||||||
|
import net.corda.core.utilities.hours
|
||||||
|
import net.corda.core.utilities.toBase58String
|
||||||
|
import net.corda.core.utilities.toBase64
|
||||||
|
import net.corda.core.utilities.toHexString
|
||||||
|
import net.corda.coretesting.internal.createNodeInfoAndSigned
|
||||||
|
import net.corda.coretesting.internal.rigorousMock
|
||||||
import net.corda.finance.USD
|
import net.corda.finance.USD
|
||||||
import net.corda.nodeapi.internal.crypto.x509Certificates
|
import net.corda.nodeapi.internal.crypto.x509Certificates
|
||||||
import net.corda.testing.common.internal.testNetworkParameters
|
import net.corda.testing.common.internal.testNetworkParameters
|
||||||
import net.corda.testing.contracts.DummyContract
|
import net.corda.testing.contracts.DummyContract
|
||||||
import net.corda.testing.core.*
|
import net.corda.testing.core.ALICE_NAME
|
||||||
import net.corda.coretesting.internal.createNodeInfoAndSigned
|
import net.corda.testing.core.BOB_NAME
|
||||||
import net.corda.coretesting.internal.rigorousMock
|
import net.corda.testing.core.DUMMY_NOTARY_NAME
|
||||||
|
import net.corda.testing.core.DummyCommandData
|
||||||
|
import net.corda.testing.core.SerializationEnvironmentRule
|
||||||
|
import net.corda.testing.core.TestIdentity
|
||||||
|
import net.corda.testing.node.MockServices
|
||||||
import org.assertj.core.api.Assertions.assertThat
|
import org.assertj.core.api.Assertions.assertThat
|
||||||
import org.assertj.core.api.Assertions.assertThatThrownBy
|
import org.assertj.core.api.Assertions.assertThatThrownBy
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
@ -54,15 +76,22 @@ import org.junit.jupiter.api.TestFactory
|
|||||||
import org.junit.runner.RunWith
|
import org.junit.runner.RunWith
|
||||||
import org.junit.runners.Parameterized
|
import org.junit.runners.Parameterized
|
||||||
import org.junit.runners.Parameterized.Parameters
|
import org.junit.runners.Parameterized.Parameters
|
||||||
|
import org.mockito.kotlin.spy
|
||||||
|
import org.mockito.kotlin.whenever
|
||||||
import java.math.BigInteger
|
import java.math.BigInteger
|
||||||
import java.nio.charset.StandardCharsets.UTF_8
|
import java.nio.charset.StandardCharsets.UTF_8
|
||||||
import java.security.PublicKey
|
import java.security.PublicKey
|
||||||
import java.security.cert.CertPath
|
import java.security.cert.CertPath
|
||||||
import java.security.cert.X509Certificate
|
import java.security.cert.X509Certificate
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
import java.util.*
|
import java.util.Currency
|
||||||
|
import java.util.Date
|
||||||
|
import java.util.UUID
|
||||||
import javax.security.auth.x500.X500Principal
|
import javax.security.auth.x500.X500Principal
|
||||||
import kotlin.collections.ArrayList
|
import kotlin.collections.component1
|
||||||
|
import kotlin.collections.component2
|
||||||
|
import kotlin.collections.component3
|
||||||
|
import kotlin.collections.component4
|
||||||
|
|
||||||
@RunWith(Parameterized::class)
|
@RunWith(Parameterized::class)
|
||||||
class JacksonSupportTest(@Suppress("unused") private val name: String, factory: JsonFactory) {
|
class JacksonSupportTest(@Suppress("unused") private val name: String, factory: JsonFactory) {
|
||||||
@ -90,23 +119,12 @@ class JacksonSupportTest(@Suppress("unused") private val name: String, factory:
|
|||||||
|
|
||||||
@Before
|
@Before
|
||||||
fun setup() {
|
fun setup() {
|
||||||
val unsignedAttachment = object : AbstractAttachment({ byteArrayOf() }, "test") {
|
services = MockServices(
|
||||||
override val id: SecureHash get() = throw UnsupportedOperationException()
|
listOf("net.corda.testing.contracts"),
|
||||||
}
|
MINI_CORP,
|
||||||
|
testNetworkParameters(minimumPlatformVersion = 4)
|
||||||
val attachments = rigorousMock<AttachmentStorage>().also {
|
)
|
||||||
doReturn(unsignedAttachment).whenever(it).openAttachment(any())
|
|
||||||
}
|
|
||||||
services = rigorousMock()
|
|
||||||
cordappProvider = rigorousMock()
|
cordappProvider = rigorousMock()
|
||||||
val networkParameters = testNetworkParameters(minimumPlatformVersion = 4)
|
|
||||||
val networkParametersService = rigorousMock<NetworkParametersService>().also {
|
|
||||||
doReturn(networkParameters.serialize().hash).whenever(it).currentHash
|
|
||||||
}
|
|
||||||
doReturn(networkParametersService).whenever(services).networkParametersService
|
|
||||||
doReturn(cordappProvider).whenever(services).cordappProvider
|
|
||||||
doReturn(networkParameters).whenever(services).networkParameters
|
|
||||||
doReturn(attachments).whenever(services).attachments
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(timeout=300_000)
|
@Test(timeout=300_000)
|
||||||
@ -263,17 +281,6 @@ class JacksonSupportTest(@Suppress("unused") private val name: String, factory:
|
|||||||
@Test(timeout=300_000)
|
@Test(timeout=300_000)
|
||||||
fun `SignedTransaction (WireTransaction)`() {
|
fun `SignedTransaction (WireTransaction)`() {
|
||||||
val attachmentId = SecureHash.randomSHA256()
|
val attachmentId = SecureHash.randomSHA256()
|
||||||
doReturn(attachmentId).whenever(cordappProvider).getContractAttachmentID(DummyContract.PROGRAM_ID)
|
|
||||||
val attachmentStorage = rigorousMock<AttachmentStorage>()
|
|
||||||
doReturn(attachmentStorage).whenever(services).attachments
|
|
||||||
doReturn(mock<TransactionStorage>()).whenever(services).validatedTransactions
|
|
||||||
doReturn(mock<IdentityService>()).whenever(services).identityService
|
|
||||||
val attachment = rigorousMock<ContractAttachment>()
|
|
||||||
doReturn(attachment).whenever(attachmentStorage).openAttachment(attachmentId)
|
|
||||||
doReturn(attachmentId).whenever(attachment).id
|
|
||||||
doReturn(emptyList<Party>()).whenever(attachment).signerKeys
|
|
||||||
doReturn(setOf(DummyContract.PROGRAM_ID)).whenever(attachment).allContracts
|
|
||||||
doReturn("app").whenever(attachment).uploader
|
|
||||||
|
|
||||||
val wtx = TransactionBuilder(
|
val wtx = TransactionBuilder(
|
||||||
notary = DUMMY_NOTARY,
|
notary = DUMMY_NOTARY,
|
||||||
|
@ -1,28 +1,27 @@
|
|||||||
// JDK 11 JavaFX
|
// JDK 11 JavaFX
|
||||||
plugins {
|
plugins {
|
||||||
id 'org.openjfx.javafxplugin' version '0.0.7' apply false
|
id 'org.openjfx.javafxplugin' version '0.0.7' apply false
|
||||||
|
id 'corda.common-publishing'
|
||||||
}
|
}
|
||||||
|
|
||||||
if (JavaVersion.current().isJava9Compatible()) {
|
|
||||||
apply plugin: 'org.openjfx.javafxplugin'
|
apply plugin: 'org.openjfx.javafxplugin'
|
||||||
javafx {
|
javafx {
|
||||||
version = "11.0.2"
|
version = "11.0.2"
|
||||||
modules = ['javafx.controls',
|
modules = [
|
||||||
|
'javafx.controls',
|
||||||
'javafx.fxml'
|
'javafx.fxml'
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
|
||||||
apply plugin: 'kotlin'
|
apply plugin: 'org.jetbrains.kotlin.jvm'
|
||||||
apply plugin: 'net.corda.plugins.quasar-utils'
|
apply plugin: 'net.corda.plugins.quasar-utils'
|
||||||
apply plugin: 'net.corda.plugins.publish-utils'
|
|
||||||
apply plugin: 'com.jfrog.artifactory'
|
|
||||||
|
|
||||||
description 'Corda client JavaFX modules'
|
description 'Corda client JavaFX modules'
|
||||||
|
|
||||||
//noinspection GroovyAssignabilityCheck
|
//noinspection GroovyAssignabilityCheck
|
||||||
configurations {
|
configurations {
|
||||||
integrationTestCompile.extendsFrom testCompile
|
integrationTestImplementation.extendsFrom testImplementation
|
||||||
integrationTestRuntime.extendsFrom testRuntime
|
integrationTestRuntime.extendsFrom testRuntimeOnly
|
||||||
}
|
}
|
||||||
|
|
||||||
sourceSets {
|
sourceSets {
|
||||||
@ -39,23 +38,26 @@ sourceSets {
|
|||||||
// build/reports/project/dependencies/index.html for green highlighted parts of the tree.
|
// build/reports/project/dependencies/index.html for green highlighted parts of the tree.
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
compile project(':core')
|
implementation project(':core')
|
||||||
compile project(':finance:contracts')
|
implementation project(':finance:contracts')
|
||||||
compile project(':finance:workflows')
|
implementation project(':finance:workflows')
|
||||||
compile project(':client:rpc')
|
implementation project(':client:rpc')
|
||||||
|
|
||||||
compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
|
implementation "com.google.guava:guava:$guava_version"
|
||||||
compile "com.google.guava:guava:$guava_version"
|
implementation "io.reactivex:rxjava:$rxjava_version"
|
||||||
|
|
||||||
|
// For caches rather than guava
|
||||||
|
implementation "com.github.ben-manes.caffeine:caffeine:$caffeine_version"
|
||||||
|
|
||||||
// ReactFX: Functional reactive UI programming.
|
// ReactFX: Functional reactive UI programming.
|
||||||
compile 'org.reactfx:reactfx:2.0-M5'
|
implementation 'org.reactfx:reactfx:2.0-M5'
|
||||||
compile 'org.fxmisc.easybind:easybind:1.0.3'
|
implementation 'org.fxmisc.easybind:easybind:1.0.3'
|
||||||
|
|
||||||
// Artemis Client: ability to connect to an Artemis broker and control it.
|
// Artemis Client: ability to connect to an Artemis broker and control it.
|
||||||
// TODO: remove the forced update of commons-collections and beanutils when artemis updates them
|
// TODO: remove the forced update of commons-collections and beanutils when artemis updates them
|
||||||
compile "org.apache.commons:commons-collections4:${commons_collections_version}"
|
implementation "org.apache.commons:commons-collections4:${commons_collections_version}"
|
||||||
compile "commons-beanutils:commons-beanutils:${beanutils_version}"
|
implementation "commons-beanutils:commons-beanutils:${beanutils_version}"
|
||||||
compile("org.apache.activemq:artemis-core-client:${artemis_version}") {
|
implementation("org.apache.activemq:artemis-core-client:${artemis_version}") {
|
||||||
exclude group: 'org.jgroups', module: 'jgroups'
|
exclude group: 'org.jgroups', module: 'jgroups'
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -67,13 +69,14 @@ dependencies {
|
|||||||
testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:${junit_jupiter_version}"
|
testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:${junit_jupiter_version}"
|
||||||
testRuntimeOnly "org.junit.platform:junit-platform-launcher:${junit_platform_version}"
|
testRuntimeOnly "org.junit.platform:junit-platform-launcher:${junit_platform_version}"
|
||||||
|
|
||||||
testCompile "org.assertj:assertj-core:${assertj_version}"
|
testImplementation "org.jetbrains.kotlin:kotlin-test:$kotlin_version"
|
||||||
|
testImplementation "org.assertj:assertj-core:${assertj_version}"
|
||||||
|
|
||||||
testCompile project(':test-utils')
|
testImplementation project(':test-utils')
|
||||||
|
|
||||||
// Integration test helpers
|
// Integration test helpers
|
||||||
integrationTestCompile "junit:junit:$junit_version"
|
integrationTestImplementation "junit:junit:$junit_version"
|
||||||
integrationTestCompile project(':node-driver')
|
integrationTestImplementation project(':node-driver')
|
||||||
}
|
}
|
||||||
|
|
||||||
task integrationTest(type: Test) {
|
task integrationTest(type: Test) {
|
||||||
@ -88,6 +91,11 @@ jar {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
publish {
|
publishing {
|
||||||
name jar.baseName
|
publications {
|
||||||
|
maven(MavenPublication) {
|
||||||
|
artifactId jar.baseName
|
||||||
|
from components.java
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,13 +14,14 @@ import java.util.stream.Collectors
|
|||||||
* Utility bindings for the [Amount] type, similar in spirit to [Bindings]
|
* Utility bindings for the [Amount] type, similar in spirit to [Bindings]
|
||||||
*/
|
*/
|
||||||
object AmountBindings {
|
object AmountBindings {
|
||||||
|
@Suppress("SpreadOperator")
|
||||||
fun <T : Any> sum(amounts: ObservableList<Amount<T>>, token: T): MonadicBinding<Amount<T>> = EasyBind.map(
|
fun <T : Any> sum(amounts: ObservableList<Amount<T>>, token: T): MonadicBinding<Amount<T>> = EasyBind.map(
|
||||||
Bindings.createLongBinding({
|
Bindings.createLongBinding({
|
||||||
amounts.stream().collect(Collectors.summingLong {
|
amounts.stream().collect(Collectors.summingLong {
|
||||||
require(it.token == token)
|
require(it.token == token)
|
||||||
it.quantity
|
it.quantity
|
||||||
})
|
})
|
||||||
}, arrayOf(amounts))
|
}, *arrayOf(amounts))
|
||||||
) { sum -> Amount(sum.toLong(), token) }
|
) { sum -> Amount(sum.toLong(), token) }
|
||||||
|
|
||||||
fun exchange(
|
fun exchange(
|
||||||
@ -35,6 +36,7 @@ object AmountBindings {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Suppress("SpreadOperator")
|
||||||
fun sumAmountExchange(
|
fun sumAmountExchange(
|
||||||
amounts: ObservableList<Amount<Currency>>,
|
amounts: ObservableList<Amount<Currency>>,
|
||||||
currency: ObservableValue<Currency>,
|
currency: ObservableValue<Currency>,
|
||||||
@ -45,7 +47,7 @@ object AmountBindings {
|
|||||||
EasyBind.map(
|
EasyBind.map(
|
||||||
Bindings.createLongBinding({
|
Bindings.createLongBinding({
|
||||||
amounts.stream().collect(Collectors.summingLong { exchange(it) })
|
amounts.stream().collect(Collectors.summingLong { exchange(it) })
|
||||||
}, arrayOf(amounts))
|
}, *arrayOf(amounts))
|
||||||
) { Amount(it.toLong(), currencyValue) }
|
) { Amount(it.toLong(), currencyValue) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -120,7 +120,7 @@ fun <A> ObservableList<out A>.filter(predicate: ObservableValue<(A) -> Boolean>)
|
|||||||
*/
|
*/
|
||||||
fun <A> ObservableList<out A?>.filterNotNull(): ObservableList<A> {
|
fun <A> ObservableList<out A?>.filterNotNull(): ObservableList<A> {
|
||||||
//TODO This is a tactical work round for an issue with SAM conversion (https://youtrack.jetbrains.com/issue/ALL-1552) so that the M10 explorer works.
|
//TODO This is a tactical work round for an issue with SAM conversion (https://youtrack.jetbrains.com/issue/ALL-1552) so that the M10 explorer works.
|
||||||
return uncheckedCast(uncheckedCast<Any, ObservableList<A?>>(this).filtered { t -> t != null })
|
return uncheckedCast(uncheckedCast<Any, ObservableList<A>>(this).filtered { t -> t != null })
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -128,6 +128,7 @@ fun <A> ObservableList<out A?>.filterNotNull(): ObservableList<A> {
|
|||||||
* val concatenatedNames = people.foldObservable("", { names, person -> names + person.name })
|
* val concatenatedNames = people.foldObservable("", { names, person -> names + person.name })
|
||||||
* val concatenatedNames2 = people.map(Person::name).fold("", String::plus)
|
* val concatenatedNames2 = people.map(Person::name).fold("", String::plus)
|
||||||
*/
|
*/
|
||||||
|
@Suppress("SpreadOperator")
|
||||||
fun <A, B> ObservableList<out A>.foldObservable(initial: B, folderFunction: (B, A) -> B): ObservableValue<B> {
|
fun <A, B> ObservableList<out A>.foldObservable(initial: B, folderFunction: (B, A) -> B): ObservableValue<B> {
|
||||||
return Bindings.createObjectBinding({
|
return Bindings.createObjectBinding({
|
||||||
var current = initial
|
var current = initial
|
||||||
@ -135,7 +136,7 @@ fun <A, B> ObservableList<out A>.foldObservable(initial: B, folderFunction: (B,
|
|||||||
current = folderFunction(current, it)
|
current = folderFunction(current, it)
|
||||||
}
|
}
|
||||||
current
|
current
|
||||||
}, arrayOf(this))
|
}, *arrayOf(this))
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -285,6 +286,7 @@ fun <A> ObservableList<A>.first(): ObservableValue<A?> {
|
|||||||
return getValueAt(0)
|
return getValueAt(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Suppress("SpreadOperator")
|
||||||
fun <A> ObservableList<A>.last(): ObservableValue<A?> {
|
fun <A> ObservableList<A>.last(): ObservableValue<A?> {
|
||||||
return Bindings.createObjectBinding({
|
return Bindings.createObjectBinding({
|
||||||
if (size > 0) {
|
if (size > 0) {
|
||||||
@ -292,7 +294,7 @@ fun <A> ObservableList<A>.last(): ObservableValue<A?> {
|
|||||||
} else {
|
} else {
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
}, arrayOf(this))
|
}, *arrayOf(this))
|
||||||
}
|
}
|
||||||
|
|
||||||
fun <T : Any> ObservableList<T>.unique(): ObservableList<T> {
|
fun <T : Any> ObservableList<T>.unique(): ObservableList<T> {
|
||||||
@ -303,24 +305,27 @@ fun <T : Any, K : Any> ObservableList<T>.distinctBy(toKey: (T) -> K): Observable
|
|||||||
return AggregatedList(this, toKey, { _, entryList -> entryList[0] })
|
return AggregatedList(this, toKey, { _, entryList -> entryList[0] })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Suppress("SpreadOperator")
|
||||||
fun ObservableValue<*>.isNotNull(): BooleanBinding {
|
fun ObservableValue<*>.isNotNull(): BooleanBinding {
|
||||||
return Bindings.createBooleanBinding({ this.value != null }, arrayOf(this))
|
return Bindings.createBooleanBinding({ this.value != null }, *arrayOf(this))
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return first element of the observable list as observable value.
|
* Return first element of the observable list as observable value.
|
||||||
* Return provided default value if the list is empty.
|
* Return provided default value if the list is empty.
|
||||||
*/
|
*/
|
||||||
|
@Suppress("SpreadOperator")
|
||||||
fun <A> ObservableList<A>.firstOrDefault(default: ObservableValue<A?>, predicate: (A) -> Boolean): ObservableValue<A?> {
|
fun <A> ObservableList<A>.firstOrDefault(default: ObservableValue<A?>, predicate: (A) -> Boolean): ObservableValue<A?> {
|
||||||
return Bindings.createObjectBinding({ this.firstOrNull(predicate) ?: default.value }, arrayOf(this, default))
|
return Bindings.createObjectBinding({ this.firstOrNull(predicate) ?: default.value }, *arrayOf(this, default))
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return first element of the observable list as observable value.
|
* Return first element of the observable list as observable value.
|
||||||
* Return ObservableValue(null) if the list is empty.
|
* Return ObservableValue(null) if the list is empty.
|
||||||
*/
|
*/
|
||||||
|
@Suppress("SpreadOperator")
|
||||||
fun <A> ObservableList<A>.firstOrNullObservable(predicate: (A) -> Boolean): ObservableValue<A?> {
|
fun <A> ObservableList<A>.firstOrNullObservable(predicate: (A) -> Boolean): ObservableValue<A?> {
|
||||||
return Bindings.createObjectBinding({ this.firstOrNull(predicate) }, arrayOf(this))
|
return Bindings.createObjectBinding({ this.firstOrNull(predicate) }, *arrayOf(this))
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
apply plugin: 'kotlin'
|
apply plugin: 'org.jetbrains.kotlin.jvm'
|
||||||
apply plugin: 'net.corda.plugins.quasar-utils'
|
apply plugin: 'net.corda.plugins.quasar-utils'
|
||||||
apply plugin: 'net.corda.plugins.publish-utils'
|
apply plugin: 'corda.common-publishing'
|
||||||
apply plugin: 'com.jfrog.artifactory'
|
|
||||||
|
|
||||||
description 'Corda client mock modules'
|
description 'Corda client mock modules'
|
||||||
|
|
||||||
@ -9,9 +8,9 @@ description 'Corda client mock modules'
|
|||||||
// build/reports/project/dependencies/index.html for green highlighted parts of the tree.
|
// build/reports/project/dependencies/index.html for green highlighted parts of the tree.
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
compile project(":core")
|
implementation project(":core")
|
||||||
compile project(':finance:workflows')
|
implementation project(':finance:workflows')
|
||||||
compile project(':finance:contracts')
|
implementation project(':finance:contracts')
|
||||||
|
|
||||||
testImplementation "org.junit.jupiter:junit-jupiter-api:${junit_jupiter_version}"
|
testImplementation "org.junit.jupiter:junit-jupiter-api:${junit_jupiter_version}"
|
||||||
testImplementation "junit:junit:$junit_version"
|
testImplementation "junit:junit:$junit_version"
|
||||||
@ -21,9 +20,9 @@ dependencies {
|
|||||||
testRuntimeOnly "org.junit.platform:junit-platform-launcher:${junit_platform_version}"
|
testRuntimeOnly "org.junit.platform:junit-platform-launcher:${junit_platform_version}"
|
||||||
|
|
||||||
// Unit testing helpers.
|
// Unit testing helpers.
|
||||||
testCompile "org.assertj:assertj-core:${assertj_version}"
|
testImplementation "org.assertj:assertj-core:${assertj_version}"
|
||||||
|
|
||||||
testCompile project(':test-utils')
|
testImplementation project(':test-utils')
|
||||||
}
|
}
|
||||||
|
|
||||||
jar {
|
jar {
|
||||||
@ -39,6 +38,11 @@ jar {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
publish {
|
publishing {
|
||||||
name jar.baseName
|
publications {
|
||||||
|
maven(MavenPublication) {
|
||||||
|
artifactId jar.baseName
|
||||||
|
from components.java
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,26 +1,15 @@
|
|||||||
apply plugin: 'kotlin'
|
apply plugin: 'org.jetbrains.kotlin.jvm'
|
||||||
apply plugin: 'net.corda.plugins.quasar-utils'
|
apply plugin: 'net.corda.plugins.quasar-utils'
|
||||||
apply plugin: 'net.corda.plugins.publish-utils'
|
|
||||||
apply plugin: 'net.corda.plugins.api-scanner'
|
apply plugin: 'net.corda.plugins.api-scanner'
|
||||||
apply plugin: 'com.jfrog.artifactory'
|
apply plugin: 'corda.common-publishing'
|
||||||
|
apply plugin: 'us.kirchmeier.capsule'
|
||||||
|
|
||||||
description 'Corda client RPC modules'
|
description 'Corda client RPC modules'
|
||||||
|
|
||||||
//noinspection GroovyAssignabilityCheck
|
//noinspection GroovyAssignabilityCheck
|
||||||
configurations {
|
configurations {
|
||||||
integrationTestCompile.extendsFrom testCompile
|
integrationTestImplementation.extendsFrom testImplementation
|
||||||
integrationTestRuntimeOnly.extendsFrom testRuntimeOnly
|
integrationTestRuntimeOnly.extendsFrom testRuntimeOnly
|
||||||
|
|
||||||
smokeTestCompile.extendsFrom compile
|
|
||||||
smokeTestRuntimeOnly.extendsFrom runtimeOnly
|
|
||||||
}
|
|
||||||
|
|
||||||
compileKotlin {
|
|
||||||
kotlinOptions.jvmTarget = "1.8"
|
|
||||||
}
|
|
||||||
|
|
||||||
compileTestKotlin {
|
|
||||||
kotlinOptions.jvmTarget = "1.8"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sourceSets {
|
sourceSets {
|
||||||
@ -39,46 +28,17 @@ sourceSets {
|
|||||||
srcDirs "src/integration-test/resources"
|
srcDirs "src/integration-test/resources"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
smokeTest {
|
|
||||||
kotlin {
|
|
||||||
// We must NOT have any Node code on the classpath, so do NOT
|
|
||||||
// include the test or integrationTest dependencies here.
|
|
||||||
compileClasspath += main.output
|
|
||||||
runtimeClasspath += main.output
|
|
||||||
srcDir file('src/smoke-test/kotlin')
|
|
||||||
}
|
}
|
||||||
java {
|
|
||||||
compileClasspath += main.output
|
|
||||||
runtimeClasspath += main.output
|
|
||||||
srcDir file('src/smoke-test/java')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
processSmokeTestResources {
|
|
||||||
from(project(':node:capsule').tasks['buildCordaJAR']) {
|
|
||||||
rename 'corda-(.*)', 'corda.jar'
|
|
||||||
}
|
|
||||||
from(project(':finance:workflows').tasks['jar']) {
|
|
||||||
rename '.*finance-workflows-.*', 'cordapp-finance-workflows.jar'
|
|
||||||
}
|
|
||||||
from(project(':finance:contracts').tasks['jar']) {
|
|
||||||
rename '.*finance-contracts-.*', 'cordapp-finance-contracts.jar'
|
|
||||||
}
|
|
||||||
from(project(':testing:cordapps:sleeping').tasks['jar']) {
|
|
||||||
rename 'testing-sleeping-cordapp-*', 'cordapp-sleeping.jar'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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 {
|
dependencies {
|
||||||
compile project(':core')
|
implementation project(':core')
|
||||||
compile project(':node-api')
|
implementation project(':node-api')
|
||||||
|
implementation project(':serialization')
|
||||||
|
|
||||||
// For caches rather than guava
|
// For caches rather than guava
|
||||||
compile "com.github.ben-manes.caffeine:caffeine:$caffeine_version"
|
implementation "com.github.ben-manes.caffeine:caffeine:$caffeine_version"
|
||||||
|
|
||||||
|
implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
|
||||||
|
|
||||||
testImplementation "junit:junit:$junit_version"
|
testImplementation "junit:junit:$junit_version"
|
||||||
|
|
||||||
@ -86,38 +46,37 @@ dependencies {
|
|||||||
testRuntimeOnly "org.junit.platform:junit-platform-launcher:${junit_platform_version}"
|
testRuntimeOnly "org.junit.platform:junit-platform-launcher:${junit_platform_version}"
|
||||||
|
|
||||||
// Unit testing helpers.
|
// Unit testing helpers.
|
||||||
testCompile "org.jetbrains.kotlin:kotlin-test:$kotlin_version"
|
testImplementation "org.jetbrains.kotlin:kotlin-test:$kotlin_version"
|
||||||
testCompile "org.assertj:assertj-core:${assertj_version}"
|
testImplementation "org.assertj:assertj-core:${assertj_version}"
|
||||||
|
testImplementation "io.dropwizard.metrics:metrics-core:$metrics_version"
|
||||||
|
|
||||||
testCompile project(':node-driver')
|
testImplementation project(':node')
|
||||||
testCompile project(':client:mock')
|
testImplementation project(':node-driver')
|
||||||
integrationTestCompile project(path: ':node-api', configuration: 'testArtifacts')
|
testImplementation project(':client:mock')
|
||||||
|
testImplementation project(':core-test-utils')
|
||||||
|
|
||||||
// Smoke tests do NOT have any Node code on the classpath!
|
integrationTestImplementation project(path: ':node-api', configuration: 'testArtifacts')
|
||||||
smokeTestCompile project(':smoke-test-utils')
|
integrationTestImplementation project(':common-configuration-parsing')
|
||||||
smokeTestCompile project(':finance:contracts')
|
integrationTestImplementation project(':finance:contracts')
|
||||||
smokeTestCompile project(':finance:workflows')
|
integrationTestImplementation project(':finance:workflows')
|
||||||
smokeTestCompile project(':testing:cordapps:sleeping')
|
integrationTestImplementation project(':test-utils')
|
||||||
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
|
integrationTestImplementation "co.paralleluniverse:quasar-core:$quasar_version"
|
||||||
smokeTestRuntimeOnly "com.esotericsoftware:kryo:$kryo_version"
|
integrationTestImplementation "org.mockito.kotlin:mockito-kotlin:$mockito_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"
|
||||||
}
|
}
|
||||||
|
|
||||||
task integrationTest(type: Test) {
|
task integrationTest(type: Test) {
|
||||||
testClassesDirs = sourceSets.integrationTest.output.classesDirs
|
testClassesDirs = sourceSets.integrationTest.output.classesDirs
|
||||||
classpath = sourceSets.integrationTest.runtimeClasspath
|
classpath = sourceSets.integrationTest.runtimeClasspath
|
||||||
}
|
|
||||||
|
|
||||||
task smokeTest(type: Test) {
|
jvmArgs test_add_opens
|
||||||
testClassesDirs = sourceSets.smokeTest.output.classesDirs
|
jvmArgs test_add_exports
|
||||||
classpath = sourceSets.smokeTest.runtimeClasspath
|
|
||||||
}
|
}
|
||||||
|
|
||||||
jar {
|
jar {
|
||||||
@ -127,6 +86,11 @@ jar {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
publish {
|
publishing {
|
||||||
name jar.baseName
|
publications {
|
||||||
|
maven(MavenPublication) {
|
||||||
|
artifactId jar.baseName
|
||||||
|
from components.java
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -255,12 +255,21 @@ class CordaRPCClientTest : NodeBasedTest(FINANCE_CORDAPPS, notaries = listOf(DUM
|
|||||||
fun `additional class loader used by WireTransaction when it deserialises its components`() {
|
fun `additional class loader used by WireTransaction when it deserialises its components`() {
|
||||||
val financeLocation = Cash::class.java.location.toPath().toString()
|
val financeLocation = Cash::class.java.location.toPath().toString()
|
||||||
val classPathWithoutFinance = ProcessUtilities.defaultClassPath.filter { financeLocation !in it }
|
val classPathWithoutFinance = ProcessUtilities.defaultClassPath.filter { financeLocation !in it }
|
||||||
|
val moduleOpens = listOf(
|
||||||
|
"--add-opens", "java.base/java.time=ALL-UNNAMED", "--add-opens", "java.base/java.io=ALL-UNNAMED",
|
||||||
|
"--add-opens", "java.base/java.util=ALL-UNNAMED", "--add-opens", "java.base/java.net=ALL-UNNAMED",
|
||||||
|
"--add-opens", "java.base/java.nio=ALL-UNNAMED", "--add-opens", "java.base/java.lang.invoke=ALL-UNNAMED",
|
||||||
|
"--add-opens", "java.base/java.security.cert=ALL-UNNAMED", "--add-opens", "java.base/javax.net.ssl=ALL-UNNAMED",
|
||||||
|
"--add-opens", "java.base/java.util.concurrent=ALL-UNNAMED", "--add-opens", "java.sql/java.sql=ALL-UNNAMED",
|
||||||
|
"--add-opens", "java.base/java.lang=ALL-UNNAMED"
|
||||||
|
)
|
||||||
|
|
||||||
// Create a Cash.State object for the StandaloneCashRpcClient to get
|
// Create a Cash.State object for the StandaloneCashRpcClient to get
|
||||||
node.services.startFlow(CashIssueFlow(100.POUNDS, OpaqueBytes.of(1), identity), InvocationContext.shell()).flatMap { it.resultFuture }.getOrThrow()
|
node.services.startFlow(CashIssueFlow(100.POUNDS, OpaqueBytes.of(1), identity), InvocationContext.shell()).flatMap { it.resultFuture }.getOrThrow()
|
||||||
val outOfProcessRpc = ProcessUtilities.startJavaProcess<StandaloneCashRpcClient>(
|
val outOfProcessRpc = ProcessUtilities.startJavaProcess<StandaloneCashRpcClient>(
|
||||||
classPath = classPathWithoutFinance,
|
classPath = classPathWithoutFinance,
|
||||||
arguments = listOf(node.node.configuration.rpcOptions.address.toString(), financeLocation)
|
arguments = listOf(node.node.configuration.rpcOptions.address.toString(), financeLocation),
|
||||||
|
extraJvmArguments = moduleOpens
|
||||||
)
|
)
|
||||||
assertThat(outOfProcessRpc.waitFor()).isZero() // i.e. no exceptions were thrown
|
assertThat(outOfProcessRpc.waitFor()).isZero() // i.e. no exceptions were thrown
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
package net.corda.client.rpc
|
package net.corda.client.rpc
|
||||||
|
|
||||||
import com.nhaarman.mockito_kotlin.any
|
import org.mockito.kotlin.any
|
||||||
import com.nhaarman.mockito_kotlin.atLeastOnce
|
import org.mockito.kotlin.atLeastOnce
|
||||||
import com.nhaarman.mockito_kotlin.mock
|
import org.mockito.kotlin.mock
|
||||||
import com.nhaarman.mockito_kotlin.never
|
import org.mockito.kotlin.never
|
||||||
import com.nhaarman.mockito_kotlin.times
|
import org.mockito.kotlin.times
|
||||||
import com.nhaarman.mockito_kotlin.verify
|
import org.mockito.kotlin.verify
|
||||||
import com.nhaarman.mockito_kotlin.whenever
|
import org.mockito.kotlin.whenever
|
||||||
import net.corda.client.rpc.internal.RPCClient
|
import net.corda.client.rpc.internal.RPCClient
|
||||||
import net.corda.client.rpc.ext.RPCConnectionListener
|
import net.corda.client.rpc.ext.RPCConnectionListener
|
||||||
import net.corda.core.messaging.RPCOps
|
import net.corda.core.messaging.RPCOps
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
package net.corda.client.rpc
|
package net.corda.client.rpc
|
||||||
|
|
||||||
import com.nhaarman.mockito_kotlin.mock
|
import org.mockito.kotlin.mock
|
||||||
import net.corda.client.rpc.RPCMultipleInterfacesTests.StringRPCOpsImpl.testPhrase
|
import net.corda.client.rpc.RPCMultipleInterfacesTests.StringRPCOpsImpl.testPhrase
|
||||||
import net.corda.core.crypto.SecureHash
|
import net.corda.core.crypto.SecureHash
|
||||||
import net.corda.core.messaging.CordaRPCOps
|
import net.corda.core.messaging.CordaRPCOps
|
||||||
|
@ -89,6 +89,7 @@ class RPCStabilityTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test(timeout=300_000)
|
@Test(timeout=300_000)
|
||||||
|
@Ignore("TODO JDK17:Fixme")
|
||||||
fun `client and server dont leak threads`() {
|
fun `client and server dont leak threads`() {
|
||||||
fun startAndStop() {
|
fun startAndStop() {
|
||||||
rpcDriver {
|
rpcDriver {
|
||||||
@ -121,6 +122,7 @@ class RPCStabilityTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test(timeout=300_000)
|
@Test(timeout=300_000)
|
||||||
|
@Ignore("TODO JDK17:Fixme")
|
||||||
fun `client doesnt leak threads when it fails to start`() {
|
fun `client doesnt leak threads when it fails to start`() {
|
||||||
fun startAndStop() {
|
fun startAndStop() {
|
||||||
rpcDriver {
|
rpcDriver {
|
||||||
@ -489,6 +491,7 @@ class RPCStabilityTests {
|
|||||||
* In this test we create a number of out of process RPC clients that call [TrackSubscriberOps.subscribe] in a loop.
|
* In this test we create a number of out of process RPC clients that call [TrackSubscriberOps.subscribe] in a loop.
|
||||||
*/
|
*/
|
||||||
@Test(timeout=300_000)
|
@Test(timeout=300_000)
|
||||||
|
@Ignore("TODO JDK17:Fixme")
|
||||||
fun `server cleans up queues after disconnected clients`() {
|
fun `server cleans up queues after disconnected clients`() {
|
||||||
rpcDriver {
|
rpcDriver {
|
||||||
val trackSubscriberOpsImpl = object : TrackSubscriberOps {
|
val trackSubscriberOpsImpl = object : TrackSubscriberOps {
|
||||||
|
@ -37,6 +37,7 @@ import net.corda.testing.node.internal.enclosedCordapp
|
|||||||
import net.corda.testing.node.internal.rpcDriver
|
import net.corda.testing.node.internal.rpcDriver
|
||||||
import org.assertj.core.api.Assertions.assertThat
|
import org.assertj.core.api.Assertions.assertThat
|
||||||
import org.assertj.core.api.Assertions.assertThatThrownBy
|
import org.assertj.core.api.Assertions.assertThatThrownBy
|
||||||
|
import org.junit.Ignore
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import java.lang.IllegalStateException
|
import java.lang.IllegalStateException
|
||||||
import java.lang.RuntimeException
|
import java.lang.RuntimeException
|
||||||
@ -53,6 +54,7 @@ import kotlin.test.assertFalse
|
|||||||
import kotlin.test.assertNull
|
import kotlin.test.assertNull
|
||||||
import kotlin.test.assertTrue
|
import kotlin.test.assertTrue
|
||||||
|
|
||||||
|
@Ignore("TODO JDK17: Fixme")
|
||||||
class CordaRPCClientReconnectionTest {
|
class CordaRPCClientReconnectionTest {
|
||||||
|
|
||||||
private val portAllocator = incrementalPortAllocation()
|
private val portAllocator = incrementalPortAllocation()
|
||||||
|
@ -150,7 +150,6 @@ internal class RPCClientProxyHandler(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("TooGenericExceptionCaught")
|
|
||||||
private fun closeObservable(observable: UnicastSubject<Notification<*>>) {
|
private fun closeObservable(observable: UnicastSubject<Notification<*>>) {
|
||||||
// Notify listeners of the observables that the connection is being terminated.
|
// Notify listeners of the observables that the connection is being terminated.
|
||||||
try {
|
try {
|
||||||
@ -300,7 +299,7 @@ internal class RPCClientProxyHandler(
|
|||||||
class FailoverHandler(private val detected: () -> Unit = {},
|
class FailoverHandler(private val detected: () -> Unit = {},
|
||||||
private val completed: () -> Unit = {},
|
private val completed: () -> Unit = {},
|
||||||
private val failed: () -> Unit = {}): FailoverEventListener {
|
private val failed: () -> Unit = {}): FailoverEventListener {
|
||||||
override fun failoverEvent(eventType: FailoverEventType?) {
|
override fun failoverEvent(eventType: FailoverEventType) {
|
||||||
when (eventType) {
|
when (eventType) {
|
||||||
FailoverEventType.FAILURE_DETECTED -> { detected() }
|
FailoverEventType.FAILURE_DETECTED -> { detected() }
|
||||||
FailoverEventType.FAILOVER_COMPLETED -> { completed() }
|
FailoverEventType.FAILOVER_COMPLETED -> { completed() }
|
||||||
@ -589,7 +588,6 @@ internal class RPCClientProxyHandler(
|
|||||||
}
|
}
|
||||||
if (observableIds != null) {
|
if (observableIds != null) {
|
||||||
log.debug { "Reaping ${observableIds.size} observables" }
|
log.debug { "Reaping ${observableIds.size} observables" }
|
||||||
@Suppress("TooGenericExceptionCaught")
|
|
||||||
try {
|
try {
|
||||||
sendMessage(RPCApi.ClientToServer.ObservablesClosed(observableIds))
|
sendMessage(RPCApi.ClientToServer.ObservablesClosed(observableIds))
|
||||||
} catch(ex: Exception) {
|
} catch(ex: Exception) {
|
||||||
|
@ -9,9 +9,11 @@ import net.corda.testing.core.SerializationEnvironmentRule
|
|||||||
import net.corda.testing.node.internal.rpcDriver
|
import net.corda.testing.node.internal.rpcDriver
|
||||||
import net.corda.testing.node.internal.startRpcClient
|
import net.corda.testing.node.internal.startRpcClient
|
||||||
import org.assertj.core.api.Assertions.assertThatThrownBy
|
import org.assertj.core.api.Assertions.assertThatThrownBy
|
||||||
|
import org.junit.Ignore
|
||||||
import org.junit.Rule
|
import org.junit.Rule
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
|
|
||||||
|
@Ignore("TODO JDK17: Fixme")
|
||||||
class RPCFailureTests {
|
class RPCFailureTests {
|
||||||
@Rule
|
@Rule
|
||||||
@JvmField
|
@JvmField
|
||||||
|
@ -1,15 +1,12 @@
|
|||||||
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'
|
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
compile group: "org.jetbrains.kotlin", name: "kotlin-stdlib-jdk8", version: kotlin_version
|
implementation group: "org.jetbrains.kotlin", name: "kotlin-reflect", version: kotlin_version
|
||||||
compile group: "org.jetbrains.kotlin", name: "kotlin-reflect", version: kotlin_version
|
|
||||||
|
|
||||||
compile group: "com.typesafe", name: "config", version: typesafe_config_version
|
implementation group: "com.typesafe", name: "config", version: typesafe_config_version
|
||||||
|
|
||||||
compile project(":common-validation")
|
implementation project(":common-validation")
|
||||||
|
|
||||||
testImplementation "org.junit.jupiter:junit-jupiter-api:${junit_jupiter_version}"
|
testImplementation "org.junit.jupiter:junit-jupiter-api:${junit_jupiter_version}"
|
||||||
testImplementation "junit:junit:$junit_version"
|
testImplementation "junit:junit:$junit_version"
|
||||||
@ -18,14 +15,20 @@ dependencies {
|
|||||||
testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:${junit_jupiter_version}"
|
testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:${junit_jupiter_version}"
|
||||||
testRuntimeOnly "org.junit.platform:junit-platform-launcher:${junit_platform_version}"
|
testRuntimeOnly "org.junit.platform:junit-platform-launcher:${junit_platform_version}"
|
||||||
|
|
||||||
testCompile group: "org.jetbrains.kotlin", name: "kotlin-test", version: kotlin_version
|
testImplementation group: "org.jetbrains.kotlin", name: "kotlin-test", version: kotlin_version
|
||||||
testCompile group: "org.assertj", name: "assertj-core", version: assertj_version
|
testImplementation group: "org.assertj", name: "assertj-core", version: assertj_version
|
||||||
}
|
}
|
||||||
|
|
||||||
jar {
|
jar {
|
||||||
baseName 'corda-common-configuration-parsing'
|
baseName 'corda-common-configuration-parsing'
|
||||||
}
|
}
|
||||||
|
|
||||||
publish {
|
publishing {
|
||||||
name jar.baseName
|
publications {
|
||||||
|
maven(MavenPublication) {
|
||||||
|
artifactId jar.baseName
|
||||||
|
from components.java
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -1,28 +1,26 @@
|
|||||||
import org.apache.tools.ant.filters.ReplaceTokens
|
import org.apache.tools.ant.filters.ReplaceTokens
|
||||||
|
|
||||||
apply plugin: 'kotlin'
|
apply plugin: 'org.jetbrains.kotlin.jvm'
|
||||||
apply plugin: 'net.corda.plugins.publish-utils'
|
apply plugin: 'corda.common-publishing'
|
||||||
apply plugin: 'com.jfrog.artifactory'
|
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
compile group: "org.jetbrains.kotlin", name: "kotlin-stdlib-jdk8", version: kotlin_version
|
implementation group: "org.jetbrains.kotlin", name: "kotlin-reflect", version: kotlin_version
|
||||||
compile group: "org.jetbrains.kotlin", name: "kotlin-reflect", version: kotlin_version
|
|
||||||
|
|
||||||
compile group: "com.typesafe", name: "config", version: typesafe_config_version
|
implementation group: "com.typesafe", name: "config", version: typesafe_config_version
|
||||||
|
|
||||||
// Log4J: logging framework
|
// Log4J: logging framework
|
||||||
compile "org.apache.logging.log4j:log4j-core:$log4j_version"
|
implementation "org.apache.logging.log4j:log4j-core:$log4j_version"
|
||||||
|
|
||||||
compile "com.jcabi:jcabi-manifests:$jcabi_manifests_version"
|
implementation "com.jcabi:jcabi-manifests:$jcabi_manifests_version"
|
||||||
|
|
||||||
// Need to depend on one other Corda project in order to get hold of a valid manifest for the tests
|
// Need to depend on one other Corda project in order to get hold of a valid manifest for the tests
|
||||||
testCompile project(":common-validation")
|
testImplementation project(":common-validation")
|
||||||
|
|
||||||
// test dependencies
|
// test dependencies
|
||||||
testImplementation "junit:junit:$junit_version"
|
testImplementation "junit:junit:$junit_version"
|
||||||
testCompile group: "org.jetbrains.kotlin", name: "kotlin-test", version: kotlin_version
|
testImplementation group: "org.jetbrains.kotlin", name: "kotlin-test", version: kotlin_version
|
||||||
testCompile "org.mockito:mockito-core:$mockito_version"
|
testImplementation "org.mockito:mockito-core:$mockito_version"
|
||||||
testCompile "com.natpryce:hamkrest:$hamkrest_version"
|
testImplementation "com.natpryce:hamkrest:$hamkrest_version"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -38,6 +36,12 @@ jar {
|
|||||||
baseName 'corda-common-logging'
|
baseName 'corda-common-logging'
|
||||||
}
|
}
|
||||||
|
|
||||||
publish {
|
publishing {
|
||||||
name jar.baseName
|
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)
|
* (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,17 +1,19 @@
|
|||||||
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'
|
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
|
implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
|
||||||
compile "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
jar {
|
jar {
|
||||||
baseName 'corda-common-validation'
|
baseName 'corda-common-validation'
|
||||||
}
|
}
|
||||||
|
|
||||||
publish {
|
publishing {
|
||||||
name jar.baseName
|
publications {
|
||||||
|
maven(MavenPublication) {
|
||||||
|
artifactId jar.baseName
|
||||||
|
from components.java
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
@ -1,18 +1,23 @@
|
|||||||
// This contains the SwapIdentitiesFlow which can be used for exchanging confidential identities as part of a flow.
|
// This contains the SwapIdentitiesFlow which can be used for exchanging confidential identities as part of a flow.
|
||||||
// TODO: Merge this into core: the original plan was to develop it independently but in practice it's too widely used to break compatibility now, as finance uses it.
|
// TODO: Merge this into core: the original plan was to develop it independently but in practice it's too widely used to break compatibility now, as finance uses it.
|
||||||
apply plugin: 'kotlin'
|
|
||||||
apply plugin: 'net.corda.plugins.publish-utils'
|
|
||||||
apply plugin: 'net.corda.plugins.quasar-utils'
|
apply plugin: 'net.corda.plugins.quasar-utils'
|
||||||
apply plugin: 'net.corda.plugins.cordapp'
|
apply plugin: 'net.corda.plugins.cordapp'
|
||||||
apply plugin: 'com.jfrog.artifactory'
|
apply plugin: 'corda.common-publishing'
|
||||||
|
|
||||||
description 'Corda Experimental Confidential Identities'
|
description 'Corda Experimental Confidential Identities'
|
||||||
|
|
||||||
dependencies {
|
cordapp {
|
||||||
cordaCompile project(':core')
|
targetPlatformVersion corda_platform_version.toInteger()
|
||||||
|
}
|
||||||
|
|
||||||
testCompile "org.jetbrains.kotlin:kotlin-test:$kotlin_version"
|
dependencies {
|
||||||
|
cordaProvided project(':core')
|
||||||
|
|
||||||
|
api "org.slf4j:slf4j-api:$slf4j_version"
|
||||||
|
|
||||||
|
testImplementation "org.jetbrains.kotlin:kotlin-test:$kotlin_version"
|
||||||
testImplementation "org.junit.jupiter:junit-jupiter-api:${junit_jupiter_version}"
|
testImplementation "org.junit.jupiter:junit-jupiter-api:${junit_jupiter_version}"
|
||||||
|
testImplementation "co.paralleluniverse:quasar-core:$quasar_version"
|
||||||
testImplementation "junit:junit:$junit_version"
|
testImplementation "junit:junit:$junit_version"
|
||||||
|
|
||||||
testRuntimeOnly "org.junit.vintage:junit-vintage-engine:${junit_vintage_version}"
|
testRuntimeOnly "org.junit.vintage:junit-vintage-engine:${junit_vintage_version}"
|
||||||
@ -20,19 +25,27 @@ dependencies {
|
|||||||
testRuntimeOnly "org.junit.platform:junit-platform-launcher:${junit_platform_version}"
|
testRuntimeOnly "org.junit.platform:junit-platform-launcher:${junit_platform_version}"
|
||||||
|
|
||||||
// Guava: Google test library (collections test suite)
|
// Guava: Google test library (collections test suite)
|
||||||
testCompile "com.google.guava:guava-testlib:$guava_version"
|
testImplementation "com.google.guava:guava-testlib:$guava_version"
|
||||||
|
|
||||||
// Bring in the MockNode infrastructure for writing protocol unit tests.
|
// Bring in the MockNode infrastructure for writing protocol unit tests.
|
||||||
testCompile project(":node-driver")
|
testImplementation project(":node")
|
||||||
|
testImplementation project(":node-api")
|
||||||
|
testImplementation project(":node-driver")
|
||||||
|
testImplementation project(":core-test-utils")
|
||||||
|
testImplementation project(':finance:contracts')
|
||||||
|
testImplementation project(':finance:workflows')
|
||||||
|
|
||||||
// AssertJ: for fluent assertions for testing
|
// AssertJ: for fluent assertions for testing
|
||||||
testCompile "org.assertj:assertj-core:$assertj_version"
|
testImplementation "org.assertj:assertj-core:$assertj_version"
|
||||||
|
testImplementation "com.natpryce:hamkrest:$hamkrest_version"
|
||||||
|
testImplementation "io.reactivex:rxjava:$rxjava_version"
|
||||||
}
|
}
|
||||||
|
|
||||||
jar {
|
publishing {
|
||||||
baseName 'corda-confidential-identities'
|
publications {
|
||||||
|
maven(MavenPublication) {
|
||||||
|
artifactId 'corda-confidential-identities'
|
||||||
|
from components.cordapp
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
publish {
|
|
||||||
name jar.baseName
|
|
||||||
}
|
}
|
||||||
|
@ -2,64 +2,26 @@
|
|||||||
<Configuration status="info" shutdownHook="disable">
|
<Configuration status="info" shutdownHook="disable">
|
||||||
|
|
||||||
<Properties>
|
<Properties>
|
||||||
<Property name="log-path">${sys:log-path:-logs}</Property>
|
<Property name="log_path">${sys:log-path:-logs}</Property>
|
||||||
<Property name="log-name">node-${hostName}</Property>
|
<Property name="log_name">node-${hostName}</Property>
|
||||||
<Property name="diagnostic-log-name">diagnostic-${hostName}</Property>
|
<Property name="diagnostic_log_name">diagnostic-${hostName}</Property>
|
||||||
<Property name="archive">${log-path}/archive</Property>
|
<Property name="archive">${log_path}/archive</Property>
|
||||||
<Property name="defaultLogLevel">${sys:defaultLogLevel:-info}</Property>
|
<Property name="default_log_level">${sys:defaultLogLevel:-info}</Property>
|
||||||
<Property name="consoleLogLevel">${sys:consoleLogLevel:-error}</Property>
|
<Property name="console_log_level">${sys:consoleLogLevel:-error}</Property>
|
||||||
</Properties>
|
</Properties>
|
||||||
|
|
||||||
<Appenders>
|
<Appenders>
|
||||||
<ScriptAppenderSelector name="Console-Selector">
|
|
||||||
<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 -->
|
<!-- The default console appender - prints no exception information -->
|
||||||
<Console name="Console-Appender" target="SYSTEM_OUT">
|
<Console name="Console-Appender" target="SYSTEM_OUT">
|
||||||
<PatternLayout>
|
<PatternLayout
|
||||||
<ScriptPatternSelector
|
pattern="%highlight{[%level{length=5}] %date{HH:mm:ssZ} [%t] %c{2}.%method - %msg%n%throwable{0}}{INFO=white,WARN=red,FATAL=bright red}"/>
|
||||||
defaultPattern="%highlight{[%level{length=5}] %date{HH:mm:ssZ} [%t] %c{2}.%method - %msg%n%throwable{0}}{INFO=white,WARN=red,FATAL=bright red}">
|
|
||||||
<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>
|
</Console>
|
||||||
|
|
||||||
<!-- The console appender when debug or trace level logging is specified. Prints full stack trace -->
|
<!-- The console appender when debug or trace level logging is specified. Prints full stack trace -->
|
||||||
<Console name="Console-Debug-Appender" target="SYSTEM_OUT">
|
<Console name="Console-Debug-Appender" target="SYSTEM_OUT">
|
||||||
<PatternLayout>
|
<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}">
|
pattern="%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>
|
</Console>
|
||||||
</AppenderSet>
|
|
||||||
</ScriptAppenderSelector>
|
|
||||||
|
|
||||||
<!-- Required for printBasicInfo -->
|
<!-- Required for printBasicInfo -->
|
||||||
<Console name="Console-Appender-Println" target="SYSTEM_OUT">
|
<Console name="Console-Appender-Println" target="SYSTEM_OUT">
|
||||||
@ -69,24 +31,10 @@
|
|||||||
<!-- Will generate up to 500 log files for a given day. Adjust this number according to the available storage.
|
<!-- Will generate up to 500 log files for a given day. Adjust this number according to the available storage.
|
||||||
During every rollover it will delete those that are older than 60 days, but keep the most recent 10 GB -->
|
During every rollover it will delete those that are older than 60 days, but keep the most recent 10 GB -->
|
||||||
<RollingRandomAccessFile name="RollingFile-Appender"
|
<RollingRandomAccessFile name="RollingFile-Appender"
|
||||||
fileName="${log-path}/${log-name}.log"
|
fileName="${log_path}/${log_name}.log"
|
||||||
filePattern="${archive}/${log-name}.%date{yyyy-MM-dd}-%i.log.gz">
|
filePattern="${archive}/${log_name}.%date{yyyy-MM-dd}-%i.log.gz">
|
||||||
|
|
||||||
<PatternLayout>
|
<PatternLayout pattern="[%-5level] %date{ISO8601}{UTC}Z [%t] %c{2}.%method - %msg%n"/>
|
||||||
<ScriptPatternSelector defaultPattern="[%-5level] %date{ISO8601}{UTC}Z [%t] %c{2}.%method - %msg%n">
|
|
||||||
<Script name="MDCSelector" language="javascript"><![CDATA[
|
|
||||||
result = null;
|
|
||||||
if (!logEvent.getContextData().size() == 0) {
|
|
||||||
result = "WithMDC";
|
|
||||||
} else {
|
|
||||||
result = null;
|
|
||||||
}
|
|
||||||
result;
|
|
||||||
]]>
|
|
||||||
</Script>
|
|
||||||
<PatternMatch key="WithMDC" pattern="[%-5level] %date{ISO8601}{UTC}Z [%t] %c{2}.%method - %msg %X%n"/>
|
|
||||||
</ScriptPatternSelector>
|
|
||||||
</PatternLayout>
|
|
||||||
|
|
||||||
<Policies>
|
<Policies>
|
||||||
<TimeBasedTriggeringPolicy/>
|
<TimeBasedTriggeringPolicy/>
|
||||||
@ -95,7 +43,7 @@
|
|||||||
|
|
||||||
<DefaultRolloverStrategy min="1" max="500">
|
<DefaultRolloverStrategy min="1" max="500">
|
||||||
<Delete basePath="${archive}" maxDepth="1">
|
<Delete basePath="${archive}" maxDepth="1">
|
||||||
<IfFileName glob="${log-name}*.log.gz"/>
|
<IfFileName glob="${log_name}*.log.gz"/>
|
||||||
<IfLastModified age="60d">
|
<IfLastModified age="60d">
|
||||||
<IfAny>
|
<IfAny>
|
||||||
<IfAccumulatedFileSize exceeds="10 GB"/>
|
<IfAccumulatedFileSize exceeds="10 GB"/>
|
||||||
@ -109,24 +57,10 @@
|
|||||||
<!-- Will generate up to 100 log files for a given day. During every rollover it will delete
|
<!-- Will generate up to 100 log files for a given day. During every rollover it will delete
|
||||||
those that are older than 60 days, but keep the most recent 10 GB -->
|
those that are older than 60 days, but keep the most recent 10 GB -->
|
||||||
<RollingRandomAccessFile name="Diagnostic-RollingFile-Appender"
|
<RollingRandomAccessFile name="Diagnostic-RollingFile-Appender"
|
||||||
fileName="${log-path}/${diagnostic-log-name}.log"
|
fileName="${log_path}/${diagnostic_log_name}.log"
|
||||||
filePattern="${archive}/${diagnostic-log-name}.%date{yyyy-MM-dd}-%i.log.gz">
|
filePattern="${archive}/${diagnostic_log_name}.%date{yyyy-MM-dd}-%i.log.gz">
|
||||||
|
|
||||||
<PatternLayout>
|
<PatternLayout pattern="[%-5level] %date{ISO8601}{UTC}Z [%t] %c{2}.%method - %msg%n"/>
|
||||||
<ScriptPatternSelector defaultPattern="[%-5level] %date{ISO8601}{UTC}Z [%t] %c{2}.%method - %msg%n">
|
|
||||||
<Script name="MDCSelector" language="javascript"><![CDATA[
|
|
||||||
result = null;
|
|
||||||
if (!logEvent.getContextData().size() == 0) {
|
|
||||||
result = "WithMDC";
|
|
||||||
} else {
|
|
||||||
result = null;
|
|
||||||
}
|
|
||||||
result;
|
|
||||||
]]>
|
|
||||||
</Script>
|
|
||||||
<PatternMatch key="WithMDC" pattern="[%-5level] %date{ISO8601}{UTC}Z [%t] %c{2}.%method - %msg %X%n"/>
|
|
||||||
</ScriptPatternSelector>
|
|
||||||
</PatternLayout>
|
|
||||||
|
|
||||||
<Policies>
|
<Policies>
|
||||||
<TimeBasedTriggeringPolicy/>
|
<TimeBasedTriggeringPolicy/>
|
||||||
@ -135,7 +69,7 @@
|
|||||||
|
|
||||||
<DefaultRolloverStrategy min="1" max="100">
|
<DefaultRolloverStrategy min="1" max="100">
|
||||||
<Delete basePath="${archive}" maxDepth="1">
|
<Delete basePath="${archive}" maxDepth="1">
|
||||||
<IfFileName glob="${diagnostic-log-name}*.log.gz"/>
|
<IfFileName glob="${diagnostic_log_name}*.log.gz"/>
|
||||||
<IfLastModified age="60d">
|
<IfLastModified age="60d">
|
||||||
<IfAny>
|
<IfAny>
|
||||||
<IfAccumulatedFileSize exceeds="10 GB"/>
|
<IfAccumulatedFileSize exceeds="10 GB"/>
|
||||||
@ -147,7 +81,7 @@
|
|||||||
</RollingRandomAccessFile>
|
</RollingRandomAccessFile>
|
||||||
|
|
||||||
<RollingFile name="Checkpoint-Agent-RollingFile-Appender"
|
<RollingFile name="Checkpoint-Agent-RollingFile-Appender"
|
||||||
fileName="${log-path}/checkpoints_agent-${date:yyyyMMdd-HHmmss}.log"
|
fileName="${log_path}/checkpoints_agent-${date:yyyyMMdd-HHmmss}.log"
|
||||||
filePattern="${archive}/checkpoints_agent.%date{yyyy-MM-dd}-%i.log.gz">
|
filePattern="${archive}/checkpoints_agent.%date{yyyy-MM-dd}-%i.log.gz">
|
||||||
|
|
||||||
<PatternLayout pattern="[%-5level] %date{ISO8601}{UTC}Z [%t] %c{2}.%method - %msg%n"/>
|
<PatternLayout pattern="[%-5level] %date{ISO8601}{UTC}Z [%t] %c{2}.%method - %msg%n"/>
|
||||||
@ -171,7 +105,7 @@
|
|||||||
</RollingFile>
|
</RollingFile>
|
||||||
|
|
||||||
<Rewrite name="Console-ErrorCode-Selector">
|
<Rewrite name="Console-ErrorCode-Selector">
|
||||||
<AppenderRef ref="Console-Selector"/>
|
<AppenderRef ref="Console-Appender"/>
|
||||||
</Rewrite>
|
</Rewrite>
|
||||||
|
|
||||||
<Rewrite name="Console-ErrorCode-Appender-Println">
|
<Rewrite name="Console-ErrorCode-Appender-Println">
|
||||||
@ -187,8 +121,8 @@
|
|||||||
</Appenders>
|
</Appenders>
|
||||||
|
|
||||||
<Loggers>
|
<Loggers>
|
||||||
<Root level="${defaultLogLevel}">
|
<Root level="${default_log_level}">
|
||||||
<AppenderRef ref="Console-ErrorCode-Selector" level="${consoleLogLevel}"/>
|
<AppenderRef ref="Console-ErrorCode-Selector" level="${console_log_level}"/>
|
||||||
<AppenderRef ref="RollingFile-ErrorCode-Appender"/>
|
<AppenderRef ref="RollingFile-ErrorCode-Appender"/>
|
||||||
</Root>
|
</Root>
|
||||||
<Logger name="BasicInfo" additivity="false">
|
<Logger name="BasicInfo" additivity="false">
|
||||||
|
@ -3,26 +3,24 @@
|
|||||||
# their own projects. So don't get fancy with syntax!
|
# their own projects. So don't get fancy with syntax!
|
||||||
# Fancy syntax - multi pass ${whatever} replacement
|
# Fancy syntax - multi pass ${whatever} replacement
|
||||||
|
|
||||||
cordaVersion=4.11
|
cordaVersion=4.12
|
||||||
versionSuffix=SNAPSHOT
|
versionSuffix=SNAPSHOT
|
||||||
gradlePluginsVersion=5.0.12
|
cordaShellVersion=4.12-HC01
|
||||||
kotlinVersion=1.2.71
|
gradlePluginsVersion=5.1.1
|
||||||
java8MinUpdateVersion=171
|
artifactoryContextUrl=https://software.r3.com/artifactory
|
||||||
|
internalPublishVersion=1.+
|
||||||
# ***************************************************************#
|
# ***************************************************************#
|
||||||
# When incrementing platformVersion make sure to update #
|
# When incrementing platformVersion make sure to update #
|
||||||
# net.corda.core.internal.CordaUtilsKt.PLATFORM_VERSION as well. #
|
# net.corda.core.internal.CordaUtilsKt.PLATFORM_VERSION as well. #
|
||||||
# ***************************************************************#
|
# ***************************************************************#
|
||||||
platformVersion=13
|
platformVersion=14
|
||||||
openTelemetryVersion=1.20.1
|
openTelemetryVersion=1.20.1
|
||||||
openTelemetrySemConvVersion=1.20.1-alpha
|
openTelemetrySemConvVersion=1.20.1-alpha
|
||||||
guavaVersion=28.0-jre
|
guavaVersion=28.0-jre
|
||||||
# Quasar version to use with Java 8:
|
# Quasar version to use with Java 8:
|
||||||
quasarVersion=0.7.16_r3
|
quasarVersion=0.9.0_r3
|
||||||
# Quasar version to use with Java 11:
|
|
||||||
quasarVersion11=0.8.1_r3
|
|
||||||
jdkClassifier11=jdk11
|
|
||||||
dockerJavaVersion=3.2.5
|
dockerJavaVersion=3.2.5
|
||||||
proguardVersion=6.1.1
|
proguardVersion=7.3.1
|
||||||
// bouncy castle version must not be changed on a patch release. Needs a full release test cycle to flush out any issues.
|
// bouncy castle version must not be changed on a patch release. Needs a full release test cycle to flush out any issues.
|
||||||
bouncycastleVersion=1.75
|
bouncycastleVersion=1.75
|
||||||
classgraphVersion=4.8.135
|
classgraphVersion=4.8.135
|
||||||
@ -46,9 +44,9 @@ commonsTextVersion=1.10.0
|
|||||||
|
|
||||||
# gradle-capsule-plugin:1.0.2 contains capsule:1.0.1 by default.
|
# gradle-capsule-plugin:1.0.2 contains capsule:1.0.1 by default.
|
||||||
# We must configure it manually to use the latest capsule version.
|
# We must configure it manually to use the latest capsule version.
|
||||||
capsuleVersion=1.0.3
|
capsuleVersion=1.0.4_r3
|
||||||
asmVersion=7.1
|
asmVersion=9.5
|
||||||
artemisVersion=2.19.1
|
artemisVersion=2.29.0
|
||||||
# TODO Upgrade Jackson only when corda is using kotlin 1.3.10
|
# TODO Upgrade Jackson only when corda is using kotlin 1.3.10
|
||||||
jacksonVersion=2.13.5
|
jacksonVersion=2.13.5
|
||||||
jacksonKotlinVersion=2.9.7
|
jacksonKotlinVersion=2.9.7
|
||||||
@ -57,11 +55,11 @@ jerseyVersion=2.25
|
|||||||
servletVersion=4.0.1
|
servletVersion=4.0.1
|
||||||
assertjVersion=3.12.2
|
assertjVersion=3.12.2
|
||||||
slf4JVersion=1.7.30
|
slf4JVersion=1.7.30
|
||||||
log4JVersion=2.17.1
|
log4JVersion=2.20.0
|
||||||
okhttpVersion=3.14.9
|
okhttpVersion=4.11.0
|
||||||
nettyVersion=4.1.77.Final
|
nettyVersion=4.1.77.Final
|
||||||
fileuploadVersion=1.4
|
fileuploadVersion=1.4
|
||||||
kryoVersion=4.0.2
|
kryoVersion=5.5.0
|
||||||
kryoSerializerVersion=0.43
|
kryoSerializerVersion=0.43
|
||||||
# Legacy JUnit 4 version
|
# Legacy JUnit 4 version
|
||||||
junitVersion=4.12
|
junitVersion=4.12
|
||||||
@ -71,8 +69,8 @@ junitVersion=4.12
|
|||||||
junitVintageVersion=5.5.0-RC1
|
junitVintageVersion=5.5.0-RC1
|
||||||
junitJupiterVersion=5.5.0-RC1
|
junitJupiterVersion=5.5.0-RC1
|
||||||
junitPlatformVersion=1.5.0-RC1
|
junitPlatformVersion=1.5.0-RC1
|
||||||
mockitoVersion=2.28.2
|
mockitoVersion=5.5.0
|
||||||
mockitoKotlinVersion=1.6.0
|
mockitoKotlinVersion=4.1.0
|
||||||
hamkrestVersion=1.7.0.0
|
hamkrestVersion=1.7.0.0
|
||||||
joptSimpleVersion=5.0.2
|
joptSimpleVersion=5.0.2
|
||||||
jansiVersion=1.18
|
jansiVersion=1.18
|
||||||
@ -80,7 +78,7 @@ hibernateVersion=5.6.14.Final
|
|||||||
# h2Version - Update docs if renamed or removed.
|
# h2Version - Update docs if renamed or removed.
|
||||||
h2Version=2.2.224
|
h2Version=2.2.224
|
||||||
rxjavaVersion=1.3.8
|
rxjavaVersion=1.3.8
|
||||||
dokkaVersion=0.10.1
|
dokkaVersion=1.8.20
|
||||||
eddsaVersion=0.3.0
|
eddsaVersion=0.3.0
|
||||||
dependencyCheckerVersion=5.2.0
|
dependencyCheckerVersion=5.2.0
|
||||||
commonsCollectionsVersion=4.3
|
commonsCollectionsVersion=4.3
|
||||||
@ -97,13 +95,8 @@ protonjVersion=0.33.0
|
|||||||
snappyVersion=0.4
|
snappyVersion=0.4
|
||||||
jcabiManifestsVersion=1.1
|
jcabiManifestsVersion=1.1
|
||||||
picocliVersion=3.9.6
|
picocliVersion=3.9.6
|
||||||
commonsLangVersion=3.9
|
|
||||||
commonsIoVersion=2.6
|
commonsIoVersion=2.6
|
||||||
controlsfxVersion=8.40.15
|
controlsfxVersion=8.40.15
|
||||||
# FontAwesomeFX for Java8
|
|
||||||
fontawesomefxCommonsJava8Version=8.15
|
|
||||||
fontawesomefxFontawesomeJava8Version=4.7.0-5
|
|
||||||
# FontAwesomeFX for a more recent version of the Java Runtime (class file version 55.0)
|
|
||||||
fontawesomefxCommonsVersion=11.0
|
fontawesomefxCommonsVersion=11.0
|
||||||
fontawesomefxFontawesomeVersion=4.7.0-11
|
fontawesomefxFontawesomeVersion=4.7.0-11
|
||||||
javaassistVersion=3.27.0-GA
|
javaassistVersion=3.29.2-GA
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
apply plugin: 'kotlin'
|
apply plugin: 'org.jetbrains.kotlin.jvm'
|
||||||
apply plugin: 'kotlin-jpa'
|
apply plugin: 'org.jetbrains.kotlin.plugin.jpa'
|
||||||
apply plugin: 'net.corda.plugins.quasar-utils'
|
apply plugin: 'net.corda.plugins.quasar-utils'
|
||||||
|
apply plugin: 'idea'
|
||||||
|
|
||||||
description 'Corda core tests'
|
description 'Corda core tests'
|
||||||
|
|
||||||
configurations {
|
configurations {
|
||||||
integrationTestCompile.extendsFrom testCompile
|
integrationTestImplementation.extendsFrom testImplementation
|
||||||
integrationTestRuntimeOnly.extendsFrom testRuntimeOnly
|
integrationTestRuntimeOnly.extendsFrom testRuntimeOnly
|
||||||
|
|
||||||
smokeTestCompile.extendsFrom compile
|
smokeTestCompile.extendsFrom compile
|
||||||
@ -41,47 +42,65 @@ sourceSets {
|
|||||||
|
|
||||||
processSmokeTestResources {
|
processSmokeTestResources {
|
||||||
// Bring in the fully built corda.jar for use by NodeFactory in the smoke tests
|
// Bring in the fully built corda.jar for use by NodeFactory in the smoke tests
|
||||||
from(project(':node:capsule').tasks['buildCordaJAR']) {
|
from(project(":node:capsule").tasks['buildCordaJAR']) {
|
||||||
rename 'corda-(.*)', 'corda.jar'
|
rename 'corda-(.*)', 'corda.jar'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
|
||||||
testImplementation "org.junit.jupiter:junit-jupiter-api:${junit_jupiter_version}"
|
testImplementation "org.junit.jupiter:junit-jupiter-api:${junit_jupiter_version}"
|
||||||
testImplementation "junit:junit:$junit_version"
|
testImplementation "junit:junit:$junit_version"
|
||||||
testRuntimeOnly "org.junit.vintage:junit-vintage-engine:${junit_vintage_version}"
|
testRuntimeOnly "org.junit.vintage:junit-vintage-engine:${junit_vintage_version}"
|
||||||
testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:${junit_jupiter_version}"
|
testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:${junit_jupiter_version}"
|
||||||
testRuntimeOnly "org.junit.platform:junit-platform-launcher:${junit_platform_version}"
|
testRuntimeOnly "org.junit.platform:junit-platform-launcher:${junit_platform_version}"
|
||||||
|
|
||||||
testCompile "commons-fileupload:commons-fileupload:$fileupload_version"
|
testImplementation "commons-fileupload:commons-fileupload:$fileupload_version"
|
||||||
testCompile project(":core")
|
testImplementation project(":core")
|
||||||
testCompile project(path: ':core', configuration: 'testArtifacts')
|
testImplementation project(path: ':core', configuration: 'testArtifacts')
|
||||||
|
|
||||||
|
testImplementation project(":node")
|
||||||
|
testImplementation project(":node-api")
|
||||||
|
testImplementation project(":client:rpc")
|
||||||
|
testImplementation project(":serialization")
|
||||||
|
testImplementation project(":common-configuration-parsing")
|
||||||
|
testImplementation project(":finance:contracts")
|
||||||
|
testImplementation project(":finance:workflows")
|
||||||
|
testImplementation project(":core-test-utils")
|
||||||
|
testImplementation project(":test-utils")
|
||||||
|
testImplementation project(path: ':core', configuration: 'testArtifacts')
|
||||||
|
|
||||||
|
|
||||||
// Guava: Google test library (collections test suite)
|
// Guava: Google test library (collections test suite)
|
||||||
testCompile "com.google.guava:guava-testlib:$guava_version"
|
testImplementation "com.google.guava:guava-testlib:$guava_version"
|
||||||
|
testImplementation "com.google.jimfs:jimfs:1.1"
|
||||||
|
testImplementation group: "com.typesafe", name: "config", version: typesafe_config_version
|
||||||
|
|
||||||
// Bring in the MockNode infrastructure for writing protocol unit tests.
|
// Bring in the MockNode infrastructure for writing protocol unit tests.
|
||||||
testCompile project(":node-driver")
|
testImplementation project(":node-driver")
|
||||||
|
|
||||||
compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
|
implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
|
||||||
compile "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
|
testImplementation "org.jetbrains.kotlin:kotlin-test:$kotlin_version"
|
||||||
testCompile "org.jetbrains.kotlin:kotlin-test:$kotlin_version"
|
|
||||||
|
|
||||||
// Hamkrest, for fluent, composable matchers
|
// Hamkrest, for fluent, composable matchers
|
||||||
testCompile "com.natpryce:hamkrest:$hamkrest_version"
|
testImplementation "com.natpryce:hamkrest:$hamkrest_version"
|
||||||
|
|
||||||
// SLF4J: commons-logging bindings for a SLF4J back end
|
// SLF4J: commons-logging bindings for a SLF4J back end
|
||||||
compile "org.slf4j:jcl-over-slf4j:$slf4j_version"
|
implementation "org.slf4j:jcl-over-slf4j:$slf4j_version"
|
||||||
compile "org.slf4j:slf4j-api:$slf4j_version"
|
implementation "org.slf4j:slf4j-api:$slf4j_version"
|
||||||
|
|
||||||
// AssertJ: for fluent assertions for testing
|
// AssertJ: for fluent assertions for testing
|
||||||
testCompile "org.assertj:assertj-core:${assertj_version}"
|
testImplementation "org.assertj:assertj-core:${assertj_version}"
|
||||||
|
|
||||||
// Guava: Google utilities library.
|
// Guava: Google utilities library.
|
||||||
testCompile "com.google.guava:guava:$guava_version"
|
testImplementation "com.google.guava:guava:$guava_version"
|
||||||
|
|
||||||
// Smoke tests do NOT have any Node code on the classpath!
|
// Smoke tests do NOT have any Node code on the classpath!
|
||||||
|
smokeTestImplementation project(":core")
|
||||||
|
smokeTestImplementation project(":node-api")
|
||||||
|
smokeTestImplementation project(":client:rpc")
|
||||||
|
|
||||||
|
smokeTestImplementation "org.bouncycastle:bcprov-jdk18on:${bouncycastle_version}"
|
||||||
|
smokeTestImplementation "co.paralleluniverse:quasar-core:$quasar_version"
|
||||||
smokeTestImplementation "org.junit.jupiter:junit-jupiter-api:${junit_jupiter_version}"
|
smokeTestImplementation "org.junit.jupiter:junit-jupiter-api:${junit_jupiter_version}"
|
||||||
smokeTestImplementation "junit:junit:$junit_version"
|
smokeTestImplementation "junit:junit:$junit_version"
|
||||||
|
|
||||||
@ -93,11 +112,11 @@ dependencies {
|
|||||||
smokeTestCompile "org.assertj:assertj-core:${assertj_version}"
|
smokeTestCompile "org.assertj:assertj-core:${assertj_version}"
|
||||||
|
|
||||||
// used by FinalityFlowTests
|
// used by FinalityFlowTests
|
||||||
testCompile project(':testing:cordapps:cashobservers')
|
testImplementation project(':testing:cordapps:cashobservers')
|
||||||
}
|
}
|
||||||
|
|
||||||
configurations {
|
configurations {
|
||||||
testArtifacts.extendsFrom testRuntimeClasspath
|
testArtifacts.extendsFrom testRuntimeOnlyClasspath
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.withType(Test).configureEach {
|
tasks.withType(Test).configureEach {
|
||||||
@ -124,6 +143,16 @@ task smokeTest(type: Test) {
|
|||||||
dependsOn smokeTestJar
|
dependsOn smokeTestJar
|
||||||
testClassesDirs = sourceSets.smokeTest.output.classesDirs
|
testClassesDirs = sourceSets.smokeTest.output.classesDirs
|
||||||
classpath = sourceSets.smokeTest.runtimeClasspath
|
classpath = sourceSets.smokeTest.runtimeClasspath
|
||||||
|
|
||||||
|
jvmArgs test_add_opens
|
||||||
|
jvmArgs test_add_exports
|
||||||
|
}
|
||||||
|
|
||||||
|
idea {
|
||||||
|
module {
|
||||||
|
downloadJavadoc = true
|
||||||
|
downloadSources = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// quasar exclusions upon agent code instrumentation at run-time
|
// quasar exclusions upon agent code instrumentation at run-time
|
||||||
|
@ -4,7 +4,8 @@ import co.paralleluniverse.fibers.Suspendable
|
|||||||
import net.corda.core.flows.FlowLogic
|
import net.corda.core.flows.FlowLogic
|
||||||
import net.corda.core.flows.StartableByRPC
|
import net.corda.core.flows.StartableByRPC
|
||||||
import net.corda.core.identity.CordaX500Name
|
import net.corda.core.identity.CordaX500Name
|
||||||
import net.corda.core.internal.*
|
import net.corda.core.internal.PLATFORM_VERSION
|
||||||
|
import net.corda.core.internal.copyToDirectory
|
||||||
import net.corda.core.messaging.startFlow
|
import net.corda.core.messaging.startFlow
|
||||||
import net.corda.core.utilities.getOrThrow
|
import net.corda.core.utilities.getOrThrow
|
||||||
import net.corda.nodeapi.internal.config.User
|
import net.corda.nodeapi.internal.config.User
|
||||||
@ -14,10 +15,12 @@ import org.assertj.core.api.Assertions.assertThat
|
|||||||
import org.junit.After
|
import org.junit.After
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import java.nio.file.Paths
|
|
||||||
import java.util.concurrent.atomic.AtomicInteger
|
import java.util.concurrent.atomic.AtomicInteger
|
||||||
import java.util.jar.JarFile
|
import java.util.jar.JarFile
|
||||||
import kotlin.streams.toList
|
import kotlin.io.path.Path
|
||||||
|
import kotlin.io.path.createDirectories
|
||||||
|
import kotlin.io.path.div
|
||||||
|
import kotlin.io.path.listDirectoryEntries
|
||||||
|
|
||||||
class NodeVersioningTest {
|
class NodeVersioningTest {
|
||||||
private companion object {
|
private companion object {
|
||||||
@ -67,9 +70,7 @@ class NodeVersioningTest {
|
|||||||
fun `platform version from RPC`() {
|
fun `platform version from RPC`() {
|
||||||
val cordappsDir = (factory.baseDirectory(aliceConfig) / NodeProcess.CORDAPPS_DIR_NAME).createDirectories()
|
val cordappsDir = (factory.baseDirectory(aliceConfig) / NodeProcess.CORDAPPS_DIR_NAME).createDirectories()
|
||||||
// Find the jar file for the smoke tests of this module
|
// Find the jar file for the smoke tests of this module
|
||||||
val selfCordapp = Paths.get("build", "libs").list {
|
val selfCordapp = Path("build", "libs").listDirectoryEntries("*-smokeTests*").single()
|
||||||
it.filter { "-smokeTests" in it.toString() }.toList().single()
|
|
||||||
}
|
|
||||||
selfCordapp.copyToDirectory(cordappsDir)
|
selfCordapp.copyToDirectory(cordappsDir)
|
||||||
|
|
||||||
factory.create(aliceConfig).use { alice ->
|
factory.create(aliceConfig).use { alice ->
|
||||||
|
@ -3,11 +3,16 @@ package net.corda.coretests.cordapp
|
|||||||
import co.paralleluniverse.fibers.Suspendable
|
import co.paralleluniverse.fibers.Suspendable
|
||||||
import net.corda.core.crypto.Crypto.generateKeyPair
|
import net.corda.core.crypto.Crypto.generateKeyPair
|
||||||
import net.corda.core.crypto.sign
|
import net.corda.core.crypto.sign
|
||||||
import net.corda.core.flows.*
|
import net.corda.core.flows.FlowInfo
|
||||||
|
import net.corda.core.flows.FlowLogic
|
||||||
|
import net.corda.core.flows.FlowSession
|
||||||
|
import net.corda.core.flows.InitiatedBy
|
||||||
|
import net.corda.core.flows.InitiatingFlow
|
||||||
|
import net.corda.core.flows.StartableByRPC
|
||||||
import net.corda.core.identity.CordaX500Name
|
import net.corda.core.identity.CordaX500Name
|
||||||
import net.corda.core.identity.Party
|
import net.corda.core.identity.Party
|
||||||
import net.corda.core.identity.PartyAndCertificate
|
import net.corda.core.identity.PartyAndCertificate
|
||||||
import net.corda.core.internal.*
|
import net.corda.core.internal.copyToDirectory
|
||||||
import net.corda.core.messaging.startFlow
|
import net.corda.core.messaging.startFlow
|
||||||
import net.corda.core.node.NodeInfo
|
import net.corda.core.node.NodeInfo
|
||||||
import net.corda.core.serialization.serialize
|
import net.corda.core.serialization.serialize
|
||||||
@ -29,12 +34,16 @@ import org.junit.After
|
|||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
import java.nio.file.Paths
|
|
||||||
import java.security.KeyPair
|
import java.security.KeyPair
|
||||||
import java.security.PrivateKey
|
import java.security.PrivateKey
|
||||||
import java.security.PublicKey
|
import java.security.PublicKey
|
||||||
import java.util.concurrent.atomic.AtomicInteger
|
import java.util.concurrent.atomic.AtomicInteger
|
||||||
import kotlin.streams.toList
|
import kotlin.io.path.Path
|
||||||
|
import kotlin.io.path.createDirectories
|
||||||
|
import kotlin.io.path.div
|
||||||
|
import kotlin.io.path.name
|
||||||
|
import kotlin.io.path.useDirectoryEntries
|
||||||
|
import kotlin.io.path.writeBytes
|
||||||
|
|
||||||
class CordappSmokeTest {
|
class CordappSmokeTest {
|
||||||
private companion object {
|
private companion object {
|
||||||
@ -79,9 +88,7 @@ class CordappSmokeTest {
|
|||||||
val baseDir = factory.baseDirectory(aliceConfig)
|
val baseDir = factory.baseDirectory(aliceConfig)
|
||||||
val cordappsDir = (baseDir / CORDAPPS_DIR_NAME).createDirectories()
|
val cordappsDir = (baseDir / CORDAPPS_DIR_NAME).createDirectories()
|
||||||
// Find the jar file for the smoke tests of this module
|
// Find the jar file for the smoke tests of this module
|
||||||
val selfCordapp = Paths.get("build", "libs").list {
|
val selfCordapp = Path("build", "libs").useDirectoryEntries { it.single { "-smokeTests" in it.toString() } }
|
||||||
it.filter { "-smokeTests" in it.toString() }.toList().single()
|
|
||||||
}
|
|
||||||
selfCordapp.copyToDirectory(cordappsDir)
|
selfCordapp.copyToDirectory(cordappsDir)
|
||||||
|
|
||||||
// The `nodeReadyFuture` in the persistent network map cache will not complete unless there is at least one other
|
// The `nodeReadyFuture` in the persistent network map cache will not complete unless there is at least one other
|
||||||
@ -96,7 +103,7 @@ class CordappSmokeTest {
|
|||||||
val aliceIdentity = connectionToAlice.proxy.nodeInfo().legalIdentitiesAndCerts.first().party
|
val aliceIdentity = connectionToAlice.proxy.nodeInfo().legalIdentitiesAndCerts.first().party
|
||||||
val future = connectionToAlice.proxy.startFlow(CordappSmokeTest::GatherContextsFlow, aliceIdentity).returnValue
|
val future = connectionToAlice.proxy.startFlow(CordappSmokeTest::GatherContextsFlow, aliceIdentity).returnValue
|
||||||
val (sessionInitContext, sessionConfirmContext) = future.getOrThrow()
|
val (sessionInitContext, sessionConfirmContext) = future.getOrThrow()
|
||||||
val selfCordappName = selfCordapp.fileName.toString().removeSuffix(".jar")
|
val selfCordappName = selfCordapp.name.removeSuffix(".jar")
|
||||||
assertThat(sessionInitContext.appName).isEqualTo(selfCordappName)
|
assertThat(sessionInitContext.appName).isEqualTo(selfCordappName)
|
||||||
assertThat(sessionConfirmContext.appName).isEqualTo(selfCordappName)
|
assertThat(sessionConfirmContext.appName).isEqualTo(selfCordappName)
|
||||||
}
|
}
|
||||||
@ -139,7 +146,7 @@ class CordappSmokeTest {
|
|||||||
val dummyKeyPair = generateKeyPair()
|
val dummyKeyPair = generateKeyPair()
|
||||||
val nodeInfo = createNodeInfoWithSingleIdentity(CordaX500Name(organisation = "Bob Corp", locality = "Madrid", country = "ES"), dummyKeyPair, dummyKeyPair.public)
|
val nodeInfo = createNodeInfoWithSingleIdentity(CordaX500Name(organisation = "Bob Corp", locality = "Madrid", country = "ES"), dummyKeyPair, dummyKeyPair.public)
|
||||||
val signedNodeInfo = signWith(nodeInfo, listOf(dummyKeyPair.private))
|
val signedNodeInfo = signWith(nodeInfo, listOf(dummyKeyPair.private))
|
||||||
(additionalNodeInfoDir / "nodeInfo-41408E093F95EAD51F6892C34DEB65AE1A3569A4B0E5744769A1B485AF8E04B5").write(signedNodeInfo.serialize().bytes)
|
(additionalNodeInfoDir / "nodeInfo-41408E093F95EAD51F6892C34DEB65AE1A3569A4B0E5744769A1B485AF8E04B5").writeBytes(signedNodeInfo.serialize().bytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createNodeInfoWithSingleIdentity(name: CordaX500Name, nodeKeyPair: KeyPair, identityCertPublicKey: PublicKey): NodeInfo {
|
private fun createNodeInfoWithSingleIdentity(name: CordaX500Name, nodeKeyPair: KeyPair, identityCertPublicKey: PublicKey): NodeInfo {
|
||||||
|
@ -1,9 +1,17 @@
|
|||||||
package net.corda.coretests.contracts
|
package net.corda.coretests.contracts
|
||||||
|
|
||||||
import com.nhaarman.mockito_kotlin.doReturn
|
import net.corda.core.contracts.AlwaysAcceptAttachmentConstraint
|
||||||
import com.nhaarman.mockito_kotlin.mock
|
import net.corda.core.contracts.AutomaticPlaceholderConstraint
|
||||||
import com.nhaarman.mockito_kotlin.whenever
|
import net.corda.core.contracts.BelongsToContract
|
||||||
import net.corda.core.contracts.*
|
import net.corda.core.contracts.CommandData
|
||||||
|
import net.corda.core.contracts.Contract
|
||||||
|
import net.corda.core.contracts.ContractAttachment
|
||||||
|
import net.corda.core.contracts.ContractState
|
||||||
|
import net.corda.core.contracts.HashAttachmentConstraint
|
||||||
|
import net.corda.core.contracts.NoConstraintPropagation
|
||||||
|
import net.corda.core.contracts.SignatureAttachmentConstraint
|
||||||
|
import net.corda.core.contracts.StateRef
|
||||||
|
import net.corda.core.contracts.WhitelistedByZoneAttachmentConstraint
|
||||||
import net.corda.core.crypto.Crypto
|
import net.corda.core.crypto.Crypto
|
||||||
import net.corda.core.crypto.SecureHash
|
import net.corda.core.crypto.SecureHash
|
||||||
import net.corda.core.crypto.SecureHash.Companion.allOnesHash
|
import net.corda.core.crypto.SecureHash.Companion.allOnesHash
|
||||||
@ -14,17 +22,16 @@ import net.corda.core.crypto.sign
|
|||||||
import net.corda.core.identity.AbstractParty
|
import net.corda.core.identity.AbstractParty
|
||||||
import net.corda.core.identity.CordaX500Name
|
import net.corda.core.identity.CordaX500Name
|
||||||
import net.corda.core.internal.canBeTransitionedFrom
|
import net.corda.core.internal.canBeTransitionedFrom
|
||||||
import net.corda.core.internal.inputStream
|
import net.corda.core.internal.read
|
||||||
import net.corda.core.internal.requireSupportedHashType
|
import net.corda.core.internal.requireSupportedHashType
|
||||||
import net.corda.core.internal.toPath
|
|
||||||
import net.corda.core.node.NotaryInfo
|
import net.corda.core.node.NotaryInfo
|
||||||
import net.corda.core.node.services.IdentityService
|
import net.corda.core.node.services.IdentityService
|
||||||
import net.corda.core.transactions.LedgerTransaction
|
import net.corda.core.transactions.LedgerTransaction
|
||||||
import net.corda.core.transactions.SignedTransaction
|
import net.corda.core.transactions.SignedTransaction
|
||||||
import net.corda.core.transactions.WireTransaction
|
import net.corda.core.transactions.WireTransaction
|
||||||
import net.corda.finance.POUNDS
|
import net.corda.finance.POUNDS
|
||||||
import net.corda.finance.`issued by`
|
|
||||||
import net.corda.finance.contracts.asset.Cash
|
import net.corda.finance.contracts.asset.Cash
|
||||||
|
import net.corda.finance.`issued by`
|
||||||
import net.corda.testing.common.internal.testNetworkParameters
|
import net.corda.testing.common.internal.testNetworkParameters
|
||||||
import net.corda.testing.core.ALICE_NAME
|
import net.corda.testing.core.ALICE_NAME
|
||||||
import net.corda.testing.core.DUMMY_NOTARY_NAME
|
import net.corda.testing.core.DUMMY_NOTARY_NAME
|
||||||
@ -36,7 +43,15 @@ import net.corda.testing.core.internal.SelfCleaningDir
|
|||||||
import net.corda.testing.internal.MockCordappProvider
|
import net.corda.testing.internal.MockCordappProvider
|
||||||
import net.corda.testing.node.MockServices
|
import net.corda.testing.node.MockServices
|
||||||
import net.corda.testing.node.ledger
|
import net.corda.testing.node.ledger
|
||||||
import org.junit.*
|
import org.junit.AfterClass
|
||||||
|
import org.junit.Before
|
||||||
|
import org.junit.BeforeClass
|
||||||
|
import org.junit.Ignore
|
||||||
|
import org.junit.Rule
|
||||||
|
import org.junit.Test
|
||||||
|
import org.mockito.kotlin.doReturn
|
||||||
|
import org.mockito.kotlin.mock
|
||||||
|
import org.mockito.kotlin.whenever
|
||||||
import java.security.PublicKey
|
import java.security.PublicKey
|
||||||
import java.util.jar.Attributes
|
import java.util.jar.Attributes
|
||||||
import kotlin.test.assertFailsWith
|
import kotlin.test.assertFailsWith
|
||||||
@ -91,8 +106,8 @@ class ConstraintsPropagationTests {
|
|||||||
},
|
},
|
||||||
networkParameters = testNetworkParameters(minimumPlatformVersion = 4)
|
networkParameters = testNetworkParameters(minimumPlatformVersion = 4)
|
||||||
.copy(whitelistedContractImplementations = mapOf(
|
.copy(whitelistedContractImplementations = mapOf(
|
||||||
Cash.PROGRAM_ID to listOf(SecureHash.zeroHash, SecureHash.allOnesHash),
|
Cash.PROGRAM_ID to listOf(zeroHash, allOnesHash),
|
||||||
noPropagationContractClassName to listOf(SecureHash.zeroHash)),
|
noPropagationContractClassName to listOf(zeroHash)),
|
||||||
notaries = listOf(NotaryInfo(DUMMY_NOTARY, true)))
|
notaries = listOf(NotaryInfo(DUMMY_NOTARY, true)))
|
||||||
) {
|
) {
|
||||||
override fun loadContractAttachment(stateRef: StateRef) = servicesForResolution.loadContractAttachment(stateRef)
|
override fun loadContractAttachment(stateRef: StateRef) = servicesForResolution.loadContractAttachment(stateRef)
|
||||||
@ -103,13 +118,13 @@ class ConstraintsPropagationTests {
|
|||||||
fun `Happy path with the HashConstraint`() {
|
fun `Happy path with the HashConstraint`() {
|
||||||
ledgerServices.ledger(DUMMY_NOTARY) {
|
ledgerServices.ledger(DUMMY_NOTARY) {
|
||||||
ledgerServices.recordTransaction(transaction {
|
ledgerServices.recordTransaction(transaction {
|
||||||
attachment(Cash.PROGRAM_ID, SecureHash.allOnesHash)
|
attachment(Cash.PROGRAM_ID, allOnesHash)
|
||||||
output(Cash.PROGRAM_ID, "c1", DUMMY_NOTARY, null, HashAttachmentConstraint(allOnesHash), Cash.State(1000.POUNDS `issued by` ALICE_PARTY.ref(1), ALICE_PARTY))
|
output(Cash.PROGRAM_ID, "c1", DUMMY_NOTARY, null, HashAttachmentConstraint(allOnesHash), Cash.State(1000.POUNDS `issued by` ALICE_PARTY.ref(1), ALICE_PARTY))
|
||||||
command(ALICE_PUBKEY, Cash.Commands.Issue())
|
command(ALICE_PUBKEY, Cash.Commands.Issue())
|
||||||
verifies()
|
verifies()
|
||||||
})
|
})
|
||||||
transaction {
|
transaction {
|
||||||
attachment(Cash.PROGRAM_ID, SecureHash.allOnesHash)
|
attachment(Cash.PROGRAM_ID, allOnesHash)
|
||||||
input("c1")
|
input("c1")
|
||||||
output(Cash.PROGRAM_ID, "c2", DUMMY_NOTARY, null, HashAttachmentConstraint(allOnesHash), Cash.State(1000.POUNDS `issued by` ALICE_PARTY.ref(1), BOB_PARTY))
|
output(Cash.PROGRAM_ID, "c2", DUMMY_NOTARY, null, HashAttachmentConstraint(allOnesHash), Cash.State(1000.POUNDS `issued by` ALICE_PARTY.ref(1), BOB_PARTY))
|
||||||
command(ALICE_PUBKEY, Cash.Commands.Move())
|
command(ALICE_PUBKEY, Cash.Commands.Move())
|
||||||
@ -125,13 +140,13 @@ class ConstraintsPropagationTests {
|
|||||||
val cordappAttachmentIds =
|
val cordappAttachmentIds =
|
||||||
cordapps.map { cordapp ->
|
cordapps.map { cordapp ->
|
||||||
val unsignedAttId =
|
val unsignedAttId =
|
||||||
cordapp.jarPath.toPath().inputStream().use { unsignedJarStream ->
|
cordapp.jarPath.openStream().use { unsignedJarStream ->
|
||||||
ledgerServices.attachments.importContractAttachment(cordapp.contractClassNames, "rpc", unsignedJarStream, null)
|
ledgerServices.attachments.importContractAttachment(cordapp.contractClassNames, "rpc", unsignedJarStream, null)
|
||||||
}
|
}
|
||||||
val jarAndSigner = ContractJarTestUtils.signContractJar(cordapp.jarPath, copyFirst = true, keyStoreDir = keyStoreDir.path)
|
val jarAndSigner = ContractJarTestUtils.signContractJar(cordapp.jarPath, copyFirst = true, keyStoreDir = keyStoreDir.path)
|
||||||
val signedJar = jarAndSigner.first
|
val signedJar = jarAndSigner.first
|
||||||
val signedAttId =
|
val signedAttId =
|
||||||
signedJar.inputStream().use { signedJarStream ->
|
signedJar.read { signedJarStream ->
|
||||||
ledgerServices.attachments.importContractAttachment(cordapp.contractClassNames, "rpc", signedJarStream, null, listOf(jarAndSigner.second))
|
ledgerServices.attachments.importContractAttachment(cordapp.contractClassNames, "rpc", signedJarStream, null, listOf(jarAndSigner.second))
|
||||||
}
|
}
|
||||||
Pair(unsignedAttId, signedAttId)
|
Pair(unsignedAttId, signedAttId)
|
||||||
@ -163,14 +178,14 @@ class ConstraintsPropagationTests {
|
|||||||
fun `Fail early in the TransactionBuilder when attempting to change the hash of the HashConstraint on the spending transaction`() {
|
fun `Fail early in the TransactionBuilder when attempting to change the hash of the HashConstraint on the spending transaction`() {
|
||||||
ledgerServices.ledger(DUMMY_NOTARY) {
|
ledgerServices.ledger(DUMMY_NOTARY) {
|
||||||
transaction {
|
transaction {
|
||||||
attachment(Cash.PROGRAM_ID, SecureHash.zeroHash)
|
attachment(Cash.PROGRAM_ID, zeroHash)
|
||||||
output(Cash.PROGRAM_ID, "c1", DUMMY_NOTARY, null, HashAttachmentConstraint(zeroHash), Cash.State(1000.POUNDS `issued by` ALICE_PARTY.ref(1), ALICE_PARTY))
|
output(Cash.PROGRAM_ID, "c1", DUMMY_NOTARY, null, HashAttachmentConstraint(zeroHash), Cash.State(1000.POUNDS `issued by` ALICE_PARTY.ref(1), ALICE_PARTY))
|
||||||
command(ALICE_PUBKEY, Cash.Commands.Issue())
|
command(ALICE_PUBKEY, Cash.Commands.Issue())
|
||||||
verifies()
|
verifies()
|
||||||
}
|
}
|
||||||
assertFailsWith<IllegalArgumentException> {
|
assertFailsWith<IllegalArgumentException> {
|
||||||
transaction {
|
transaction {
|
||||||
attachment(Cash.PROGRAM_ID, SecureHash.allOnesHash)
|
attachment(Cash.PROGRAM_ID, allOnesHash)
|
||||||
input("c1")
|
input("c1")
|
||||||
output(Cash.PROGRAM_ID, "c2", DUMMY_NOTARY, null, HashAttachmentConstraint(allOnesHash), Cash.State(1000.POUNDS `issued by` ALICE_PARTY.ref(1), BOB_PARTY))
|
output(Cash.PROGRAM_ID, "c2", DUMMY_NOTARY, null, HashAttachmentConstraint(allOnesHash), Cash.State(1000.POUNDS `issued by` ALICE_PARTY.ref(1), BOB_PARTY))
|
||||||
command(ALICE_PUBKEY, Cash.Commands.Move())
|
command(ALICE_PUBKEY, Cash.Commands.Move())
|
||||||
@ -184,27 +199,27 @@ class ConstraintsPropagationTests {
|
|||||||
fun `Transaction validation fails, when constraints do not propagate correctly`() {
|
fun `Transaction validation fails, when constraints do not propagate correctly`() {
|
||||||
ledgerServices.ledger(DUMMY_NOTARY) {
|
ledgerServices.ledger(DUMMY_NOTARY) {
|
||||||
ledgerServices.recordTransaction(transaction {
|
ledgerServices.recordTransaction(transaction {
|
||||||
attachment(Cash.PROGRAM_ID, SecureHash.zeroHash)
|
attachment(Cash.PROGRAM_ID, zeroHash)
|
||||||
output(Cash.PROGRAM_ID, "c1", DUMMY_NOTARY, null, HashAttachmentConstraint(zeroHash), Cash.State(1000.POUNDS `issued by` ALICE_PARTY.ref(1), ALICE_PARTY))
|
output(Cash.PROGRAM_ID, "c1", DUMMY_NOTARY, null, HashAttachmentConstraint(zeroHash), Cash.State(1000.POUNDS `issued by` ALICE_PARTY.ref(1), ALICE_PARTY))
|
||||||
command(ALICE_PUBKEY, Cash.Commands.Issue())
|
command(ALICE_PUBKEY, Cash.Commands.Issue())
|
||||||
verifies()
|
verifies()
|
||||||
})
|
})
|
||||||
ledgerServices.recordTransaction(transaction {
|
ledgerServices.recordTransaction(transaction {
|
||||||
attachment(Cash.PROGRAM_ID, SecureHash.zeroHash)
|
attachment(Cash.PROGRAM_ID, zeroHash)
|
||||||
input("c1")
|
input("c1")
|
||||||
output(Cash.PROGRAM_ID, "c2", DUMMY_NOTARY, null, WhitelistedByZoneAttachmentConstraint, Cash.State(1000.POUNDS `issued by` ALICE_PARTY.ref(1), BOB_PARTY))
|
output(Cash.PROGRAM_ID, "c2", DUMMY_NOTARY, null, WhitelistedByZoneAttachmentConstraint, Cash.State(1000.POUNDS `issued by` ALICE_PARTY.ref(1), BOB_PARTY))
|
||||||
command(ALICE_PUBKEY, Cash.Commands.Move())
|
command(ALICE_PUBKEY, Cash.Commands.Move())
|
||||||
failsWith("are not propagated correctly")
|
failsWith("are not propagated correctly")
|
||||||
})
|
})
|
||||||
ledgerServices.recordTransaction(transaction {
|
ledgerServices.recordTransaction(transaction {
|
||||||
attachment(Cash.PROGRAM_ID, SecureHash.zeroHash)
|
attachment(Cash.PROGRAM_ID, zeroHash)
|
||||||
input("c1")
|
input("c1")
|
||||||
output(Cash.PROGRAM_ID, "c3", DUMMY_NOTARY, null, SignatureAttachmentConstraint(ALICE_PUBKEY), Cash.State(1000.POUNDS `issued by` ALICE_PARTY.ref(1), BOB_PARTY))
|
output(Cash.PROGRAM_ID, "c3", DUMMY_NOTARY, null, SignatureAttachmentConstraint(ALICE_PUBKEY), Cash.State(1000.POUNDS `issued by` ALICE_PARTY.ref(1), BOB_PARTY))
|
||||||
command(ALICE_PUBKEY, Cash.Commands.Move())
|
command(ALICE_PUBKEY, Cash.Commands.Move())
|
||||||
fails()
|
fails()
|
||||||
})
|
})
|
||||||
transaction {
|
transaction {
|
||||||
attachment(Cash.PROGRAM_ID, SecureHash.zeroHash)
|
attachment(Cash.PROGRAM_ID, zeroHash)
|
||||||
input("c1")
|
input("c1")
|
||||||
output(Cash.PROGRAM_ID, "c4", DUMMY_NOTARY, null, AlwaysAcceptAttachmentConstraint, Cash.State(1000.POUNDS `issued by` ALICE_PARTY.ref(1), BOB_PARTY))
|
output(Cash.PROGRAM_ID, "c4", DUMMY_NOTARY, null, AlwaysAcceptAttachmentConstraint, Cash.State(1000.POUNDS `issued by` ALICE_PARTY.ref(1), BOB_PARTY))
|
||||||
command(ALICE_PUBKEY, Cash.Commands.Move())
|
command(ALICE_PUBKEY, Cash.Commands.Move())
|
||||||
@ -217,13 +232,13 @@ class ConstraintsPropagationTests {
|
|||||||
fun `When the constraint of the output state is a valid transition from the input state, transaction validation works`() {
|
fun `When the constraint of the output state is a valid transition from the input state, transaction validation works`() {
|
||||||
ledgerServices.ledger(DUMMY_NOTARY) {
|
ledgerServices.ledger(DUMMY_NOTARY) {
|
||||||
ledgerServices.recordTransaction(transaction {
|
ledgerServices.recordTransaction(transaction {
|
||||||
attachment(Cash.PROGRAM_ID, SecureHash.zeroHash)
|
attachment(Cash.PROGRAM_ID, zeroHash)
|
||||||
output(Cash.PROGRAM_ID, "c1", DUMMY_NOTARY, null, WhitelistedByZoneAttachmentConstraint, Cash.State(1000.POUNDS `issued by` ALICE_PARTY.ref(1), ALICE_PARTY))
|
output(Cash.PROGRAM_ID, "c1", DUMMY_NOTARY, null, WhitelistedByZoneAttachmentConstraint, Cash.State(1000.POUNDS `issued by` ALICE_PARTY.ref(1), ALICE_PARTY))
|
||||||
command(ALICE_PUBKEY, Cash.Commands.Issue())
|
command(ALICE_PUBKEY, Cash.Commands.Issue())
|
||||||
verifies()
|
verifies()
|
||||||
})
|
})
|
||||||
transaction {
|
transaction {
|
||||||
attachment(Cash.PROGRAM_ID, SecureHash.zeroHash)
|
attachment(Cash.PROGRAM_ID, zeroHash)
|
||||||
input("c1")
|
input("c1")
|
||||||
output(Cash.PROGRAM_ID, "c2", DUMMY_NOTARY, null, HashAttachmentConstraint(zeroHash), Cash.State(1000.POUNDS `issued by` ALICE_PARTY.ref(1), BOB_PARTY))
|
output(Cash.PROGRAM_ID, "c2", DUMMY_NOTARY, null, HashAttachmentConstraint(zeroHash), Cash.State(1000.POUNDS `issued by` ALICE_PARTY.ref(1), BOB_PARTY))
|
||||||
command(ALICE_PUBKEY, Cash.Commands.Move())
|
command(ALICE_PUBKEY, Cash.Commands.Move())
|
||||||
@ -237,7 +252,7 @@ class ConstraintsPropagationTests {
|
|||||||
|
|
||||||
ledgerServices.ledger(DUMMY_NOTARY) {
|
ledgerServices.ledger(DUMMY_NOTARY) {
|
||||||
ledgerServices.recordTransaction(transaction {
|
ledgerServices.recordTransaction(transaction {
|
||||||
attachment(Cash.PROGRAM_ID, SecureHash.zeroHash)
|
attachment(Cash.PROGRAM_ID, zeroHash)
|
||||||
output(Cash.PROGRAM_ID, "w1", DUMMY_NOTARY, null, WhitelistedByZoneAttachmentConstraint, Cash.State(1000.POUNDS `issued by` ALICE_PARTY.ref(1), ALICE_PARTY))
|
output(Cash.PROGRAM_ID, "w1", DUMMY_NOTARY, null, WhitelistedByZoneAttachmentConstraint, Cash.State(1000.POUNDS `issued by` ALICE_PARTY.ref(1), ALICE_PARTY))
|
||||||
command(ALICE_PUBKEY, Cash.Commands.Issue())
|
command(ALICE_PUBKEY, Cash.Commands.Issue())
|
||||||
verifies()
|
verifies()
|
||||||
@ -245,7 +260,7 @@ class ConstraintsPropagationTests {
|
|||||||
|
|
||||||
// the attachment is signed
|
// the attachment is signed
|
||||||
transaction {
|
transaction {
|
||||||
attachment(Cash.PROGRAM_ID, SecureHash.allOnesHash, listOf(hashToSignatureConstraintsKey))
|
attachment(Cash.PROGRAM_ID, allOnesHash, listOf(hashToSignatureConstraintsKey))
|
||||||
input("w1")
|
input("w1")
|
||||||
output(Cash.PROGRAM_ID, "w2", DUMMY_NOTARY, null, SignatureAttachmentConstraint(hashToSignatureConstraintsKey), Cash.State(1000.POUNDS `issued by` ALICE_PARTY.ref(1), BOB_PARTY))
|
output(Cash.PROGRAM_ID, "w2", DUMMY_NOTARY, null, SignatureAttachmentConstraint(hashToSignatureConstraintsKey), Cash.State(1000.POUNDS `issued by` ALICE_PARTY.ref(1), BOB_PARTY))
|
||||||
command(ALICE_PUBKEY, Cash.Commands.Move())
|
command(ALICE_PUBKEY, Cash.Commands.Move())
|
||||||
@ -258,14 +273,14 @@ class ConstraintsPropagationTests {
|
|||||||
fun `Switching from the WhitelistConstraint to the Signature Constraint fails if the signature constraint does not inherit all jar signatures`() {
|
fun `Switching from the WhitelistConstraint to the Signature Constraint fails if the signature constraint does not inherit all jar signatures`() {
|
||||||
ledgerServices.ledger(DUMMY_NOTARY) {
|
ledgerServices.ledger(DUMMY_NOTARY) {
|
||||||
ledgerServices.recordTransaction(transaction {
|
ledgerServices.recordTransaction(transaction {
|
||||||
attachment(Cash.PROGRAM_ID, SecureHash.zeroHash)
|
attachment(Cash.PROGRAM_ID, zeroHash)
|
||||||
output(Cash.PROGRAM_ID, "w1", DUMMY_NOTARY, null, WhitelistedByZoneAttachmentConstraint, Cash.State(1000.POUNDS `issued by` ALICE_PARTY.ref(1), ALICE_PARTY))
|
output(Cash.PROGRAM_ID, "w1", DUMMY_NOTARY, null, WhitelistedByZoneAttachmentConstraint, Cash.State(1000.POUNDS `issued by` ALICE_PARTY.ref(1), ALICE_PARTY))
|
||||||
command(ALICE_PUBKEY, Cash.Commands.Issue())
|
command(ALICE_PUBKEY, Cash.Commands.Issue())
|
||||||
verifies()
|
verifies()
|
||||||
})
|
})
|
||||||
// the attachment is not signed
|
// the attachment is not signed
|
||||||
transaction {
|
transaction {
|
||||||
attachment(Cash.PROGRAM_ID, SecureHash.zeroHash)
|
attachment(Cash.PROGRAM_ID, zeroHash)
|
||||||
input("w1")
|
input("w1")
|
||||||
output(Cash.PROGRAM_ID, "w2", DUMMY_NOTARY, null, SignatureAttachmentConstraint(ALICE_PUBKEY), Cash.State(1000.POUNDS `issued by` ALICE_PARTY.ref(1), BOB_PARTY))
|
output(Cash.PROGRAM_ID, "w2", DUMMY_NOTARY, null, SignatureAttachmentConstraint(ALICE_PUBKEY), Cash.State(1000.POUNDS `issued by` ALICE_PARTY.ref(1), BOB_PARTY))
|
||||||
command(ALICE_PUBKEY, Cash.Commands.Move())
|
command(ALICE_PUBKEY, Cash.Commands.Move())
|
||||||
@ -279,13 +294,13 @@ class ConstraintsPropagationTests {
|
|||||||
fun `On contract annotated with NoConstraintPropagation there is no platform check for propagation, but the transaction builder can't use the AutomaticPlaceholderConstraint`() {
|
fun `On contract annotated with NoConstraintPropagation there is no platform check for propagation, but the transaction builder can't use the AutomaticPlaceholderConstraint`() {
|
||||||
ledgerServices.ledger(DUMMY_NOTARY) {
|
ledgerServices.ledger(DUMMY_NOTARY) {
|
||||||
ledgerServices.recordTransaction(transaction {
|
ledgerServices.recordTransaction(transaction {
|
||||||
attachment(noPropagationContractClassName, SecureHash.zeroHash)
|
attachment(noPropagationContractClassName, zeroHash)
|
||||||
output(noPropagationContractClassName, "c1", DUMMY_NOTARY, null, HashAttachmentConstraint(zeroHash), NoPropagationContractState())
|
output(noPropagationContractClassName, "c1", DUMMY_NOTARY, null, HashAttachmentConstraint(zeroHash), NoPropagationContractState())
|
||||||
command(ALICE_PUBKEY, NoPropagationContract.Create())
|
command(ALICE_PUBKEY, NoPropagationContract.Create())
|
||||||
verifies()
|
verifies()
|
||||||
})
|
})
|
||||||
ledgerServices.recordTransaction(transaction {
|
ledgerServices.recordTransaction(transaction {
|
||||||
attachment(noPropagationContractClassName, SecureHash.zeroHash)
|
attachment(noPropagationContractClassName, zeroHash)
|
||||||
input("c1")
|
input("c1")
|
||||||
output(noPropagationContractClassName, "c2", DUMMY_NOTARY, null, WhitelistedByZoneAttachmentConstraint, NoPropagationContractState())
|
output(noPropagationContractClassName, "c2", DUMMY_NOTARY, null, WhitelistedByZoneAttachmentConstraint, NoPropagationContractState())
|
||||||
command(ALICE_PUBKEY, NoPropagationContract.Create())
|
command(ALICE_PUBKEY, NoPropagationContract.Create())
|
||||||
@ -293,7 +308,7 @@ class ConstraintsPropagationTests {
|
|||||||
})
|
})
|
||||||
assertFailsWith<IllegalArgumentException> {
|
assertFailsWith<IllegalArgumentException> {
|
||||||
transaction {
|
transaction {
|
||||||
attachment(noPropagationContractClassName, SecureHash.zeroHash)
|
attachment(noPropagationContractClassName, zeroHash)
|
||||||
input("c1")
|
input("c1")
|
||||||
output(noPropagationContractClassName, "c3", DUMMY_NOTARY, null, AutomaticPlaceholderConstraint, NoPropagationContractState())
|
output(noPropagationContractClassName, "c3", DUMMY_NOTARY, null, AutomaticPlaceholderConstraint, NoPropagationContractState())
|
||||||
command(ALICE_PUBKEY, NoPropagationContract.Create())
|
command(ALICE_PUBKEY, NoPropagationContract.Create())
|
||||||
@ -387,13 +402,13 @@ class ConstraintsPropagationTests {
|
|||||||
fun `Input state contract version may be incompatible with lower version`() {
|
fun `Input state contract version may be incompatible with lower version`() {
|
||||||
ledgerServices.ledger(DUMMY_NOTARY) {
|
ledgerServices.ledger(DUMMY_NOTARY) {
|
||||||
ledgerServices.recordTransaction(transaction {
|
ledgerServices.recordTransaction(transaction {
|
||||||
attachment(Cash.PROGRAM_ID, SecureHash.allOnesHash, listOf(hashToSignatureConstraintsKey), mapOf(Attributes.Name.IMPLEMENTATION_VERSION.toString() to "2"))
|
attachment(Cash.PROGRAM_ID, allOnesHash, listOf(hashToSignatureConstraintsKey), mapOf(Attributes.Name.IMPLEMENTATION_VERSION.toString() to "2"))
|
||||||
output(Cash.PROGRAM_ID, "c1", DUMMY_NOTARY, null, SignatureAttachmentConstraint(hashToSignatureConstraintsKey), Cash.State(1000.POUNDS `issued by` ALICE_PARTY.ref(1), ALICE_PARTY))
|
output(Cash.PROGRAM_ID, "c1", DUMMY_NOTARY, null, SignatureAttachmentConstraint(hashToSignatureConstraintsKey), Cash.State(1000.POUNDS `issued by` ALICE_PARTY.ref(1), ALICE_PARTY))
|
||||||
command(ALICE_PUBKEY, Cash.Commands.Issue())
|
command(ALICE_PUBKEY, Cash.Commands.Issue())
|
||||||
verifies()
|
verifies()
|
||||||
})
|
})
|
||||||
transaction {
|
transaction {
|
||||||
attachment(Cash.PROGRAM_ID, SecureHash.zeroHash, listOf(hashToSignatureConstraintsKey), mapOf(Attributes.Name.IMPLEMENTATION_VERSION.toString() to "1"))
|
attachment(Cash.PROGRAM_ID, zeroHash, listOf(hashToSignatureConstraintsKey), mapOf(Attributes.Name.IMPLEMENTATION_VERSION.toString() to "1"))
|
||||||
input("c1")
|
input("c1")
|
||||||
output(Cash.PROGRAM_ID, "c2", DUMMY_NOTARY, null, SignatureAttachmentConstraint(hashToSignatureConstraintsKey), Cash.State(1000.POUNDS `issued by` ALICE_PARTY.ref(1), BOB_PARTY))
|
output(Cash.PROGRAM_ID, "c2", DUMMY_NOTARY, null, SignatureAttachmentConstraint(hashToSignatureConstraintsKey), Cash.State(1000.POUNDS `issued by` ALICE_PARTY.ref(1), BOB_PARTY))
|
||||||
command(ALICE_PUBKEY, Cash.Commands.Move())
|
command(ALICE_PUBKEY, Cash.Commands.Move())
|
||||||
@ -406,13 +421,13 @@ class ConstraintsPropagationTests {
|
|||||||
fun `Input state contract version is compatible with the same version`() {
|
fun `Input state contract version is compatible with the same version`() {
|
||||||
ledgerServices.ledger(DUMMY_NOTARY) {
|
ledgerServices.ledger(DUMMY_NOTARY) {
|
||||||
ledgerServices.recordTransaction(transaction {
|
ledgerServices.recordTransaction(transaction {
|
||||||
attachment(Cash.PROGRAM_ID, SecureHash.allOnesHash, listOf(hashToSignatureConstraintsKey), mapOf(Attributes.Name.IMPLEMENTATION_VERSION.toString() to "3"))
|
attachment(Cash.PROGRAM_ID, allOnesHash, listOf(hashToSignatureConstraintsKey), mapOf(Attributes.Name.IMPLEMENTATION_VERSION.toString() to "3"))
|
||||||
output(Cash.PROGRAM_ID, "c1", DUMMY_NOTARY, null, SignatureAttachmentConstraint(hashToSignatureConstraintsKey), Cash.State(1000.POUNDS `issued by` ALICE_PARTY.ref(1), ALICE_PARTY))
|
output(Cash.PROGRAM_ID, "c1", DUMMY_NOTARY, null, SignatureAttachmentConstraint(hashToSignatureConstraintsKey), Cash.State(1000.POUNDS `issued by` ALICE_PARTY.ref(1), ALICE_PARTY))
|
||||||
command(ALICE_PUBKEY, Cash.Commands.Issue())
|
command(ALICE_PUBKEY, Cash.Commands.Issue())
|
||||||
verifies()
|
verifies()
|
||||||
})
|
})
|
||||||
transaction {
|
transaction {
|
||||||
attachment(Cash.PROGRAM_ID, SecureHash.zeroHash, listOf(hashToSignatureConstraintsKey), mapOf(Attributes.Name.IMPLEMENTATION_VERSION.toString() to "3"))
|
attachment(Cash.PROGRAM_ID, zeroHash, listOf(hashToSignatureConstraintsKey), mapOf(Attributes.Name.IMPLEMENTATION_VERSION.toString() to "3"))
|
||||||
input("c1")
|
input("c1")
|
||||||
output(Cash.PROGRAM_ID, "c2", DUMMY_NOTARY, null, SignatureAttachmentConstraint(hashToSignatureConstraintsKey), Cash.State(1000.POUNDS `issued by` ALICE_PARTY.ref(1), BOB_PARTY))
|
output(Cash.PROGRAM_ID, "c2", DUMMY_NOTARY, null, SignatureAttachmentConstraint(hashToSignatureConstraintsKey), Cash.State(1000.POUNDS `issued by` ALICE_PARTY.ref(1), BOB_PARTY))
|
||||||
command(ALICE_PUBKEY, Cash.Commands.Move())
|
command(ALICE_PUBKEY, Cash.Commands.Move())
|
||||||
@ -425,13 +440,13 @@ class ConstraintsPropagationTests {
|
|||||||
fun `Input state contract version is compatible with higher version`() {
|
fun `Input state contract version is compatible with higher version`() {
|
||||||
ledgerServices.ledger(DUMMY_NOTARY) {
|
ledgerServices.ledger(DUMMY_NOTARY) {
|
||||||
ledgerServices.recordTransaction(transaction {
|
ledgerServices.recordTransaction(transaction {
|
||||||
attachment(Cash.PROGRAM_ID, SecureHash.allOnesHash, listOf(hashToSignatureConstraintsKey), mapOf(Attributes.Name.IMPLEMENTATION_VERSION.toString() to "1"))
|
attachment(Cash.PROGRAM_ID, allOnesHash, listOf(hashToSignatureConstraintsKey), mapOf(Attributes.Name.IMPLEMENTATION_VERSION.toString() to "1"))
|
||||||
output(Cash.PROGRAM_ID, "c1", DUMMY_NOTARY, null, SignatureAttachmentConstraint(hashToSignatureConstraintsKey), Cash.State(1000.POUNDS `issued by` ALICE_PARTY.ref(1), ALICE_PARTY))
|
output(Cash.PROGRAM_ID, "c1", DUMMY_NOTARY, null, SignatureAttachmentConstraint(hashToSignatureConstraintsKey), Cash.State(1000.POUNDS `issued by` ALICE_PARTY.ref(1), ALICE_PARTY))
|
||||||
command(ALICE_PUBKEY, Cash.Commands.Issue())
|
command(ALICE_PUBKEY, Cash.Commands.Issue())
|
||||||
verifies()
|
verifies()
|
||||||
})
|
})
|
||||||
transaction {
|
transaction {
|
||||||
attachment(Cash.PROGRAM_ID, SecureHash.zeroHash, listOf(hashToSignatureConstraintsKey), mapOf(Attributes.Name.IMPLEMENTATION_VERSION.toString() to "2"))
|
attachment(Cash.PROGRAM_ID, zeroHash, listOf(hashToSignatureConstraintsKey), mapOf(Attributes.Name.IMPLEMENTATION_VERSION.toString() to "2"))
|
||||||
input("c1")
|
input("c1")
|
||||||
output(Cash.PROGRAM_ID, "c2", DUMMY_NOTARY, null, SignatureAttachmentConstraint(hashToSignatureConstraintsKey), Cash.State(1000.POUNDS `issued by` ALICE_PARTY.ref(1), BOB_PARTY))
|
output(Cash.PROGRAM_ID, "c2", DUMMY_NOTARY, null, SignatureAttachmentConstraint(hashToSignatureConstraintsKey), Cash.State(1000.POUNDS `issued by` ALICE_PARTY.ref(1), BOB_PARTY))
|
||||||
command(ALICE_PUBKEY, Cash.Commands.Move())
|
command(ALICE_PUBKEY, Cash.Commands.Move())
|
||||||
@ -444,13 +459,13 @@ class ConstraintsPropagationTests {
|
|||||||
fun `Input states contract version may be lower that current contract version`() {
|
fun `Input states contract version may be lower that current contract version`() {
|
||||||
ledgerServices.ledger(DUMMY_NOTARY) {
|
ledgerServices.ledger(DUMMY_NOTARY) {
|
||||||
ledgerServices.recordTransaction(transaction {
|
ledgerServices.recordTransaction(transaction {
|
||||||
attachment(Cash.PROGRAM_ID, SecureHash.allOnesHash, listOf(hashToSignatureConstraintsKey), mapOf(Attributes.Name.IMPLEMENTATION_VERSION.toString() to "1"))
|
attachment(Cash.PROGRAM_ID, allOnesHash, listOf(hashToSignatureConstraintsKey), mapOf(Attributes.Name.IMPLEMENTATION_VERSION.toString() to "1"))
|
||||||
output(Cash.PROGRAM_ID, "c1", DUMMY_NOTARY, null, SignatureAttachmentConstraint(hashToSignatureConstraintsKey), Cash.State(1000.POUNDS `issued by` ALICE_PARTY.ref(1), ALICE_PARTY))
|
output(Cash.PROGRAM_ID, "c1", DUMMY_NOTARY, null, SignatureAttachmentConstraint(hashToSignatureConstraintsKey), Cash.State(1000.POUNDS `issued by` ALICE_PARTY.ref(1), ALICE_PARTY))
|
||||||
command(ALICE_PUBKEY, Cash.Commands.Issue())
|
command(ALICE_PUBKEY, Cash.Commands.Issue())
|
||||||
verifies()
|
verifies()
|
||||||
})
|
})
|
||||||
ledgerServices.recordTransaction(transaction {
|
ledgerServices.recordTransaction(transaction {
|
||||||
attachment(Cash.PROGRAM_ID, SecureHash.zeroHash, listOf(hashToSignatureConstraintsKey), mapOf(Attributes.Name.IMPLEMENTATION_VERSION.toString() to "2"))
|
attachment(Cash.PROGRAM_ID, zeroHash, listOf(hashToSignatureConstraintsKey), mapOf(Attributes.Name.IMPLEMENTATION_VERSION.toString() to "2"))
|
||||||
output(Cash.PROGRAM_ID, "c2", DUMMY_NOTARY, null, SignatureAttachmentConstraint(hashToSignatureConstraintsKey), Cash.State(1000.POUNDS `issued by` ALICE_PARTY.ref(1), ALICE_PARTY))
|
output(Cash.PROGRAM_ID, "c2", DUMMY_NOTARY, null, SignatureAttachmentConstraint(hashToSignatureConstraintsKey), Cash.State(1000.POUNDS `issued by` ALICE_PARTY.ref(1), ALICE_PARTY))
|
||||||
command(ALICE_PUBKEY, Cash.Commands.Issue())
|
command(ALICE_PUBKEY, Cash.Commands.Issue())
|
||||||
verifies()
|
verifies()
|
||||||
@ -469,13 +484,13 @@ class ConstraintsPropagationTests {
|
|||||||
fun `Input state with contract version can be downgraded to no version`() {
|
fun `Input state with contract version can be downgraded to no version`() {
|
||||||
ledgerServices.ledger(DUMMY_NOTARY) {
|
ledgerServices.ledger(DUMMY_NOTARY) {
|
||||||
ledgerServices.recordTransaction(transaction {
|
ledgerServices.recordTransaction(transaction {
|
||||||
attachment(Cash.PROGRAM_ID, SecureHash.allOnesHash, listOf(hashToSignatureConstraintsKey), mapOf(Attributes.Name.IMPLEMENTATION_VERSION.toString() to "2"))
|
attachment(Cash.PROGRAM_ID, allOnesHash, listOf(hashToSignatureConstraintsKey), mapOf(Attributes.Name.IMPLEMENTATION_VERSION.toString() to "2"))
|
||||||
output(Cash.PROGRAM_ID, "c1", DUMMY_NOTARY, null, SignatureAttachmentConstraint(hashToSignatureConstraintsKey), Cash.State(1000.POUNDS `issued by` ALICE_PARTY.ref(1), ALICE_PARTY))
|
output(Cash.PROGRAM_ID, "c1", DUMMY_NOTARY, null, SignatureAttachmentConstraint(hashToSignatureConstraintsKey), Cash.State(1000.POUNDS `issued by` ALICE_PARTY.ref(1), ALICE_PARTY))
|
||||||
command(ALICE_PUBKEY, Cash.Commands.Issue())
|
command(ALICE_PUBKEY, Cash.Commands.Issue())
|
||||||
verifies()
|
verifies()
|
||||||
})
|
})
|
||||||
transaction {
|
transaction {
|
||||||
attachment(Cash.PROGRAM_ID, SecureHash.zeroHash, listOf(hashToSignatureConstraintsKey), emptyMap())
|
attachment(Cash.PROGRAM_ID, zeroHash, listOf(hashToSignatureConstraintsKey), emptyMap())
|
||||||
input("c1")
|
input("c1")
|
||||||
output(Cash.PROGRAM_ID, "c2", DUMMY_NOTARY, null, SignatureAttachmentConstraint(hashToSignatureConstraintsKey), Cash.State(1000.POUNDS `issued by` ALICE_PARTY.ref(1), BOB_PARTY))
|
output(Cash.PROGRAM_ID, "c2", DUMMY_NOTARY, null, SignatureAttachmentConstraint(hashToSignatureConstraintsKey), Cash.State(1000.POUNDS `issued by` ALICE_PARTY.ref(1), BOB_PARTY))
|
||||||
command(ALICE_PUBKEY, Cash.Commands.Move())
|
command(ALICE_PUBKEY, Cash.Commands.Move())
|
||||||
@ -488,13 +503,13 @@ class ConstraintsPropagationTests {
|
|||||||
fun `Input state without contract version is compatible with any version`() {
|
fun `Input state without contract version is compatible with any version`() {
|
||||||
ledgerServices.ledger(DUMMY_NOTARY) {
|
ledgerServices.ledger(DUMMY_NOTARY) {
|
||||||
ledgerServices.recordTransaction(transaction {
|
ledgerServices.recordTransaction(transaction {
|
||||||
attachment(Cash.PROGRAM_ID, SecureHash.allOnesHash, listOf(hashToSignatureConstraintsKey), emptyMap())
|
attachment(Cash.PROGRAM_ID, allOnesHash, listOf(hashToSignatureConstraintsKey), emptyMap())
|
||||||
output(Cash.PROGRAM_ID, "c1", DUMMY_NOTARY, null, SignatureAttachmentConstraint(hashToSignatureConstraintsKey), Cash.State(1000.POUNDS `issued by` ALICE_PARTY.ref(1), ALICE_PARTY))
|
output(Cash.PROGRAM_ID, "c1", DUMMY_NOTARY, null, SignatureAttachmentConstraint(hashToSignatureConstraintsKey), Cash.State(1000.POUNDS `issued by` ALICE_PARTY.ref(1), ALICE_PARTY))
|
||||||
command(ALICE_PUBKEY, Cash.Commands.Issue())
|
command(ALICE_PUBKEY, Cash.Commands.Issue())
|
||||||
verifies()
|
verifies()
|
||||||
})
|
})
|
||||||
transaction {
|
transaction {
|
||||||
attachment(Cash.PROGRAM_ID, SecureHash.zeroHash, listOf(hashToSignatureConstraintsKey), mapOf(Attributes.Name.IMPLEMENTATION_VERSION.toString() to "2"))
|
attachment(Cash.PROGRAM_ID, zeroHash, listOf(hashToSignatureConstraintsKey), mapOf(Attributes.Name.IMPLEMENTATION_VERSION.toString() to "2"))
|
||||||
input("c1")
|
input("c1")
|
||||||
output(Cash.PROGRAM_ID, "c2", DUMMY_NOTARY, null, SignatureAttachmentConstraint(hashToSignatureConstraintsKey), Cash.State(1000.POUNDS `issued by` ALICE_PARTY.ref(1), BOB_PARTY))
|
output(Cash.PROGRAM_ID, "c2", DUMMY_NOTARY, null, SignatureAttachmentConstraint(hashToSignatureConstraintsKey), Cash.State(1000.POUNDS `issued by` ALICE_PARTY.ref(1), BOB_PARTY))
|
||||||
command(ALICE_PUBKEY, Cash.Commands.Move())
|
command(ALICE_PUBKEY, Cash.Commands.Move())
|
||||||
|
@ -1,11 +1,16 @@
|
|||||||
package net.corda.coretests.contracts
|
package net.corda.coretests.contracts
|
||||||
|
|
||||||
import net.corda.core.contracts.*
|
import net.corda.core.contracts.CommandData
|
||||||
|
import net.corda.core.contracts.CommandWithParties
|
||||||
|
import net.corda.core.contracts.TypeOnlyCommandData
|
||||||
|
import net.corda.core.contracts.requireSingleCommand
|
||||||
|
import net.corda.core.contracts.select
|
||||||
import net.corda.core.identity.AbstractParty
|
import net.corda.core.identity.AbstractParty
|
||||||
import net.corda.core.identity.CordaX500Name
|
import net.corda.core.identity.CordaX500Name
|
||||||
import net.corda.core.identity.Party
|
import net.corda.core.identity.Party
|
||||||
import net.corda.testing.core.TestIdentity
|
import net.corda.testing.core.TestIdentity
|
||||||
import org.assertj.core.api.Assertions
|
import org.assertj.core.api.Assertions.assertThatIllegalArgumentException
|
||||||
|
import org.assertj.core.api.Assertions.assertThatThrownBy
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.junit.runner.RunWith
|
import org.junit.runner.RunWith
|
||||||
import org.junit.runners.Parameterized
|
import org.junit.runners.Parameterized
|
||||||
@ -35,8 +40,8 @@ class RequireSingleCommandTests(private val testFunction: (Collection<CommandWit
|
|||||||
@JvmStatic
|
@JvmStatic
|
||||||
@Parameterized.Parameters(name = "{1}")
|
@Parameterized.Parameters(name = "{1}")
|
||||||
fun data(): Collection<Array<Any>> = listOf(
|
fun data(): Collection<Array<Any>> = listOf(
|
||||||
arrayOf<Any>({ commands: Collection<CommandWithParties<CommandData>> -> commands.requireSingleCommand<TestCommands>() }, "Inline version"),
|
arrayOf({ commands: Collection<CommandWithParties<CommandData>> -> commands.requireSingleCommand<TestCommands>() }, "Inline version"),
|
||||||
arrayOf<Any>({ commands: Collection<CommandWithParties<CommandData>> -> commands.requireSingleCommand(TestCommands::class.java) }, "Interop version")
|
arrayOf({ commands: Collection<CommandWithParties<CommandData>> -> commands.requireSingleCommand(TestCommands::class.java) }, "Interop version")
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -47,16 +52,18 @@ class RequireSingleCommandTests(private val testFunction: (Collection<CommandWit
|
|||||||
assertEquals(returnedCommand, validCommandOne, "they should be the same")
|
assertEquals(returnedCommand, validCommandOne, "they should be the same")
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(expected = IllegalArgumentException::class, timeout=300_000)
|
@Test(timeout=300_000)
|
||||||
fun `check error is thrown if more than one valid command`() {
|
fun `check error is thrown if more than one valid command`() {
|
||||||
val commands = listOf(validCommandOne, validCommandTwo)
|
val commands = listOf(validCommandOne, validCommandTwo)
|
||||||
|
assertThatIllegalArgumentException().isThrownBy {
|
||||||
testFunction(commands)
|
testFunction(commands)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test(timeout=300_000)
|
@Test(timeout=300_000)
|
||||||
fun `check error is thrown when command is of wrong type`() {
|
fun `check error is thrown when command is of wrong type`() {
|
||||||
val commands = listOf(invalidCommand)
|
val commands = listOf(invalidCommand)
|
||||||
Assertions.assertThatThrownBy { testFunction(commands) }
|
assertThatThrownBy { testFunction(commands) }
|
||||||
.isInstanceOf(IllegalStateException::class.java)
|
.isInstanceOf(IllegalStateException::class.java)
|
||||||
.hasMessage("Required net.corda.coretests.contracts.TestCommands command")
|
.hasMessage("Required net.corda.coretests.contracts.TestCommands command")
|
||||||
}
|
}
|
||||||
@ -69,8 +76,8 @@ class SelectWithSingleInputsTests(private val testFunction: (Collection<CommandW
|
|||||||
@JvmStatic
|
@JvmStatic
|
||||||
@Parameterized.Parameters(name = "{1}")
|
@Parameterized.Parameters(name = "{1}")
|
||||||
fun data(): Collection<Array<Any>> = listOf(
|
fun data(): Collection<Array<Any>> = listOf(
|
||||||
arrayOf<Any>({ commands: Collection<CommandWithParties<CommandData>>, signer: PublicKey?, party: AbstractParty? -> commands.select<TestCommands>(signer, party) }, "Inline version"),
|
arrayOf({ commands: Collection<CommandWithParties<CommandData>>, signer: PublicKey?, party: AbstractParty? -> commands.select<TestCommands>(signer, party) }, "Inline version"),
|
||||||
arrayOf<Any>({ commands: Collection<CommandWithParties<CommandData>>, signer: PublicKey?, party: AbstractParty? -> commands.select(TestCommands::class.java, signer, party) }, "Interop version")
|
arrayOf({ commands: Collection<CommandWithParties<CommandData>>, signer: PublicKey?, party: AbstractParty? -> commands.select(TestCommands::class.java, signer, party) }, "Interop version")
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -118,8 +125,8 @@ class SelectWithMultipleInputsTests(private val testFunction: (Collection<Comman
|
|||||||
@JvmStatic
|
@JvmStatic
|
||||||
@Parameterized.Parameters(name = "{1}")
|
@Parameterized.Parameters(name = "{1}")
|
||||||
fun data(): Collection<Array<Any>> = listOf(
|
fun data(): Collection<Array<Any>> = listOf(
|
||||||
arrayOf<Any>({ commands: Collection<CommandWithParties<CommandData>>, signers: Collection<PublicKey>?, party: Collection<Party>? -> commands.select<TestCommands>(signers, party) }, "Inline version"),
|
arrayOf({ commands: Collection<CommandWithParties<CommandData>>, signers: Collection<PublicKey>?, party: Collection<Party>? -> commands.select<TestCommands>(signers, party) }, "Inline version"),
|
||||||
arrayOf<Any>({ commands: Collection<CommandWithParties<CommandData>>, signers: Collection<PublicKey>?, party: Collection<Party>? -> commands.select(TestCommands::class.java, signers, party) }, "Interop version")
|
arrayOf({ commands: Collection<CommandWithParties<CommandData>>, signers: Collection<PublicKey>?, party: Collection<Party>? -> commands.select(TestCommands::class.java, signers, party) }, "Interop version")
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
package net.corda.coretests.contracts
|
package net.corda.coretests.contracts
|
||||||
|
|
||||||
import com.nhaarman.mockito_kotlin.doReturn
|
import org.mockito.kotlin.doReturn
|
||||||
import com.nhaarman.mockito_kotlin.mock
|
import org.mockito.kotlin.mock
|
||||||
import com.nhaarman.mockito_kotlin.whenever
|
import org.mockito.kotlin.whenever
|
||||||
import net.corda.core.contracts.*
|
import net.corda.core.contracts.*
|
||||||
import net.corda.core.crypto.Crypto
|
import net.corda.core.crypto.Crypto
|
||||||
import net.corda.core.crypto.SecureHash
|
import net.corda.core.crypto.SecureHash
|
||||||
|
@ -3,7 +3,6 @@ package net.corda.coretests.crypto
|
|||||||
import net.corda.core.crypto.*
|
import net.corda.core.crypto.*
|
||||||
import net.corda.core.crypto.CompositeKey.NodeAndWeight
|
import net.corda.core.crypto.CompositeKey.NodeAndWeight
|
||||||
import net.corda.core.internal.declaredField
|
import net.corda.core.internal.declaredField
|
||||||
import net.corda.core.internal.div
|
|
||||||
import net.corda.core.serialization.serialize
|
import net.corda.core.serialization.serialize
|
||||||
import net.corda.core.utilities.OpaqueBytes
|
import net.corda.core.utilities.OpaqueBytes
|
||||||
import net.corda.core.utilities.toBase58String
|
import net.corda.core.utilities.toBase58String
|
||||||
@ -19,6 +18,7 @@ import org.junit.Test
|
|||||||
import org.junit.rules.TemporaryFolder
|
import org.junit.rules.TemporaryFolder
|
||||||
import java.security.PublicKey
|
import java.security.PublicKey
|
||||||
import javax.security.auth.x500.X500Principal
|
import javax.security.auth.x500.X500Principal
|
||||||
|
import kotlin.io.path.div
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
import kotlin.test.assertFailsWith
|
import kotlin.test.assertFailsWith
|
||||||
import kotlin.test.assertFalse
|
import kotlin.test.assertFalse
|
||||||
|
@ -1,11 +1,19 @@
|
|||||||
package net.corda.coretests.crypto
|
package net.corda.coretests.crypto
|
||||||
|
|
||||||
import com.nhaarman.mockito_kotlin.doReturn
|
import net.corda.core.contracts.Command
|
||||||
import com.nhaarman.mockito_kotlin.mock
|
import net.corda.core.contracts.PrivacySalt
|
||||||
import com.nhaarman.mockito_kotlin.whenever
|
import net.corda.core.contracts.StateRef
|
||||||
import net.corda.core.contracts.*
|
import net.corda.core.contracts.TimeWindow
|
||||||
import net.corda.core.crypto.*
|
import net.corda.core.contracts.TransactionState
|
||||||
|
import net.corda.core.crypto.DigestService
|
||||||
|
import net.corda.core.crypto.MerkleTree
|
||||||
|
import net.corda.core.crypto.MerkleTreeException
|
||||||
|
import net.corda.core.crypto.PartialMerkleTree
|
||||||
|
import net.corda.core.crypto.SecureHash
|
||||||
import net.corda.core.crypto.internal.DigestAlgorithmFactory
|
import net.corda.core.crypto.internal.DigestAlgorithmFactory
|
||||||
|
import net.corda.core.crypto.keys
|
||||||
|
import net.corda.core.crypto.randomHash
|
||||||
|
import net.corda.core.crypto.sha256
|
||||||
import net.corda.core.identity.CordaX500Name
|
import net.corda.core.identity.CordaX500Name
|
||||||
import net.corda.core.identity.Party
|
import net.corda.core.identity.Party
|
||||||
import net.corda.core.internal.BLAKE2s256DigestAlgorithm
|
import net.corda.core.internal.BLAKE2s256DigestAlgorithm
|
||||||
@ -16,9 +24,10 @@ import net.corda.core.serialization.deserialize
|
|||||||
import net.corda.core.serialization.serialize
|
import net.corda.core.serialization.serialize
|
||||||
import net.corda.core.transactions.ReferenceStateRef
|
import net.corda.core.transactions.ReferenceStateRef
|
||||||
import net.corda.core.transactions.WireTransaction
|
import net.corda.core.transactions.WireTransaction
|
||||||
|
import net.corda.coretesting.internal.TEST_TX_TIME
|
||||||
import net.corda.finance.DOLLARS
|
import net.corda.finance.DOLLARS
|
||||||
import net.corda.finance.`issued by`
|
|
||||||
import net.corda.finance.contracts.asset.Cash
|
import net.corda.finance.contracts.asset.Cash
|
||||||
|
import net.corda.finance.`issued by`
|
||||||
import net.corda.testing.common.internal.testNetworkParameters
|
import net.corda.testing.common.internal.testNetworkParameters
|
||||||
import net.corda.testing.core.DUMMY_NOTARY_NAME
|
import net.corda.testing.core.DUMMY_NOTARY_NAME
|
||||||
import net.corda.testing.core.SerializationEnvironmentRule
|
import net.corda.testing.core.SerializationEnvironmentRule
|
||||||
@ -26,20 +35,29 @@ import net.corda.testing.core.TestIdentity
|
|||||||
import net.corda.testing.dsl.LedgerDSL
|
import net.corda.testing.dsl.LedgerDSL
|
||||||
import net.corda.testing.dsl.TestLedgerDSLInterpreter
|
import net.corda.testing.dsl.TestLedgerDSLInterpreter
|
||||||
import net.corda.testing.dsl.TestTransactionDSLInterpreter
|
import net.corda.testing.dsl.TestTransactionDSLInterpreter
|
||||||
import net.corda.coretesting.internal.TEST_TX_TIME
|
|
||||||
import net.corda.testing.internal.createWireTransaction
|
import net.corda.testing.internal.createWireTransaction
|
||||||
import net.corda.testing.node.MockServices
|
import net.corda.testing.node.MockServices
|
||||||
import net.corda.testing.node.ledger
|
import net.corda.testing.node.ledger
|
||||||
|
import org.assertj.core.api.Assertions.assertThatIllegalArgumentException
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
import org.junit.Rule
|
import org.junit.Rule
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.junit.runner.RunWith
|
import org.junit.runner.RunWith
|
||||||
import org.junit.runners.Parameterized
|
import org.junit.runners.Parameterized
|
||||||
|
import org.mockito.kotlin.doReturn
|
||||||
|
import org.mockito.kotlin.mock
|
||||||
|
import org.mockito.kotlin.whenever
|
||||||
import java.security.PublicKey
|
import java.security.PublicKey
|
||||||
import java.util.function.Predicate
|
import java.util.function.Predicate
|
||||||
import java.util.stream.IntStream
|
import java.util.stream.IntStream
|
||||||
import kotlin.streams.toList
|
import kotlin.streams.toList
|
||||||
import kotlin.test.*
|
import kotlin.test.assertEquals
|
||||||
|
import kotlin.test.assertFailsWith
|
||||||
|
import kotlin.test.assertFalse
|
||||||
|
import kotlin.test.assertNotEquals
|
||||||
|
import kotlin.test.assertNotNull
|
||||||
|
import kotlin.test.assertNull
|
||||||
|
import kotlin.test.assertTrue
|
||||||
|
|
||||||
@RunWith(Parameterized::class)
|
@RunWith(Parameterized::class)
|
||||||
class PartialMerkleTreeTest(private var digestService: DigestService) {
|
class PartialMerkleTreeTest(private var digestService: DigestService) {
|
||||||
@ -204,7 +222,7 @@ class PartialMerkleTreeTest(private var digestService: DigestService) {
|
|||||||
|
|
||||||
@Test(timeout=300_000)
|
@Test(timeout=300_000)
|
||||||
fun `nothing filtered`() {
|
fun `nothing filtered`() {
|
||||||
val ftxNothing = testTx.buildFilteredTransaction(Predicate { false })
|
val ftxNothing = testTx.buildFilteredTransaction { false }
|
||||||
assertTrue(ftxNothing.componentGroups.isEmpty())
|
assertTrue(ftxNothing.componentGroups.isEmpty())
|
||||||
assertTrue(ftxNothing.attachments.isEmpty())
|
assertTrue(ftxNothing.attachments.isEmpty())
|
||||||
assertTrue(ftxNothing.commands.isEmpty())
|
assertTrue(ftxNothing.commands.isEmpty())
|
||||||
@ -291,11 +309,13 @@ class PartialMerkleTreeTest(private var digestService: DigestService) {
|
|||||||
assertFalse(pmt.verify(wrongRoot, inclHashes))
|
assertFalse(pmt.verify(wrongRoot, inclHashes))
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(expected = Exception::class, timeout=300_000)
|
@Test(timeout=300_000)
|
||||||
fun `hash map serialization not allowed`() {
|
fun `hash map serialization not allowed`() {
|
||||||
val hm1 = hashMapOf("a" to 1, "b" to 2, "c" to 3, "e" to 4)
|
val hm1 = hashMapOf("a" to 1, "b" to 2, "c" to 3, "e" to 4)
|
||||||
|
assertThatIllegalArgumentException().isThrownBy {
|
||||||
hm1.serialize()
|
hm1.serialize()
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun makeSimpleCashWtx(
|
private fun makeSimpleCashWtx(
|
||||||
notary: Party,
|
notary: Party,
|
||||||
@ -322,11 +342,11 @@ class PartialMerkleTreeTest(private var digestService: DigestService) {
|
|||||||
val merkleTree = MerkleTree.getMerkleTree(sampleLeaves, digestService)
|
val merkleTree = MerkleTree.getMerkleTree(sampleLeaves, digestService)
|
||||||
|
|
||||||
// Provided hashes are not in the tree.
|
// Provided hashes are not in the tree.
|
||||||
assertFailsWith<MerkleTreeException> { PartialMerkleTree.build(merkleTree, listOf<SecureHash>(digestService.hash("20"))) }
|
assertFailsWith<MerkleTreeException> { PartialMerkleTree.build(merkleTree, listOf(digestService.hash("20"))) }
|
||||||
// One of the provided hashes is not in the tree.
|
// One of the provided hashes is not in the tree.
|
||||||
assertFailsWith<MerkleTreeException> { PartialMerkleTree.build(merkleTree, listOf<SecureHash>(digestService.hash("20"), digestService.hash("1"), digestService.hash("5"))) }
|
assertFailsWith<MerkleTreeException> { PartialMerkleTree.build(merkleTree, listOf(digestService.hash("20"), digestService.hash("1"), digestService.hash("5"))) }
|
||||||
|
|
||||||
val pmt = PartialMerkleTree.build(merkleTree, listOf<SecureHash>(digestService.hash("1"), digestService.hash("5"), digestService.hash("0"), digestService.hash("19")))
|
val pmt = PartialMerkleTree.build(merkleTree, listOf(digestService.hash("1"), digestService.hash("5"), digestService.hash("0"), digestService.hash("19")))
|
||||||
// First leaf.
|
// First leaf.
|
||||||
assertEquals(0, pmt.leafIndex(digestService.hash("0")))
|
assertEquals(0, pmt.leafIndex(digestService.hash("0")))
|
||||||
// Second leaf.
|
// Second leaf.
|
||||||
@ -340,17 +360,17 @@ class PartialMerkleTreeTest(private var digestService: DigestService) {
|
|||||||
// The provided hash is not in the tree (using a leaf that didn't exist in the original Merkle tree).
|
// The provided hash is not in the tree (using a leaf that didn't exist in the original Merkle tree).
|
||||||
assertFailsWith<MerkleTreeException> { pmt.leafIndex(digestService.hash("30")) }
|
assertFailsWith<MerkleTreeException> { pmt.leafIndex(digestService.hash("30")) }
|
||||||
|
|
||||||
val pmtFirstElementOnly = PartialMerkleTree.build(merkleTree, listOf<SecureHash>(digestService.hash("0")))
|
val pmtFirstElementOnly = PartialMerkleTree.build(merkleTree, listOf(digestService.hash("0")))
|
||||||
assertEquals(0, pmtFirstElementOnly.leafIndex(digestService.hash("0")))
|
assertEquals(0, pmtFirstElementOnly.leafIndex(digestService.hash("0")))
|
||||||
// The provided hash is not in the tree.
|
// The provided hash is not in the tree.
|
||||||
assertFailsWith<MerkleTreeException> { pmtFirstElementOnly.leafIndex(digestService.hash("10")) }
|
assertFailsWith<MerkleTreeException> { pmtFirstElementOnly.leafIndex(digestService.hash("10")) }
|
||||||
|
|
||||||
val pmtLastElementOnly = PartialMerkleTree.build(merkleTree, listOf<SecureHash>(digestService.hash("19")))
|
val pmtLastElementOnly = PartialMerkleTree.build(merkleTree, listOf(digestService.hash("19")))
|
||||||
assertEquals(19, pmtLastElementOnly.leafIndex(digestService.hash("19")))
|
assertEquals(19, pmtLastElementOnly.leafIndex(digestService.hash("19")))
|
||||||
// The provided hash is not in the tree.
|
// The provided hash is not in the tree.
|
||||||
assertFailsWith<MerkleTreeException> { pmtLastElementOnly.leafIndex(digestService.hash("10")) }
|
assertFailsWith<MerkleTreeException> { pmtLastElementOnly.leafIndex(digestService.hash("10")) }
|
||||||
|
|
||||||
val pmtOneElement = PartialMerkleTree.build(merkleTree, listOf<SecureHash>(digestService.hash("5")))
|
val pmtOneElement = PartialMerkleTree.build(merkleTree, listOf(digestService.hash("5")))
|
||||||
assertEquals(5, pmtOneElement.leafIndex(digestService.hash("5")))
|
assertEquals(5, pmtOneElement.leafIndex(digestService.hash("5")))
|
||||||
// The provided hash is not in the tree.
|
// The provided hash is not in the tree.
|
||||||
assertFailsWith<MerkleTreeException> { pmtOneElement.leafIndex(digestService.hash("10")) }
|
assertFailsWith<MerkleTreeException> { pmtOneElement.leafIndex(digestService.hash("10")) }
|
||||||
|
@ -1,17 +1,14 @@
|
|||||||
package net.corda.coretests.crypto
|
package net.corda.coretests.crypto
|
||||||
|
|
||||||
import com.nhaarman.mockito_kotlin.doReturn
|
|
||||||
import com.nhaarman.mockito_kotlin.mock
|
|
||||||
import com.nhaarman.mockito_kotlin.whenever
|
|
||||||
import net.corda.core.contracts.Command
|
import net.corda.core.contracts.Command
|
||||||
import net.corda.core.contracts.PrivacySalt
|
import net.corda.core.contracts.PrivacySalt
|
||||||
import net.corda.core.contracts.StateRef
|
import net.corda.core.contracts.StateRef
|
||||||
import net.corda.core.contracts.TimeWindow
|
import net.corda.core.contracts.TimeWindow
|
||||||
import net.corda.core.contracts.TransactionState
|
import net.corda.core.contracts.TransactionState
|
||||||
|
import net.corda.core.crypto.DigestService
|
||||||
import net.corda.core.crypto.MerkleTree
|
import net.corda.core.crypto.MerkleTree
|
||||||
import net.corda.core.crypto.MerkleTreeException
|
import net.corda.core.crypto.MerkleTreeException
|
||||||
import net.corda.core.crypto.PartialMerkleTree
|
import net.corda.core.crypto.PartialMerkleTree
|
||||||
import net.corda.core.crypto.DigestService
|
|
||||||
import net.corda.core.crypto.SecureHash
|
import net.corda.core.crypto.SecureHash
|
||||||
import net.corda.core.crypto.SecureHash.Companion.SHA2_384
|
import net.corda.core.crypto.SecureHash.Companion.SHA2_384
|
||||||
import net.corda.core.crypto.SecureHash.Companion.hashAs
|
import net.corda.core.crypto.SecureHash.Companion.hashAs
|
||||||
@ -26,9 +23,10 @@ import net.corda.core.serialization.serialize
|
|||||||
import net.corda.core.transactions.ReferenceStateRef
|
import net.corda.core.transactions.ReferenceStateRef
|
||||||
import net.corda.core.transactions.WireTransaction
|
import net.corda.core.transactions.WireTransaction
|
||||||
import net.corda.core.utilities.OpaqueBytes
|
import net.corda.core.utilities.OpaqueBytes
|
||||||
|
import net.corda.coretesting.internal.TEST_TX_TIME
|
||||||
import net.corda.finance.DOLLARS
|
import net.corda.finance.DOLLARS
|
||||||
import net.corda.finance.`issued by`
|
|
||||||
import net.corda.finance.contracts.asset.Cash
|
import net.corda.finance.contracts.asset.Cash
|
||||||
|
import net.corda.finance.`issued by`
|
||||||
import net.corda.testing.common.internal.testNetworkParameters
|
import net.corda.testing.common.internal.testNetworkParameters
|
||||||
import net.corda.testing.core.DUMMY_NOTARY_NAME
|
import net.corda.testing.core.DUMMY_NOTARY_NAME
|
||||||
import net.corda.testing.core.SerializationEnvironmentRule
|
import net.corda.testing.core.SerializationEnvironmentRule
|
||||||
@ -36,10 +34,10 @@ import net.corda.testing.core.TestIdentity
|
|||||||
import net.corda.testing.dsl.LedgerDSL
|
import net.corda.testing.dsl.LedgerDSL
|
||||||
import net.corda.testing.dsl.TestLedgerDSLInterpreter
|
import net.corda.testing.dsl.TestLedgerDSLInterpreter
|
||||||
import net.corda.testing.dsl.TestTransactionDSLInterpreter
|
import net.corda.testing.dsl.TestTransactionDSLInterpreter
|
||||||
import net.corda.coretesting.internal.TEST_TX_TIME
|
|
||||||
import net.corda.testing.internal.createWireTransaction
|
import net.corda.testing.internal.createWireTransaction
|
||||||
import net.corda.testing.node.MockServices
|
import net.corda.testing.node.MockServices
|
||||||
import net.corda.testing.node.ledger
|
import net.corda.testing.node.ledger
|
||||||
|
import org.assertj.core.api.Assertions.assertThatIllegalArgumentException
|
||||||
import org.junit.Assert.assertFalse
|
import org.junit.Assert.assertFalse
|
||||||
import org.junit.Assert.assertNotEquals
|
import org.junit.Assert.assertNotEquals
|
||||||
import org.junit.Assert.assertNotNull
|
import org.junit.Assert.assertNotNull
|
||||||
@ -49,6 +47,9 @@ import org.junit.Before
|
|||||||
import org.junit.Rule
|
import org.junit.Rule
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.junit.jupiter.api.Assertions.assertEquals
|
import org.junit.jupiter.api.Assertions.assertEquals
|
||||||
|
import org.mockito.kotlin.doReturn
|
||||||
|
import org.mockito.kotlin.mock
|
||||||
|
import org.mockito.kotlin.whenever
|
||||||
import java.security.PublicKey
|
import java.security.PublicKey
|
||||||
import java.util.function.Predicate
|
import java.util.function.Predicate
|
||||||
import java.util.stream.IntStream
|
import java.util.stream.IntStream
|
||||||
@ -209,7 +210,7 @@ class PartialMerkleTreeWithNamedHashMultiAlgTreeTest {
|
|||||||
|
|
||||||
@Test(timeout=300_000)
|
@Test(timeout=300_000)
|
||||||
fun `nothing filtered`() {
|
fun `nothing filtered`() {
|
||||||
val ftxNothing = testTx.buildFilteredTransaction(Predicate { false })
|
val ftxNothing = testTx.buildFilteredTransaction { false }
|
||||||
assertTrue(ftxNothing.componentGroups.isEmpty())
|
assertTrue(ftxNothing.componentGroups.isEmpty())
|
||||||
assertTrue(ftxNothing.attachments.isEmpty())
|
assertTrue(ftxNothing.attachments.isEmpty())
|
||||||
assertTrue(ftxNothing.commands.isEmpty())
|
assertTrue(ftxNothing.commands.isEmpty())
|
||||||
@ -296,11 +297,13 @@ class PartialMerkleTreeWithNamedHashMultiAlgTreeTest {
|
|||||||
assertFalse(pmt.verify(wrongRoot, inclHashes))
|
assertFalse(pmt.verify(wrongRoot, inclHashes))
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(expected = Exception::class, timeout=300_000)
|
@Test(timeout=300_000)
|
||||||
fun `hash map serialization not allowed`() {
|
fun `hash map serialization not allowed`() {
|
||||||
val hm1 = hashMapOf("a" to 1, "b" to 2, "c" to 3, "e" to 4)
|
val hm1 = hashMapOf("a" to 1, "b" to 2, "c" to 3, "e" to 4)
|
||||||
|
assertThatIllegalArgumentException().isThrownBy {
|
||||||
hm1.serialize()
|
hm1.serialize()
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun makeSimpleCashWtx(
|
private fun makeSimpleCashWtx(
|
||||||
notary: Party,
|
notary: Party,
|
||||||
|
@ -1,17 +1,14 @@
|
|||||||
package net.corda.coretests.crypto
|
package net.corda.coretests.crypto
|
||||||
|
|
||||||
import com.nhaarman.mockito_kotlin.doReturn
|
|
||||||
import com.nhaarman.mockito_kotlin.mock
|
|
||||||
import com.nhaarman.mockito_kotlin.whenever
|
|
||||||
import net.corda.core.contracts.Command
|
import net.corda.core.contracts.Command
|
||||||
import net.corda.core.contracts.PrivacySalt
|
import net.corda.core.contracts.PrivacySalt
|
||||||
import net.corda.core.contracts.StateRef
|
import net.corda.core.contracts.StateRef
|
||||||
import net.corda.core.contracts.TimeWindow
|
import net.corda.core.contracts.TimeWindow
|
||||||
import net.corda.core.contracts.TransactionState
|
import net.corda.core.contracts.TransactionState
|
||||||
|
import net.corda.core.crypto.DigestService
|
||||||
import net.corda.core.crypto.MerkleTree
|
import net.corda.core.crypto.MerkleTree
|
||||||
import net.corda.core.crypto.MerkleTreeException
|
import net.corda.core.crypto.MerkleTreeException
|
||||||
import net.corda.core.crypto.PartialMerkleTree
|
import net.corda.core.crypto.PartialMerkleTree
|
||||||
import net.corda.core.crypto.DigestService
|
|
||||||
import net.corda.core.crypto.SecureHash
|
import net.corda.core.crypto.SecureHash
|
||||||
import net.corda.core.crypto.SecureHash.Companion.SHA2_384
|
import net.corda.core.crypto.SecureHash.Companion.SHA2_384
|
||||||
import net.corda.core.crypto.SecureHash.Companion.hashAs
|
import net.corda.core.crypto.SecureHash.Companion.hashAs
|
||||||
@ -26,9 +23,10 @@ import net.corda.core.serialization.serialize
|
|||||||
import net.corda.core.transactions.ReferenceStateRef
|
import net.corda.core.transactions.ReferenceStateRef
|
||||||
import net.corda.core.transactions.WireTransaction
|
import net.corda.core.transactions.WireTransaction
|
||||||
import net.corda.core.utilities.OpaqueBytes
|
import net.corda.core.utilities.OpaqueBytes
|
||||||
|
import net.corda.coretesting.internal.TEST_TX_TIME
|
||||||
import net.corda.finance.DOLLARS
|
import net.corda.finance.DOLLARS
|
||||||
import net.corda.finance.`issued by`
|
|
||||||
import net.corda.finance.contracts.asset.Cash
|
import net.corda.finance.contracts.asset.Cash
|
||||||
|
import net.corda.finance.`issued by`
|
||||||
import net.corda.testing.common.internal.testNetworkParameters
|
import net.corda.testing.common.internal.testNetworkParameters
|
||||||
import net.corda.testing.core.DUMMY_NOTARY_NAME
|
import net.corda.testing.core.DUMMY_NOTARY_NAME
|
||||||
import net.corda.testing.core.SerializationEnvironmentRule
|
import net.corda.testing.core.SerializationEnvironmentRule
|
||||||
@ -36,10 +34,10 @@ import net.corda.testing.core.TestIdentity
|
|||||||
import net.corda.testing.dsl.LedgerDSL
|
import net.corda.testing.dsl.LedgerDSL
|
||||||
import net.corda.testing.dsl.TestLedgerDSLInterpreter
|
import net.corda.testing.dsl.TestLedgerDSLInterpreter
|
||||||
import net.corda.testing.dsl.TestTransactionDSLInterpreter
|
import net.corda.testing.dsl.TestTransactionDSLInterpreter
|
||||||
import net.corda.coretesting.internal.TEST_TX_TIME
|
|
||||||
import net.corda.testing.internal.createWireTransaction
|
import net.corda.testing.internal.createWireTransaction
|
||||||
import net.corda.testing.node.MockServices
|
import net.corda.testing.node.MockServices
|
||||||
import net.corda.testing.node.ledger
|
import net.corda.testing.node.ledger
|
||||||
|
import org.assertj.core.api.Assertions.assertThatIllegalArgumentException
|
||||||
import org.junit.Assert.assertFalse
|
import org.junit.Assert.assertFalse
|
||||||
import org.junit.Assert.assertNotEquals
|
import org.junit.Assert.assertNotEquals
|
||||||
import org.junit.Assert.assertNotNull
|
import org.junit.Assert.assertNotNull
|
||||||
@ -49,6 +47,9 @@ import org.junit.Before
|
|||||||
import org.junit.Rule
|
import org.junit.Rule
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.junit.jupiter.api.Assertions.assertEquals
|
import org.junit.jupiter.api.Assertions.assertEquals
|
||||||
|
import org.mockito.kotlin.doReturn
|
||||||
|
import org.mockito.kotlin.mock
|
||||||
|
import org.mockito.kotlin.whenever
|
||||||
import java.security.PublicKey
|
import java.security.PublicKey
|
||||||
import java.util.function.Predicate
|
import java.util.function.Predicate
|
||||||
import java.util.stream.IntStream
|
import java.util.stream.IntStream
|
||||||
@ -209,7 +210,7 @@ class PartialMerkleTreeWithNamedHashTest {
|
|||||||
|
|
||||||
@Test(timeout=300_000)
|
@Test(timeout=300_000)
|
||||||
fun `nothing filtered`() {
|
fun `nothing filtered`() {
|
||||||
val ftxNothing = testTx.buildFilteredTransaction(Predicate { false })
|
val ftxNothing = testTx.buildFilteredTransaction { false }
|
||||||
assertTrue(ftxNothing.componentGroups.isEmpty())
|
assertTrue(ftxNothing.componentGroups.isEmpty())
|
||||||
assertTrue(ftxNothing.attachments.isEmpty())
|
assertTrue(ftxNothing.attachments.isEmpty())
|
||||||
assertTrue(ftxNothing.commands.isEmpty())
|
assertTrue(ftxNothing.commands.isEmpty())
|
||||||
@ -296,11 +297,13 @@ class PartialMerkleTreeWithNamedHashTest {
|
|||||||
assertFalse(pmt.verify(wrongRoot, inclHashes))
|
assertFalse(pmt.verify(wrongRoot, inclHashes))
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(expected = Exception::class, timeout=300_000)
|
@Test(timeout=300_000)
|
||||||
fun `hash map serialization not allowed`() {
|
fun `hash map serialization not allowed`() {
|
||||||
val hm1 = hashMapOf("a" to 1, "b" to 2, "c" to 3, "e" to 4)
|
val hm1 = hashMapOf("a" to 1, "b" to 2, "c" to 3, "e" to 4)
|
||||||
|
assertThatIllegalArgumentException().isThrownBy {
|
||||||
hm1.serialize()
|
hm1.serialize()
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun makeSimpleCashWtx(
|
private fun makeSimpleCashWtx(
|
||||||
notary: Party,
|
notary: Party,
|
||||||
|
@ -6,6 +6,7 @@ import net.corda.core.crypto.sign
|
|||||||
import net.corda.core.serialization.SerializedBytes
|
import net.corda.core.serialization.SerializedBytes
|
||||||
import net.corda.core.serialization.serialize
|
import net.corda.core.serialization.serialize
|
||||||
import net.corda.testing.core.SerializationEnvironmentRule
|
import net.corda.testing.core.SerializationEnvironmentRule
|
||||||
|
import org.assertj.core.api.Assertions.assertThatExceptionOfType
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
import org.junit.Rule
|
import org.junit.Rule
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
@ -35,12 +36,14 @@ class SignedDataTest {
|
|||||||
assertEquals(data, unwrappedData)
|
assertEquals(data, unwrappedData)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(expected = SignatureException::class, timeout=300_000)
|
@Test(timeout=300_000)
|
||||||
fun `make sure incorrectly signed data raises an exception`() {
|
fun `make sure incorrectly signed data raises an exception`() {
|
||||||
val keyPairA = generateKeyPair()
|
val keyPairA = generateKeyPair()
|
||||||
val keyPairB = generateKeyPair()
|
val keyPairB = generateKeyPair()
|
||||||
val sig = keyPairA.private.sign(serialized.bytes, keyPairB.public)
|
val sig = keyPairA.private.sign(serialized.bytes, keyPairB.public)
|
||||||
val wrappedData = SignedData(serialized, sig)
|
val wrappedData = SignedData(serialized, sig)
|
||||||
|
assertThatExceptionOfType(SignatureException::class.java).isThrownBy {
|
||||||
wrappedData.verified()
|
wrappedData.verified()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
@ -1,7 +1,17 @@
|
|||||||
package net.corda.coretests.crypto
|
package net.corda.coretests.crypto
|
||||||
|
|
||||||
import net.corda.core.crypto.*
|
import net.corda.core.crypto.Crypto
|
||||||
|
import net.corda.core.crypto.MerkleTree
|
||||||
|
import net.corda.core.crypto.MerkleTreeException
|
||||||
|
import net.corda.core.crypto.PartialMerkleTree
|
||||||
|
import net.corda.core.crypto.SecureHash
|
||||||
|
import net.corda.core.crypto.SignableData
|
||||||
|
import net.corda.core.crypto.SignatureMetadata
|
||||||
|
import net.corda.core.crypto.TransactionSignature
|
||||||
|
import net.corda.core.crypto.sha256
|
||||||
|
import net.corda.core.crypto.sign
|
||||||
import net.corda.testing.core.SerializationEnvironmentRule
|
import net.corda.testing.core.SerializationEnvironmentRule
|
||||||
|
import org.assertj.core.api.Assertions.assertThatExceptionOfType
|
||||||
import org.junit.Rule
|
import org.junit.Rule
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import java.math.BigInteger
|
import java.math.BigInteger
|
||||||
@ -39,13 +49,15 @@ class TransactionSignatureTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Verification should fail; corrupted metadata - clearData (Merkle root) has changed. */
|
/** Verification should fail; corrupted metadata - clearData (Merkle root) has changed. */
|
||||||
@Test(expected = SignatureException::class,timeout=300_000)
|
@Test(timeout=300_000)
|
||||||
fun `Signature metadata full failure clearData has changed`() {
|
fun `Signature metadata full failure clearData has changed`() {
|
||||||
val keyPair = Crypto.generateKeyPair("ECDSA_SECP256K1_SHA256")
|
val keyPair = Crypto.generateKeyPair("ECDSA_SECP256K1_SHA256")
|
||||||
val signableData = SignableData(testBytes.sha256(), SignatureMetadata(1, Crypto.findSignatureScheme(keyPair.public).schemeNumberID))
|
val signableData = SignableData(testBytes.sha256(), SignatureMetadata(1, Crypto.findSignatureScheme(keyPair.public).schemeNumberID))
|
||||||
val transactionSignature = keyPair.sign(signableData)
|
val transactionSignature = keyPair.sign(signableData)
|
||||||
|
assertThatExceptionOfType(SignatureException::class.java).isThrownBy {
|
||||||
Crypto.doVerify((testBytes + testBytes).sha256(), transactionSignature)
|
Crypto.doVerify((testBytes + testBytes).sha256(), transactionSignature)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test(timeout=300_000)
|
@Test(timeout=300_000)
|
||||||
fun `Verify multi-tx signature`() {
|
fun `Verify multi-tx signature`() {
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package net.corda.coretests.flows
|
package net.corda.coretests.flows
|
||||||
|
|
||||||
import co.paralleluniverse.fibers.Suspendable
|
import co.paralleluniverse.fibers.Suspendable
|
||||||
|
import co.paralleluniverse.strands.Strand
|
||||||
import net.corda.core.CordaException
|
import net.corda.core.CordaException
|
||||||
import net.corda.core.flows.FlowExternalAsyncOperation
|
import net.corda.core.flows.FlowExternalAsyncOperation
|
||||||
import net.corda.core.flows.FlowExternalOperation
|
import net.corda.core.flows.FlowExternalOperation
|
||||||
@ -133,7 +134,7 @@ abstract class AbstractFlowExternalOperationTest {
|
|||||||
fun createFuture(): CompletableFuture<Any> {
|
fun createFuture(): CompletableFuture<Any> {
|
||||||
return CompletableFuture.supplyAsync(Supplier<Any> {
|
return CompletableFuture.supplyAsync(Supplier<Any> {
|
||||||
log.info("Starting sleep inside of future")
|
log.info("Starting sleep inside of future")
|
||||||
Thread.sleep(1000)
|
Strand.sleep(1000)
|
||||||
log.info("Finished sleep inside of future")
|
log.info("Finished sleep inside of future")
|
||||||
"Here is your return value"
|
"Here is your return value"
|
||||||
}, executorService)
|
}, executorService)
|
||||||
|
@ -23,22 +23,22 @@ import net.corda.core.identity.groupAbstractPartyByWellKnownParty
|
|||||||
import net.corda.core.transactions.SignedTransaction
|
import net.corda.core.transactions.SignedTransaction
|
||||||
import net.corda.core.transactions.TransactionBuilder
|
import net.corda.core.transactions.TransactionBuilder
|
||||||
import net.corda.core.utilities.getOrThrow
|
import net.corda.core.utilities.getOrThrow
|
||||||
|
import net.corda.coretesting.internal.matchers.flow.willReturn
|
||||||
|
import net.corda.coretesting.internal.matchers.flow.willThrow
|
||||||
import net.corda.testing.contracts.DummyContract
|
import net.corda.testing.contracts.DummyContract
|
||||||
import net.corda.testing.core.ALICE_NAME
|
import net.corda.testing.core.ALICE_NAME
|
||||||
import net.corda.testing.core.BOB_NAME
|
import net.corda.testing.core.BOB_NAME
|
||||||
import net.corda.testing.core.CHARLIE_NAME
|
import net.corda.testing.core.CHARLIE_NAME
|
||||||
import net.corda.testing.core.TestIdentity
|
import net.corda.testing.core.TestIdentity
|
||||||
import net.corda.testing.core.singleIdentity
|
import net.corda.testing.core.singleIdentity
|
||||||
import net.corda.coretesting.internal.matchers.flow.willReturn
|
|
||||||
import net.corda.coretesting.internal.matchers.flow.willThrow
|
|
||||||
import net.corda.testing.node.MockServices
|
import net.corda.testing.node.MockServices
|
||||||
import net.corda.testing.node.internal.DUMMY_CONTRACTS_CORDAPP
|
import net.corda.testing.node.internal.DUMMY_CONTRACTS_CORDAPP
|
||||||
import net.corda.testing.node.internal.InternalMockNetwork
|
import net.corda.testing.node.internal.InternalMockNetwork
|
||||||
import net.corda.testing.node.internal.TestStartedNode
|
import net.corda.testing.node.internal.TestStartedNode
|
||||||
import net.corda.testing.node.internal.enclosedCordapp
|
import net.corda.testing.node.internal.enclosedCordapp
|
||||||
import org.hamcrest.CoreMatchers.`is`
|
import org.assertj.core.api.Assertions.assertThat
|
||||||
|
import org.assertj.core.api.Assertions.assertThatIllegalArgumentException
|
||||||
import org.junit.AfterClass
|
import org.junit.AfterClass
|
||||||
import org.junit.Assert
|
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import java.security.PublicKey
|
import java.security.PublicKey
|
||||||
|
|
||||||
@ -92,7 +92,7 @@ class CollectSignaturesFlowTests : WithContracts {
|
|||||||
mockNet.runNetwork()
|
mockNet.runNetwork()
|
||||||
val stx = future.get()
|
val stx = future.get()
|
||||||
val missingSigners = stx.getMissingSigners()
|
val missingSigners = stx.getMissingSigners()
|
||||||
Assert.assertThat(missingSigners, `is`(emptySet()))
|
assertThat(missingSigners).isEmpty()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(timeout=300_000)
|
@Test(timeout=300_000)
|
||||||
@ -122,10 +122,10 @@ class CollectSignaturesFlowTests : WithContracts {
|
|||||||
mockNet.runNetwork()
|
mockNet.runNetwork()
|
||||||
val stx = future.get()
|
val stx = future.get()
|
||||||
val missingSigners = stx.getMissingSigners()
|
val missingSigners = stx.getMissingSigners()
|
||||||
Assert.assertThat(missingSigners, `is`(emptySet()))
|
assertThat(missingSigners).isEmpty()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(expected = IllegalArgumentException::class, timeout=300_000)
|
@Test(timeout=300_000)
|
||||||
fun `throws exception when extra sessions are initiated`() {
|
fun `throws exception when extra sessions are initiated`() {
|
||||||
bobNode.registerInitiatedFlow(ExtraSessionsFlowResponder::class.java)
|
bobNode.registerInitiatedFlow(ExtraSessionsFlowResponder::class.java)
|
||||||
charlieNode.registerInitiatedFlow(ExtraSessionsFlowResponder::class.java)
|
charlieNode.registerInitiatedFlow(ExtraSessionsFlowResponder::class.java)
|
||||||
@ -137,8 +137,10 @@ class CollectSignaturesFlowTests : WithContracts {
|
|||||||
listOf(bobNode.info.singleIdentity(), alice)))
|
listOf(bobNode.info.singleIdentity(), alice)))
|
||||||
.resultFuture
|
.resultFuture
|
||||||
mockNet.runNetwork()
|
mockNet.runNetwork()
|
||||||
|
assertThatIllegalArgumentException().isThrownBy {
|
||||||
future.getOrThrow()
|
future.getOrThrow()
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test(timeout=300_000)
|
@Test(timeout=300_000)
|
||||||
fun `it is possible to collect from multiple well known sessions`() {
|
fun `it is possible to collect from multiple well known sessions`() {
|
||||||
@ -152,7 +154,7 @@ class CollectSignaturesFlowTests : WithContracts {
|
|||||||
listOf(bobNode.info.singleIdentity(), alice))).resultFuture
|
listOf(bobNode.info.singleIdentity(), alice))).resultFuture
|
||||||
mockNet.runNetwork()
|
mockNet.runNetwork()
|
||||||
val signedTx = future.getOrThrow()
|
val signedTx = future.getOrThrow()
|
||||||
Assert.assertThat(signedTx.getMissingSigners(), `is`(emptySet()))
|
assertThat(signedTx.getMissingSigners()).isEmpty()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(timeout=300_000)
|
@Test(timeout=300_000)
|
||||||
@ -216,7 +218,7 @@ class CollectSignaturesFlowTests : WithContracts {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@InitiatedBy(TestFlow.Initiator::class)
|
@InitiatedBy(Initiator::class)
|
||||||
class Responder(private val otherSideSession: FlowSession) : FlowLogic<Unit>() {
|
class Responder(private val otherSideSession: FlowSession) : FlowLogic<Unit>() {
|
||||||
@Suspendable
|
@Suspendable
|
||||||
override fun call() {
|
override fun call() {
|
||||||
@ -251,7 +253,7 @@ class AnonymousSessionTestFlow(private val cis: List<PartyAndCertificate>) : Flo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
val state = DummyContract.MultiOwnerState(owners = cis.map { AnonymousParty(it.owningKey) })
|
val state = DummyContract.MultiOwnerState(owners = cis.map { AnonymousParty(it.owningKey) })
|
||||||
val create = net.corda.testing.contracts.DummyContract.Commands.Create()
|
val create = DummyContract.Commands.Create()
|
||||||
val txBuilder = TransactionBuilder(notary = serviceHub.networkMapCache.notaryIdentities.first())
|
val txBuilder = TransactionBuilder(notary = serviceHub.networkMapCache.notaryIdentities.first())
|
||||||
.addOutputState(state)
|
.addOutputState(state)
|
||||||
.addCommand(create, cis.map { it.owningKey })
|
.addCommand(create, cis.map { it.owningKey })
|
||||||
@ -289,7 +291,7 @@ class MixAndMatchAnonymousSessionTestFlow(private val cis: List<PartyAndCertific
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
val state = DummyContract.MultiOwnerState(owners = cis.map { AnonymousParty(it.owningKey) })
|
val state = DummyContract.MultiOwnerState(owners = cis.map { AnonymousParty(it.owningKey) })
|
||||||
val create = net.corda.testing.contracts.DummyContract.Commands.Create()
|
val create = DummyContract.Commands.Create()
|
||||||
val txBuilder = TransactionBuilder(notary = serviceHub.networkMapCache.notaryIdentities.first())
|
val txBuilder = TransactionBuilder(notary = serviceHub.networkMapCache.notaryIdentities.first())
|
||||||
.addOutputState(state)
|
.addOutputState(state)
|
||||||
.addCommand(create, cis.map { it.owningKey })
|
.addCommand(create, cis.map { it.owningKey })
|
||||||
@ -324,7 +326,7 @@ class ExtraSessionsFlow(private val openFor: List<Party>, private val involve: L
|
|||||||
|
|
||||||
val sessions = openFor.map { initiateFlow(it) }
|
val sessions = openFor.map { initiateFlow(it) }
|
||||||
val state = DummyContract.MultiOwnerState(owners = involve.map { AnonymousParty(it.owningKey) })
|
val state = DummyContract.MultiOwnerState(owners = involve.map { AnonymousParty(it.owningKey) })
|
||||||
val create = net.corda.testing.contracts.DummyContract.Commands.Create()
|
val create = DummyContract.Commands.Create()
|
||||||
val txBuilder = TransactionBuilder(notary = serviceHub.networkMapCache.notaryIdentities.first())
|
val txBuilder = TransactionBuilder(notary = serviceHub.networkMapCache.notaryIdentities.first())
|
||||||
.addOutputState(state)
|
.addOutputState(state)
|
||||||
.addCommand(create, involve.map { it.owningKey })
|
.addCommand(create, involve.map { it.owningKey })
|
||||||
|
@ -9,6 +9,7 @@ import net.corda.core.CordaRuntimeException
|
|||||||
import net.corda.core.contracts.ContractState
|
import net.corda.core.contracts.ContractState
|
||||||
import net.corda.core.contracts.StateAndRef
|
import net.corda.core.contracts.StateAndRef
|
||||||
import net.corda.core.flows.ContractUpgradeFlow
|
import net.corda.core.flows.ContractUpgradeFlow
|
||||||
|
import net.corda.core.internal.getRequiredTransaction
|
||||||
import net.corda.core.messaging.CordaRPCOps
|
import net.corda.core.messaging.CordaRPCOps
|
||||||
import net.corda.core.transactions.ContractUpgradeLedgerTransaction
|
import net.corda.core.transactions.ContractUpgradeLedgerTransaction
|
||||||
import net.corda.core.transactions.SignedTransaction
|
import net.corda.core.transactions.SignedTransaction
|
||||||
@ -120,8 +121,7 @@ class ContractUpgradeFlowRPCTest : WithContracts, WithFinality {
|
|||||||
isUpgrade<FROM, TO>())
|
isUpgrade<FROM, TO>())
|
||||||
|
|
||||||
private fun TestStartedNode.getContractUpgradeTransaction(state: StateAndRef<ContractState>) =
|
private fun TestStartedNode.getContractUpgradeTransaction(state: StateAndRef<ContractState>) =
|
||||||
services.validatedTransactions.getTransaction(state.ref.txhash)!!
|
services.getRequiredTransaction(state.ref.txhash).resolveContractUpgradeTransaction(services)
|
||||||
.resolveContractUpgradeTransaction(services)
|
|
||||||
|
|
||||||
private inline fun <reified FROM : Any, reified TO : Any> isUpgrade() =
|
private inline fun <reified FROM : Any, reified TO : Any> isUpgrade() =
|
||||||
isUpgradeFrom<FROM>() and isUpgradeTo<TO>()
|
isUpgradeFrom<FROM>() and isUpgradeTo<TO>()
|
||||||
|
@ -1,31 +1,54 @@
|
|||||||
package net.corda.coretests.flows
|
package net.corda.coretests.flows
|
||||||
|
|
||||||
import com.natpryce.hamkrest.*
|
import com.natpryce.hamkrest.Matcher
|
||||||
|
import com.natpryce.hamkrest.and
|
||||||
|
import com.natpryce.hamkrest.anything
|
||||||
import com.natpryce.hamkrest.assertion.assertThat
|
import com.natpryce.hamkrest.assertion.assertThat
|
||||||
import net.corda.core.contracts.*
|
import com.natpryce.hamkrest.equalTo
|
||||||
|
import com.natpryce.hamkrest.has
|
||||||
|
import com.natpryce.hamkrest.isA
|
||||||
|
import net.corda.core.contracts.AlwaysAcceptAttachmentConstraint
|
||||||
|
import net.corda.core.contracts.Amount
|
||||||
|
import net.corda.core.contracts.AttachmentConstraint
|
||||||
|
import net.corda.core.contracts.BelongsToContract
|
||||||
|
import net.corda.core.contracts.CommandAndState
|
||||||
|
import net.corda.core.contracts.ContractState
|
||||||
|
import net.corda.core.contracts.FungibleAsset
|
||||||
|
import net.corda.core.contracts.Issued
|
||||||
|
import net.corda.core.contracts.StateAndRef
|
||||||
|
import net.corda.core.contracts.TypeOnlyCommandData
|
||||||
|
import net.corda.core.contracts.UpgradedContractWithLegacyConstraint
|
||||||
import net.corda.core.flows.UnexpectedFlowEndException
|
import net.corda.core.flows.UnexpectedFlowEndException
|
||||||
import net.corda.core.identity.AbstractParty
|
import net.corda.core.identity.AbstractParty
|
||||||
import net.corda.core.internal.Emoji
|
import net.corda.core.internal.Emoji
|
||||||
|
import net.corda.core.internal.getRequiredTransaction
|
||||||
|
import net.corda.core.internal.mapToSet
|
||||||
import net.corda.core.transactions.ContractUpgradeLedgerTransaction
|
import net.corda.core.transactions.ContractUpgradeLedgerTransaction
|
||||||
import net.corda.core.transactions.LedgerTransaction
|
import net.corda.core.transactions.LedgerTransaction
|
||||||
import net.corda.core.utilities.OpaqueBytes
|
import net.corda.core.utilities.OpaqueBytes
|
||||||
import net.corda.core.utilities.getOrThrow
|
import net.corda.core.utilities.getOrThrow
|
||||||
|
import net.corda.coretesting.internal.matchers.flow.willReturn
|
||||||
|
import net.corda.coretesting.internal.matchers.flow.willThrow
|
||||||
import net.corda.finance.USD
|
import net.corda.finance.USD
|
||||||
import net.corda.finance.`issued by`
|
|
||||||
import net.corda.finance.contracts.asset.Cash
|
import net.corda.finance.contracts.asset.Cash
|
||||||
import net.corda.finance.flows.CashIssueFlow
|
import net.corda.finance.flows.CashIssueFlow
|
||||||
|
import net.corda.finance.`issued by`
|
||||||
import net.corda.testing.contracts.DummyContract
|
import net.corda.testing.contracts.DummyContract
|
||||||
import net.corda.testing.contracts.DummyContractV2
|
import net.corda.testing.contracts.DummyContractV2
|
||||||
import net.corda.testing.contracts.DummyContractV3
|
import net.corda.testing.contracts.DummyContractV3
|
||||||
import net.corda.testing.core.ALICE_NAME
|
import net.corda.testing.core.ALICE_NAME
|
||||||
import net.corda.testing.core.BOB_NAME
|
import net.corda.testing.core.BOB_NAME
|
||||||
import net.corda.testing.core.singleIdentity
|
import net.corda.testing.core.singleIdentity
|
||||||
import net.corda.coretesting.internal.matchers.flow.willReturn
|
import net.corda.testing.node.internal.DUMMY_CONTRACTS_CORDAPP
|
||||||
import net.corda.coretesting.internal.matchers.flow.willThrow
|
import net.corda.testing.node.internal.FINANCE_CONTRACTS_CORDAPP
|
||||||
import net.corda.testing.node.internal.*
|
import net.corda.testing.node.internal.FINANCE_WORKFLOWS_CORDAPP
|
||||||
|
import net.corda.testing.node.internal.InternalMockNetwork
|
||||||
|
import net.corda.testing.node.internal.TestStartedNode
|
||||||
|
import net.corda.testing.node.internal.enclosedCordapp
|
||||||
|
import net.corda.testing.node.internal.startFlow
|
||||||
import org.junit.AfterClass
|
import org.junit.AfterClass
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import java.util.*
|
import java.util.Currency
|
||||||
|
|
||||||
class ContractUpgradeFlowTest : WithContracts, WithFinality {
|
class ContractUpgradeFlowTest : WithContracts, WithFinality {
|
||||||
|
|
||||||
@ -159,7 +182,7 @@ class ContractUpgradeFlowTest : WithContracts, WithFinality {
|
|||||||
@BelongsToContract(CashV2::class)
|
@BelongsToContract(CashV2::class)
|
||||||
data class State(override val amount: Amount<Issued<Currency>>, val owners: List<AbstractParty>) : FungibleAsset<Currency> {
|
data class State(override val amount: Amount<Issued<Currency>>, val owners: List<AbstractParty>) : FungibleAsset<Currency> {
|
||||||
override val owner: AbstractParty = owners.first()
|
override val owner: AbstractParty = owners.first()
|
||||||
override val exitKeys = (owners + amount.token.issuer.party).map { it.owningKey }.toSet()
|
override val exitKeys = (owners + amount.token.issuer.party).mapToSet { it.owningKey }
|
||||||
override val participants = owners
|
override val participants = owners
|
||||||
|
|
||||||
override fun withNewOwnerAndAmount(newAmount: Amount<Issued<Currency>>, newOwner: AbstractParty) = copy(amount = amount.copy(newAmount.quantity), owners = listOf(newOwner))
|
override fun withNewOwnerAndAmount(newAmount: Amount<Issued<Currency>>, newOwner: AbstractParty) = copy(amount = amount.copy(newAmount.quantity), owners = listOf(newOwner))
|
||||||
@ -180,8 +203,7 @@ class ContractUpgradeFlowTest : WithContracts, WithFinality {
|
|||||||
isUpgrade<FROM, TO>())
|
isUpgrade<FROM, TO>())
|
||||||
|
|
||||||
private fun TestStartedNode.getContractUpgradeTransaction(state: StateAndRef<ContractState>) =
|
private fun TestStartedNode.getContractUpgradeTransaction(state: StateAndRef<ContractState>) =
|
||||||
services.validatedTransactions.getTransaction(state.ref.txhash)!!
|
services.getRequiredTransaction(state.ref.txhash).resolveContractUpgradeTransaction(services)
|
||||||
.resolveContractUpgradeTransaction(services)
|
|
||||||
|
|
||||||
private inline fun <reified FROM : Any, reified TO : Any> isUpgrade() =
|
private inline fun <reified FROM : Any, reified TO : Any> isUpgrade() =
|
||||||
isUpgradeFrom<FROM>() and isUpgradeTo<TO>()
|
isUpgradeFrom<FROM>() and isUpgradeTo<TO>()
|
||||||
|
@ -15,6 +15,7 @@ import net.corda.testing.core.singleIdentity
|
|||||||
import net.corda.testing.driver.DriverParameters
|
import net.corda.testing.driver.DriverParameters
|
||||||
import net.corda.testing.driver.driver
|
import net.corda.testing.driver.driver
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
|
import java.io.Serializable
|
||||||
import java.sql.SQLTransientConnectionException
|
import java.sql.SQLTransientConnectionException
|
||||||
import java.util.concurrent.CompletableFuture
|
import java.util.concurrent.CompletableFuture
|
||||||
import kotlin.test.assertFailsWith
|
import kotlin.test.assertFailsWith
|
||||||
@ -22,6 +23,8 @@ import kotlin.test.assertTrue
|
|||||||
|
|
||||||
class FlowExternalAsyncOperationTest : AbstractFlowExternalOperationTest() {
|
class FlowExternalAsyncOperationTest : AbstractFlowExternalOperationTest() {
|
||||||
|
|
||||||
|
private fun interface SerializableLambda2<S, T, R> : (S, T) -> R, Serializable
|
||||||
|
|
||||||
@Test(timeout = 300_000)
|
@Test(timeout = 300_000)
|
||||||
fun `external async operation`() {
|
fun `external async operation`() {
|
||||||
driver(DriverParameters(notarySpecs = emptyList(), startNodesInProcess = true)) {
|
driver(DriverParameters(notarySpecs = emptyList(), startNodesInProcess = true)) {
|
||||||
@ -196,15 +199,15 @@ class FlowExternalAsyncOperationTest : AbstractFlowExternalOperationTest() {
|
|||||||
@StartableByRPC
|
@StartableByRPC
|
||||||
class FlowWithExternalAsyncOperationPropagatesException<T>(party: Party, private val exceptionType: Class<T>) :
|
class FlowWithExternalAsyncOperationPropagatesException<T>(party: Party, private val exceptionType: Class<T>) :
|
||||||
FlowWithExternalProcess(party) {
|
FlowWithExternalProcess(party) {
|
||||||
|
|
||||||
@Suspendable
|
@Suspendable
|
||||||
override fun testCode(): Any {
|
override fun testCode(): Any {
|
||||||
val e = createException()
|
val e = createException()
|
||||||
return await(ExternalAsyncOperation(serviceHub) { _, _ ->
|
|
||||||
|
return await(ExternalAsyncOperation(serviceHub, (SerializableLambda2 { _, _ ->
|
||||||
CompletableFuture<Any>().apply {
|
CompletableFuture<Any>().apply {
|
||||||
completeExceptionally(e)
|
completeExceptionally(e)
|
||||||
}
|
}
|
||||||
})
|
})))
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createException() = when (exceptionType) {
|
private fun createException() = when (exceptionType) {
|
||||||
@ -252,7 +255,6 @@ class FlowExternalAsyncOperationTest : AbstractFlowExternalOperationTest() {
|
|||||||
@StartableByRPC
|
@StartableByRPC
|
||||||
class FlowWithExternalAsyncOperationThatDirectlyAccessesServiceHubFailsRetry(party: Party) : FlowWithExternalProcess(party) {
|
class FlowWithExternalAsyncOperationThatDirectlyAccessesServiceHubFailsRetry(party: Party) : FlowWithExternalProcess(party) {
|
||||||
|
|
||||||
@Suppress("TooGenericExceptionCaught")
|
|
||||||
@Suspendable
|
@Suspendable
|
||||||
override fun testCode(): Any {
|
override fun testCode(): Any {
|
||||||
return await(ExternalAsyncOperation(serviceHub) { _, _ ->
|
return await(ExternalAsyncOperation(serviceHub) { _, _ ->
|
||||||
|
@ -21,12 +21,15 @@ import net.corda.testing.driver.DriverParameters
|
|||||||
import net.corda.testing.driver.driver
|
import net.corda.testing.driver.driver
|
||||||
import net.corda.testing.node.internal.cordappsForPackages
|
import net.corda.testing.node.internal.cordappsForPackages
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
|
import java.io.Serializable
|
||||||
import java.sql.SQLTransientConnectionException
|
import java.sql.SQLTransientConnectionException
|
||||||
import kotlin.test.assertFailsWith
|
import kotlin.test.assertFailsWith
|
||||||
import kotlin.test.assertTrue
|
import kotlin.test.assertTrue
|
||||||
|
|
||||||
class FlowExternalOperationTest : AbstractFlowExternalOperationTest() {
|
class FlowExternalOperationTest : AbstractFlowExternalOperationTest() {
|
||||||
|
|
||||||
|
private fun interface SerializableLambda2<S, T, R> : (S, T) -> R, Serializable
|
||||||
|
|
||||||
@Test(timeout = 300_000)
|
@Test(timeout = 300_000)
|
||||||
fun `external operation`() {
|
fun `external operation`() {
|
||||||
driver(DriverParameters(notarySpecs = emptyList(), startNodesInProcess = true)) {
|
driver(DriverParameters(notarySpecs = emptyList(), startNodesInProcess = true)) {
|
||||||
@ -254,7 +257,7 @@ class FlowExternalOperationTest : AbstractFlowExternalOperationTest() {
|
|||||||
@Suspendable
|
@Suspendable
|
||||||
override fun testCode() {
|
override fun testCode() {
|
||||||
val e = createException()
|
val e = createException()
|
||||||
await(ExternalOperation(serviceHub) { _, _ -> throw e })
|
await(ExternalOperation(serviceHub, (SerializableLambda2 { _, _ -> throw e })))
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createException() = when (exceptionType) {
|
private fun createException() = when (exceptionType) {
|
||||||
@ -292,7 +295,6 @@ class FlowExternalOperationTest : AbstractFlowExternalOperationTest() {
|
|||||||
@StartableByRPC
|
@StartableByRPC
|
||||||
class FlowWithExternalOperationThatDirectlyAccessesServiceHubFailsRetry(party: Party) : FlowWithExternalProcess(party) {
|
class FlowWithExternalOperationThatDirectlyAccessesServiceHubFailsRetry(party: Party) : FlowWithExternalProcess(party) {
|
||||||
|
|
||||||
@Suppress("TooGenericExceptionCaught")
|
|
||||||
@Suspendable
|
@Suspendable
|
||||||
override fun testCode(): Any {
|
override fun testCode(): Any {
|
||||||
try {
|
try {
|
||||||
|
@ -20,11 +20,13 @@ import net.corda.testing.driver.DriverParameters
|
|||||||
import net.corda.testing.driver.driver
|
import net.corda.testing.driver.driver
|
||||||
import net.corda.testing.internal.IS_S390X
|
import net.corda.testing.internal.IS_S390X
|
||||||
import org.junit.Assume
|
import org.junit.Assume
|
||||||
|
import org.junit.Ignore
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import java.time.Duration
|
import java.time.Duration
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
import kotlin.test.assertTrue
|
import kotlin.test.assertTrue
|
||||||
|
|
||||||
|
@Ignore("TODO JDK17: Fixme - flaky test")
|
||||||
class FlowSleepTest {
|
class FlowSleepTest {
|
||||||
|
|
||||||
@Test(timeout = 300_000)
|
@Test(timeout = 300_000)
|
||||||
|
@ -13,6 +13,7 @@ import net.corda.core.flows.ReceiveFinalityFlow
|
|||||||
import net.corda.core.flows.StartableByRPC
|
import net.corda.core.flows.StartableByRPC
|
||||||
import net.corda.core.identity.Party
|
import net.corda.core.identity.Party
|
||||||
import net.corda.core.internal.FlowStateMachineHandle
|
import net.corda.core.internal.FlowStateMachineHandle
|
||||||
|
import net.corda.core.internal.getRequiredTransaction
|
||||||
import net.corda.core.messaging.CordaRPCOps
|
import net.corda.core.messaging.CordaRPCOps
|
||||||
import net.corda.core.messaging.FlowHandle
|
import net.corda.core.messaging.FlowHandle
|
||||||
import net.corda.core.messaging.startFlow
|
import net.corda.core.messaging.startFlow
|
||||||
@ -26,9 +27,7 @@ interface WithFinality : WithMockNet {
|
|||||||
return startFlowAndRunNetwork(FinalityInvoker(stx, recipients.toSet(), emptySet()))
|
return startFlowAndRunNetwork(FinalityInvoker(stx, recipients.toSet(), emptySet()))
|
||||||
}
|
}
|
||||||
|
|
||||||
fun TestStartedNode.getValidatedTransaction(stx: SignedTransaction): SignedTransaction {
|
fun TestStartedNode.getValidatedTransaction(stx: SignedTransaction): SignedTransaction = services.getRequiredTransaction(stx.id)
|
||||||
return services.validatedTransactions.getTransaction(stx.id)!!
|
|
||||||
}
|
|
||||||
|
|
||||||
fun CordaRPCOps.finalise(stx: SignedTransaction, vararg recipients: Party): FlowHandle<SignedTransaction> {
|
fun CordaRPCOps.finalise(stx: SignedTransaction, vararg recipients: Party): FlowHandle<SignedTransaction> {
|
||||||
return startFlow(WithFinality::FinalityInvoker, stx, recipients.toSet(), emptySet()).andRunNetwork()
|
return startFlow(WithFinality::FinalityInvoker, stx, recipients.toSet(), emptySet()).andRunNetwork()
|
||||||
|
@ -0,0 +1,137 @@
|
|||||||
|
package net.corda.coretests.internal.verification
|
||||||
|
|
||||||
|
import net.corda.core.internal.verification.AttachmentFixups
|
||||||
|
import net.corda.core.node.services.AttachmentId
|
||||||
|
import net.corda.node.VersionInfo
|
||||||
|
import net.corda.node.internal.cordapp.JarScanningCordappLoader
|
||||||
|
import org.assertj.core.api.Assertions.assertThat
|
||||||
|
import org.junit.Test
|
||||||
|
import java.net.URL
|
||||||
|
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.toUri().toURL())) {
|
||||||
|
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.toUri().toURL())) {
|
||||||
|
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.toUri().toURL())
|
||||||
|
}
|
||||||
|
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.toUri().toURL())
|
||||||
|
}
|
||||||
|
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.toUri().toURL())
|
||||||
|
}
|
||||||
|
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.toUri().toURL())) {
|
||||||
|
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 urls: URL): AttachmentFixups {
|
||||||
|
val loader = JarScanningCordappLoader.fromJarUrls(urls.toList(), VersionInfo.UNKNOWN)
|
||||||
|
return AttachmentFixups().apply { load(loader.appClassLoader) }
|
||||||
|
}
|
||||||
|
}
|
@ -1,7 +1,7 @@
|
|||||||
package net.corda.coretests.node
|
package net.corda.coretests.node
|
||||||
|
|
||||||
import com.nhaarman.mockito_kotlin.doReturn
|
import org.mockito.kotlin.doReturn
|
||||||
import com.nhaarman.mockito_kotlin.whenever
|
import org.mockito.kotlin.whenever
|
||||||
import net.corda.core.crypto.generateKeyPair
|
import net.corda.core.crypto.generateKeyPair
|
||||||
import net.corda.core.internal.getPackageOwnerOf
|
import net.corda.core.internal.getPackageOwnerOf
|
||||||
import net.corda.core.node.NetworkParameters
|
import net.corda.core.node.NetworkParameters
|
||||||
|
@ -15,7 +15,7 @@ class VaultUpdateTests {
|
|||||||
private companion object {
|
private companion object {
|
||||||
const val DUMMY_PROGRAM_ID = "net.corda.coretests.node.VaultUpdateTests\$DummyContract"
|
const val DUMMY_PROGRAM_ID = "net.corda.coretests.node.VaultUpdateTests\$DummyContract"
|
||||||
val DUMMY_NOTARY = TestIdentity(DUMMY_NOTARY_NAME, 20).party
|
val DUMMY_NOTARY = TestIdentity(DUMMY_NOTARY_NAME, 20).party
|
||||||
val emptyUpdate = Vault.Update(emptySet(), emptySet(), type = Vault.UpdateType.GENERAL, references = emptySet())
|
val emptyUpdate = Vault.Update(emptySet<StateAndRef<*>>(), emptySet(), type = Vault.UpdateType.GENERAL, references = emptySet())
|
||||||
}
|
}
|
||||||
|
|
||||||
object DummyContract : Contract {
|
object DummyContract : Contract {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
package net.corda.coretests.serialization
|
package net.corda.coretests.serialization
|
||||||
|
|
||||||
import com.nhaarman.mockito_kotlin.mock
|
import org.mockito.kotlin.mock
|
||||||
import net.corda.core.contracts.*
|
import net.corda.core.contracts.*
|
||||||
import net.corda.core.crypto.Crypto
|
import net.corda.core.crypto.Crypto
|
||||||
import net.corda.core.crypto.SignatureMetadata
|
import net.corda.core.crypto.SignatureMetadata
|
||||||
|
@ -19,7 +19,6 @@ import net.corda.core.internal.AttachmentTrustCalculator
|
|||||||
import net.corda.core.internal.createLedgerTransaction
|
import net.corda.core.internal.createLedgerTransaction
|
||||||
import net.corda.core.internal.declaredField
|
import net.corda.core.internal.declaredField
|
||||||
import net.corda.core.internal.hash
|
import net.corda.core.internal.hash
|
||||||
import net.corda.core.internal.inputStream
|
|
||||||
import net.corda.core.node.NetworkParameters
|
import net.corda.core.node.NetworkParameters
|
||||||
import net.corda.core.node.services.AttachmentId
|
import net.corda.core.node.services.AttachmentId
|
||||||
import net.corda.core.serialization.internal.AttachmentsClassLoader
|
import net.corda.core.serialization.internal.AttachmentsClassLoader
|
||||||
@ -44,7 +43,6 @@ import org.apache.commons.io.IOUtils
|
|||||||
import org.assertj.core.api.Assertions.assertThat
|
import org.assertj.core.api.Assertions.assertThat
|
||||||
import org.junit.Assert.assertArrayEquals
|
import org.junit.Assert.assertArrayEquals
|
||||||
import org.junit.Assert.assertEquals
|
import org.junit.Assert.assertEquals
|
||||||
import org.junit.Assert.assertNull
|
|
||||||
import org.junit.Assert.assertTrue
|
import org.junit.Assert.assertTrue
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
import org.junit.Rule
|
import org.junit.Rule
|
||||||
@ -55,14 +53,16 @@ import java.io.InputStream
|
|||||||
import java.net.URL
|
import java.net.URL
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
import java.security.PublicKey
|
import java.security.PublicKey
|
||||||
|
import kotlin.io.path.inputStream
|
||||||
|
import kotlin.io.path.readBytes
|
||||||
import kotlin.test.assertFailsWith
|
import kotlin.test.assertFailsWith
|
||||||
import kotlin.test.fail
|
import kotlin.test.fail
|
||||||
|
|
||||||
class AttachmentsClassLoaderTests {
|
class AttachmentsClassLoaderTests {
|
||||||
companion object {
|
companion object {
|
||||||
// TODO Update this test to use the new isolated.jar
|
// TODO Update this test to use the new isolated.jar
|
||||||
val ISOLATED_CONTRACTS_JAR_PATH: URL = AttachmentsClassLoaderTests::class.java.getResource("old-isolated.jar")
|
val ISOLATED_CONTRACTS_JAR_PATH: URL = AttachmentsClassLoaderTests::class.java.getResource("old-isolated.jar")!!
|
||||||
val ISOLATED_CONTRACTS_JAR_PATH_V4: URL = AttachmentsClassLoaderTests::class.java.getResource("isolated-4.0.jar")
|
val ISOLATED_CONTRACTS_JAR_PATH_V4: URL = AttachmentsClassLoaderTests::class.java.getResource("isolated-4.0.jar")!!
|
||||||
private const val ISOLATED_CONTRACT_CLASS_NAME = "net.corda.finance.contracts.isolated.AnotherDummyContract"
|
private const val ISOLATED_CONTRACT_CLASS_NAME = "net.corda.finance.contracts.isolated.AnotherDummyContract"
|
||||||
|
|
||||||
private fun readAttachment(attachment: Attachment, filepath: String): ByteArray {
|
private fun readAttachment(attachment: Attachment, filepath: String): ByteArray {
|
||||||
@ -128,8 +128,6 @@ class AttachmentsClassLoaderTests {
|
|||||||
@Test(timeout=300_000)
|
@Test(timeout=300_000)
|
||||||
fun `test contracts have no permissions for protection domain`() {
|
fun `test contracts have no permissions for protection domain`() {
|
||||||
val isolatedId = importAttachment(ISOLATED_CONTRACTS_JAR_PATH.openStream(), "app", "isolated.jar")
|
val isolatedId = importAttachment(ISOLATED_CONTRACTS_JAR_PATH.openStream(), "app", "isolated.jar")
|
||||||
assertNull(System.getSecurityManager())
|
|
||||||
|
|
||||||
createClassloader(isolatedId).use { classLoader ->
|
createClassloader(isolatedId).use { classLoader ->
|
||||||
val contractClass = Class.forName(ISOLATED_CONTRACT_CLASS_NAME, true, classLoader)
|
val contractClass = Class.forName(ISOLATED_CONTRACT_CLASS_NAME, true, classLoader)
|
||||||
val protectionDomain = contractClass.protectionDomain ?: fail("Protection Domain missing")
|
val protectionDomain = contractClass.protectionDomain ?: fail("Protection Domain missing")
|
||||||
@ -614,7 +612,7 @@ class AttachmentsClassLoaderTests {
|
|||||||
|
|
||||||
private fun createAttachments(contractJarPath: Path) : List<Attachment> {
|
private fun createAttachments(contractJarPath: Path) : List<Attachment> {
|
||||||
|
|
||||||
val attachment = object : AbstractAttachment({contractJarPath.inputStream().readBytes()}, uploader = "app") {
|
val attachment = object : AbstractAttachment(contractJarPath::readBytes, uploader = "app") {
|
||||||
@Suppress("OverridingDeprecatedMember")
|
@Suppress("OverridingDeprecatedMember")
|
||||||
@Deprecated("Use signerKeys. There is no requirement that attachment signers are Corda parties.")
|
@Deprecated("Use signerKeys. There is no requirement that attachment signers are Corda parties.")
|
||||||
override val signers: List<Party> = emptyList()
|
override val signers: List<Party> = emptyList()
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
package net.corda.coretests.transactions
|
package net.corda.coretests.transactions
|
||||||
|
|
||||||
import com.nhaarman.mockito_kotlin.doReturn
|
import org.mockito.kotlin.doReturn
|
||||||
import com.nhaarman.mockito_kotlin.mock
|
import org.mockito.kotlin.mock
|
||||||
import com.nhaarman.mockito_kotlin.whenever
|
import org.mockito.kotlin.whenever
|
||||||
import net.corda.core.contracts.*
|
import net.corda.core.contracts.*
|
||||||
import net.corda.core.crypto.generateKeyPair
|
import net.corda.core.crypto.generateKeyPair
|
||||||
import net.corda.core.identity.AbstractParty
|
import net.corda.core.identity.AbstractParty
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
package net.corda.coretests.transactions
|
package net.corda.coretests.transactions
|
||||||
|
|
||||||
import com.nhaarman.mockito_kotlin.doReturn
|
import org.mockito.kotlin.doReturn
|
||||||
import com.nhaarman.mockito_kotlin.mock
|
import org.mockito.kotlin.mock
|
||||||
import com.nhaarman.mockito_kotlin.whenever
|
import org.mockito.kotlin.whenever
|
||||||
import net.corda.core.contracts.*
|
import net.corda.core.contracts.*
|
||||||
import net.corda.core.contracts.Requirements.using
|
import net.corda.core.contracts.Requirements.using
|
||||||
import net.corda.core.crypto.SecureHash
|
import net.corda.core.crypto.SecureHash
|
||||||
|
@ -1,10 +1,6 @@
|
|||||||
package net.corda.coretests.transactions
|
package net.corda.coretests.transactions
|
||||||
|
|
||||||
import com.nhaarman.mockito_kotlin.doReturn
|
|
||||||
import com.nhaarman.mockito_kotlin.mock
|
|
||||||
import com.nhaarman.mockito_kotlin.whenever
|
|
||||||
import net.corda.core.contracts.Command
|
import net.corda.core.contracts.Command
|
||||||
import net.corda.core.contracts.ContractAttachment
|
|
||||||
import net.corda.core.contracts.HashAttachmentConstraint
|
import net.corda.core.contracts.HashAttachmentConstraint
|
||||||
import net.corda.core.contracts.PrivacySalt
|
import net.corda.core.contracts.PrivacySalt
|
||||||
import net.corda.core.contracts.SignatureAttachmentConstraint
|
import net.corda.core.contracts.SignatureAttachmentConstraint
|
||||||
@ -12,42 +8,34 @@ import net.corda.core.contracts.StateAndRef
|
|||||||
import net.corda.core.contracts.StateRef
|
import net.corda.core.contracts.StateRef
|
||||||
import net.corda.core.contracts.TimeWindow
|
import net.corda.core.contracts.TimeWindow
|
||||||
import net.corda.core.contracts.TransactionState
|
import net.corda.core.contracts.TransactionState
|
||||||
import net.corda.core.contracts.TransactionVerificationException
|
import net.corda.core.contracts.TransactionVerificationException.UnsupportedHashTypeException
|
||||||
import net.corda.core.cordapp.CordappProvider
|
|
||||||
import net.corda.core.crypto.CompositeKey
|
|
||||||
import net.corda.core.crypto.DigestService
|
import net.corda.core.crypto.DigestService
|
||||||
import net.corda.core.crypto.SecureHash
|
import net.corda.core.crypto.SecureHash
|
||||||
import net.corda.core.identity.Party
|
|
||||||
import net.corda.core.internal.AbstractAttachment
|
|
||||||
import net.corda.core.internal.HashAgility
|
import net.corda.core.internal.HashAgility
|
||||||
import net.corda.core.internal.PLATFORM_VERSION
|
import net.corda.core.internal.PLATFORM_VERSION
|
||||||
import net.corda.core.internal.digestService
|
import net.corda.core.internal.digestService
|
||||||
import net.corda.core.node.ServicesForResolution
|
|
||||||
import net.corda.core.node.ZoneVersionTooLowException
|
import net.corda.core.node.ZoneVersionTooLowException
|
||||||
import net.corda.core.node.services.AttachmentStorage
|
import net.corda.core.serialization.internal._driverSerializationEnv
|
||||||
import net.corda.core.node.services.IdentityService
|
|
||||||
import net.corda.core.node.services.NetworkParametersService
|
|
||||||
import net.corda.core.serialization.serialize
|
|
||||||
import net.corda.core.transactions.TransactionBuilder
|
import net.corda.core.transactions.TransactionBuilder
|
||||||
import net.corda.coretesting.internal.rigorousMock
|
|
||||||
import net.corda.testing.common.internal.testNetworkParameters
|
import net.corda.testing.common.internal.testNetworkParameters
|
||||||
import net.corda.testing.contracts.DummyContract
|
import net.corda.testing.contracts.DummyContract
|
||||||
import net.corda.testing.contracts.DummyState
|
import net.corda.testing.contracts.DummyState
|
||||||
import net.corda.testing.core.ALICE_NAME
|
import net.corda.testing.core.ALICE_NAME
|
||||||
import net.corda.testing.core.BOB_NAME
|
|
||||||
import net.corda.testing.core.DUMMY_NOTARY_NAME
|
import net.corda.testing.core.DUMMY_NOTARY_NAME
|
||||||
import net.corda.testing.core.DummyCommandData
|
import net.corda.testing.core.DummyCommandData
|
||||||
import net.corda.testing.core.SerializationEnvironmentRule
|
import net.corda.testing.core.SerializationEnvironmentRule
|
||||||
import net.corda.testing.core.TestIdentity
|
import net.corda.testing.core.TestIdentity
|
||||||
|
import net.corda.testing.node.MockNetwork
|
||||||
|
import net.corda.testing.node.MockNetworkParameters
|
||||||
|
import net.corda.testing.node.MockServices
|
||||||
|
import net.corda.testing.node.internal.cordappWithPackages
|
||||||
import org.assertj.core.api.Assertions.assertThat
|
import org.assertj.core.api.Assertions.assertThat
|
||||||
|
import org.assertj.core.api.Assertions.assertThatExceptionOfType
|
||||||
import org.assertj.core.api.Assertions.assertThatThrownBy
|
import org.assertj.core.api.Assertions.assertThatThrownBy
|
||||||
import org.junit.Assert.assertFalse
|
|
||||||
import org.junit.Assert.assertTrue
|
import org.junit.Assert.assertTrue
|
||||||
import org.junit.Before
|
|
||||||
import org.junit.Ignore
|
import org.junit.Ignore
|
||||||
import org.junit.Rule
|
import org.junit.Rule
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import java.security.PublicKey
|
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
import kotlin.test.assertFailsWith
|
import kotlin.test.assertFailsWith
|
||||||
|
|
||||||
@ -57,33 +45,12 @@ class TransactionBuilderTest {
|
|||||||
val testSerialization = SerializationEnvironmentRule()
|
val testSerialization = SerializationEnvironmentRule()
|
||||||
|
|
||||||
private val notary = TestIdentity(DUMMY_NOTARY_NAME).party
|
private val notary = TestIdentity(DUMMY_NOTARY_NAME).party
|
||||||
private val services = rigorousMock<ServicesForResolution>()
|
private val services = MockServices(
|
||||||
private val contractAttachmentId = SecureHash.randomSHA256()
|
listOf("net.corda.testing.contracts"),
|
||||||
private val attachments = rigorousMock<AttachmentStorage>()
|
TestIdentity(ALICE_NAME),
|
||||||
private val networkParametersService = mock<NetworkParametersService>()
|
testNetworkParameters(minimumPlatformVersion = PLATFORM_VERSION)
|
||||||
|
)
|
||||||
@Before
|
private val contractAttachmentId = services.attachments.getLatestContractAttachments(DummyContract.PROGRAM_ID)[0]
|
||||||
fun setup() {
|
|
||||||
val cordappProvider = rigorousMock<CordappProvider>()
|
|
||||||
val networkParameters = testNetworkParameters(minimumPlatformVersion = PLATFORM_VERSION)
|
|
||||||
doReturn(networkParametersService).whenever(services).networkParametersService
|
|
||||||
doReturn(networkParameters.serialize().hash).whenever(networkParametersService).currentHash
|
|
||||||
doReturn(cordappProvider).whenever(services).cordappProvider
|
|
||||||
doReturn(contractAttachmentId).whenever(cordappProvider).getContractAttachmentID(DummyContract.PROGRAM_ID)
|
|
||||||
doReturn(networkParameters).whenever(services).networkParameters
|
|
||||||
doReturn(mock<IdentityService>()).whenever(services).identityService
|
|
||||||
|
|
||||||
val attachmentStorage = rigorousMock<AttachmentStorage>()
|
|
||||||
doReturn(attachmentStorage).whenever(services).attachments
|
|
||||||
val attachment = rigorousMock<ContractAttachment>()
|
|
||||||
doReturn(attachment).whenever(attachmentStorage).openAttachment(contractAttachmentId)
|
|
||||||
doReturn(contractAttachmentId).whenever(attachment).id
|
|
||||||
doReturn(setOf(DummyContract.PROGRAM_ID)).whenever(attachment).allContracts
|
|
||||||
doReturn("app").whenever(attachment).uploader
|
|
||||||
doReturn(emptyList<Party>()).whenever(attachment).signerKeys
|
|
||||||
doReturn(listOf(contractAttachmentId)).whenever(attachmentStorage)
|
|
||||||
.getLatestContractAttachments("net.corda.testing.contracts.DummyContract")
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test(timeout=300_000)
|
@Test(timeout=300_000)
|
||||||
fun `bare minimum issuance tx`() {
|
fun `bare minimum issuance tx`() {
|
||||||
@ -99,13 +66,11 @@ class TransactionBuilderTest {
|
|||||||
val wtx = builder.toWireTransaction(services)
|
val wtx = builder.toWireTransaction(services)
|
||||||
assertThat(wtx.outputs).containsOnly(outputState)
|
assertThat(wtx.outputs).containsOnly(outputState)
|
||||||
assertThat(wtx.commands).containsOnly(Command(DummyCommandData, notary.owningKey))
|
assertThat(wtx.commands).containsOnly(Command(DummyCommandData, notary.owningKey))
|
||||||
assertThat(wtx.networkParametersHash).isEqualTo(networkParametersService.currentHash)
|
assertThat(wtx.networkParametersHash).isEqualTo(services.networkParametersService.currentHash)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(timeout=300_000)
|
@Test(timeout=300_000)
|
||||||
fun `automatic hash constraint`() {
|
fun `automatic hash constraint`() {
|
||||||
doReturn(unsignedAttachment).whenever(attachments).openAttachment(contractAttachmentId)
|
|
||||||
|
|
||||||
val outputState = TransactionState(data = DummyState(), contract = DummyContract.PROGRAM_ID, notary = notary)
|
val outputState = TransactionState(data = DummyState(), contract = DummyContract.PROGRAM_ID, notary = notary)
|
||||||
val builder = TransactionBuilder()
|
val builder = TransactionBuilder()
|
||||||
.addOutputState(outputState)
|
.addOutputState(outputState)
|
||||||
@ -116,8 +81,6 @@ class TransactionBuilderTest {
|
|||||||
|
|
||||||
@Test(timeout=300_000)
|
@Test(timeout=300_000)
|
||||||
fun `reference states`() {
|
fun `reference states`() {
|
||||||
doReturn(unsignedAttachment).whenever(attachments).openAttachment(contractAttachmentId)
|
|
||||||
|
|
||||||
val referenceState = TransactionState(DummyState(), DummyContract.PROGRAM_ID, notary)
|
val referenceState = TransactionState(DummyState(), DummyContract.PROGRAM_ID, notary)
|
||||||
val referenceStateRef = StateRef(SecureHash.randomSHA256(), 1)
|
val referenceStateRef = StateRef(SecureHash.randomSHA256(), 1)
|
||||||
val builder = TransactionBuilder(notary)
|
val builder = TransactionBuilder(notary)
|
||||||
@ -125,32 +88,41 @@ class TransactionBuilderTest {
|
|||||||
.addOutputState(TransactionState(DummyState(), DummyContract.PROGRAM_ID, notary))
|
.addOutputState(TransactionState(DummyState(), DummyContract.PROGRAM_ID, notary))
|
||||||
.addCommand(DummyCommandData, notary.owningKey)
|
.addCommand(DummyCommandData, notary.owningKey)
|
||||||
|
|
||||||
doReturn(testNetworkParameters(minimumPlatformVersion = 3)).whenever(services).networkParameters
|
with(testNetworkParameters(minimumPlatformVersion = 3)) {
|
||||||
|
val services = MockServices(listOf("net.corda.testing.contracts"), TestIdentity(ALICE_NAME), this)
|
||||||
assertThatThrownBy { builder.toWireTransaction(services) }
|
assertThatThrownBy { builder.toWireTransaction(services) }
|
||||||
.isInstanceOf(ZoneVersionTooLowException::class.java)
|
.isInstanceOf(ZoneVersionTooLowException::class.java)
|
||||||
.hasMessageContaining("Reference states")
|
.hasMessageContaining("Reference states")
|
||||||
|
}
|
||||||
|
|
||||||
doReturn(testNetworkParameters(minimumPlatformVersion = 4)).whenever(services).networkParameters
|
with(testNetworkParameters(minimumPlatformVersion = 4)) {
|
||||||
doReturn(referenceState).whenever(services).loadState(referenceStateRef)
|
val services = MockServices(listOf("net.corda.testing.contracts"), TestIdentity(ALICE_NAME), this)
|
||||||
val wtx = builder.toWireTransaction(services)
|
val wtx = builder.toWireTransaction(services)
|
||||||
assertThat(wtx.references).containsOnly(referenceStateRef)
|
assertThat(wtx.references).containsOnly(referenceStateRef)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test(timeout=300_000)
|
@Test(timeout=300_000)
|
||||||
fun `automatic signature constraint`() {
|
fun `automatic signature constraint`() {
|
||||||
val aliceParty = TestIdentity(ALICE_NAME).party
|
// We need to use a MockNetwork so that we can create a signed attachment. However, SerializationEnvironmentRule and MockNetwork
|
||||||
val bobParty = TestIdentity(BOB_NAME).party
|
// don't work well together, so we temporarily clear out the driverSerializationEnv for this test.
|
||||||
val compositeKey = CompositeKey.Builder().addKeys(aliceParty.owningKey, bobParty.owningKey).build()
|
val driverSerializationEnv = _driverSerializationEnv.get()
|
||||||
val expectedConstraint = SignatureAttachmentConstraint(compositeKey)
|
_driverSerializationEnv.set(null)
|
||||||
val signedAttachment = signedAttachment(aliceParty, bobParty)
|
val mockNetwork = MockNetwork(
|
||||||
|
MockNetworkParameters(
|
||||||
|
networkParameters = testNetworkParameters(minimumPlatformVersion = PLATFORM_VERSION),
|
||||||
|
cordappsForAllNodes = listOf(cordappWithPackages("net.corda.testing.contracts").signed())
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
assertTrue(expectedConstraint.isSatisfiedBy(signedAttachment))
|
try {
|
||||||
assertFalse(expectedConstraint.isSatisfiedBy(unsignedAttachment))
|
val services = mockNetwork.notaryNodes[0].services
|
||||||
|
|
||||||
doReturn(attachments).whenever(services).attachments
|
val attachment = services.attachments.openAttachment(services.attachments.getLatestContractAttachments(DummyContract.PROGRAM_ID)[0])
|
||||||
doReturn(signedAttachment).whenever(attachments).openAttachment(contractAttachmentId)
|
val attachmentSigner = attachment!!.signerKeys.single()
|
||||||
doReturn(listOf(contractAttachmentId)).whenever(attachments)
|
|
||||||
.getLatestContractAttachments("net.corda.testing.contracts.DummyContract")
|
val expectedConstraint = SignatureAttachmentConstraint(attachmentSigner)
|
||||||
|
assertTrue(expectedConstraint.isSatisfiedBy(attachment))
|
||||||
|
|
||||||
val outputState = TransactionState(data = DummyState(), contract = DummyContract.PROGRAM_ID, notary = notary)
|
val outputState = TransactionState(data = DummyState(), contract = DummyContract.PROGRAM_ID, notary = notary)
|
||||||
val builder = TransactionBuilder()
|
val builder = TransactionBuilder()
|
||||||
@ -159,19 +131,11 @@ class TransactionBuilderTest {
|
|||||||
val wtx = builder.toWireTransaction(services)
|
val wtx = builder.toWireTransaction(services)
|
||||||
|
|
||||||
assertThat(wtx.outputs).containsOnly(outputState.copy(constraint = expectedConstraint))
|
assertThat(wtx.outputs).containsOnly(outputState.copy(constraint = expectedConstraint))
|
||||||
|
} finally {
|
||||||
|
mockNetwork.stopNodes()
|
||||||
|
_driverSerializationEnv.set(driverSerializationEnv)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private val unsignedAttachment = ContractAttachment(object : AbstractAttachment({ byteArrayOf() }, "test") {
|
|
||||||
override val id: SecureHash get() = throw UnsupportedOperationException()
|
|
||||||
|
|
||||||
override val signerKeys: List<PublicKey> get() = emptyList()
|
|
||||||
}, DummyContract.PROGRAM_ID)
|
|
||||||
|
|
||||||
private fun signedAttachment(vararg parties: Party) = ContractAttachment.create(object : AbstractAttachment({ byteArrayOf() }, "test") {
|
|
||||||
override val id: SecureHash get() = contractAttachmentId
|
|
||||||
|
|
||||||
override val signerKeys: List<PublicKey> get() = parties.map { it.owningKey }
|
|
||||||
}, DummyContract.PROGRAM_ID, signerKeys = parties.map { it.owningKey })
|
|
||||||
|
|
||||||
@Test(timeout=300_000)
|
@Test(timeout=300_000)
|
||||||
fun `list accessors are mutable copies`() {
|
fun `list accessors are mutable copies`() {
|
||||||
@ -270,7 +234,7 @@ class TransactionBuilderTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Ignore
|
@Ignore
|
||||||
@Test(timeout=300_000, expected = TransactionVerificationException.UnsupportedHashTypeException::class)
|
@Test(timeout=300_000)
|
||||||
fun `throws with non-default hash algorithm`() {
|
fun `throws with non-default hash algorithm`() {
|
||||||
HashAgility.init()
|
HashAgility.init()
|
||||||
try {
|
try {
|
||||||
@ -286,13 +250,15 @@ class TransactionBuilderTest {
|
|||||||
.addOutputState(outputState)
|
.addOutputState(outputState)
|
||||||
.addCommand(DummyCommandData, notary.owningKey)
|
.addCommand(DummyCommandData, notary.owningKey)
|
||||||
|
|
||||||
|
assertThatExceptionOfType(UnsupportedHashTypeException::class.java).isThrownBy {
|
||||||
builder.toWireTransaction(services)
|
builder.toWireTransaction(services)
|
||||||
|
}
|
||||||
} finally {
|
} finally {
|
||||||
HashAgility.init()
|
HashAgility.init()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(timeout=300_000, expected = Test.None::class)
|
@Test(timeout=300_000)
|
||||||
fun `allows non-default hash algorithm`() {
|
fun `allows non-default hash algorithm`() {
|
||||||
HashAgility.init(txHashAlgoName = DigestService.sha2_384.hashAlgorithm)
|
HashAgility.init(txHashAlgoName = DigestService.sha2_384.hashAlgorithm)
|
||||||
assertThat(services.digestService).isEqualTo(DigestService.sha2_384)
|
assertThat(services.digestService).isEqualTo(DigestService.sha2_384)
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
package net.corda.coretests.transactions
|
package net.corda.coretests.transactions
|
||||||
|
|
||||||
import com.nhaarman.mockito_kotlin.doReturn
|
import org.mockito.kotlin.doReturn
|
||||||
import com.nhaarman.mockito_kotlin.mock
|
import org.mockito.kotlin.mock
|
||||||
import com.nhaarman.mockito_kotlin.whenever
|
import org.mockito.kotlin.whenever
|
||||||
import net.corda.core.contracts.*
|
import net.corda.core.contracts.*
|
||||||
import net.corda.core.identity.AbstractParty
|
import net.corda.core.identity.AbstractParty
|
||||||
import net.corda.core.identity.CordaX500Name
|
import net.corda.core.identity.CordaX500Name
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package net.corda.coretests.transactions
|
package net.corda.coretests.transactions
|
||||||
|
|
||||||
import com.nhaarman.mockito_kotlin.doReturn
|
import org.mockito.kotlin.doReturn
|
||||||
import com.nhaarman.mockito_kotlin.whenever
|
import org.mockito.kotlin.whenever
|
||||||
import net.corda.core.contracts.*
|
import net.corda.core.contracts.*
|
||||||
import net.corda.core.crypto.*
|
import net.corda.core.crypto.*
|
||||||
import net.corda.core.crypto.CompositeKey
|
import net.corda.core.crypto.CompositeKey
|
||||||
|
@ -1,22 +1,17 @@
|
|||||||
import static org.gradle.api.JavaVersion.VERSION_1_8
|
apply plugin: 'org.jetbrains.kotlin.jvm'
|
||||||
|
apply plugin: 'org.jetbrains.kotlin.plugin.jpa'
|
||||||
apply plugin: 'kotlin'
|
|
||||||
apply plugin: 'kotlin-jpa'
|
|
||||||
apply plugin: 'net.corda.plugins.quasar-utils'
|
apply plugin: 'net.corda.plugins.quasar-utils'
|
||||||
apply plugin: 'net.corda.plugins.publish-utils'
|
|
||||||
apply plugin: 'net.corda.plugins.api-scanner'
|
apply plugin: 'net.corda.plugins.api-scanner'
|
||||||
apply plugin: 'com.jfrog.artifactory'
|
apply plugin: 'corda.common-publishing'
|
||||||
|
|
||||||
description 'Corda core'
|
description 'Corda core'
|
||||||
|
|
||||||
targetCompatibility = VERSION_1_8
|
|
||||||
|
|
||||||
sourceSets {
|
sourceSets {
|
||||||
obfuscator
|
obfuscator
|
||||||
}
|
}
|
||||||
|
|
||||||
configurations {
|
configurations {
|
||||||
integrationTestCompile.extendsFrom testCompile
|
integrationTestImplementation.extendsFrom testImplementation
|
||||||
integrationTestRuntimeOnly.extendsFrom testRuntimeOnly
|
integrationTestRuntimeOnly.extendsFrom testRuntimeOnly
|
||||||
|
|
||||||
smokeTestCompile.extendsFrom compile
|
smokeTestCompile.extendsFrom compile
|
||||||
@ -24,10 +19,9 @@ configurations {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
implementation "io.opentelemetry:opentelemetry-api:${open_telemetry_version}"
|
||||||
obfuscatorImplementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
|
|
||||||
compileOnly "io.opentelemetry:opentelemetry-api:${open_telemetry_version}"
|
|
||||||
compileOnly project(':opentelemetry')
|
compileOnly project(':opentelemetry')
|
||||||
|
|
||||||
testImplementation sourceSets.obfuscator.output
|
testImplementation sourceSets.obfuscator.output
|
||||||
testImplementation "org.junit.jupiter:junit-jupiter-api:${junit_jupiter_version}"
|
testImplementation "org.junit.jupiter:junit-jupiter-api:${junit_jupiter_version}"
|
||||||
testImplementation "junit:junit:$junit_version"
|
testImplementation "junit:junit:$junit_version"
|
||||||
@ -35,72 +29,65 @@ dependencies {
|
|||||||
testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:${junit_jupiter_version}"
|
testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:${junit_jupiter_version}"
|
||||||
testRuntimeOnly "org.junit.platform:junit-platform-launcher:${junit_platform_version}"
|
testRuntimeOnly "org.junit.platform:junit-platform-launcher:${junit_platform_version}"
|
||||||
|
|
||||||
testCompile "commons-fileupload:commons-fileupload:$fileupload_version"
|
testImplementation "commons-fileupload:commons-fileupload:$fileupload_version"
|
||||||
|
|
||||||
// Guava: Google test library (collections test suite)
|
// Guava: Google test library (collections test suite)
|
||||||
testCompile "com.google.guava:guava-testlib:$guava_version"
|
testImplementation "com.google.guava:guava-testlib:$guava_version"
|
||||||
|
|
||||||
compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
|
implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
|
||||||
compile "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
|
testImplementation "org.jetbrains.kotlin:kotlin-test:$kotlin_version"
|
||||||
testCompile "org.jetbrains.kotlin:kotlin-test:$kotlin_version"
|
|
||||||
|
|
||||||
// Hamkrest, for fluent, composable matchers
|
// Hamkrest, for fluent, composable matchers
|
||||||
testCompile "com.natpryce:hamkrest:$hamkrest_version"
|
testImplementation "com.natpryce:hamkrest:$hamkrest_version"
|
||||||
|
|
||||||
// Thread safety annotations
|
|
||||||
compile "com.google.code.findbugs:jsr305:$jsr305_version"
|
|
||||||
|
|
||||||
// SLF4J: commons-logging bindings for a SLF4J back end
|
// SLF4J: commons-logging bindings for a SLF4J back end
|
||||||
compile "org.slf4j:jcl-over-slf4j:$slf4j_version"
|
implementation "org.slf4j:jcl-over-slf4j:$slf4j_version"
|
||||||
compile "org.slf4j:slf4j-api:$slf4j_version"
|
implementation "org.slf4j:slf4j-api:$slf4j_version"
|
||||||
|
|
||||||
// AssertJ: for fluent assertions for testing
|
// AssertJ: for fluent assertions for testing
|
||||||
testCompile "org.assertj:assertj-core:${assertj_version}"
|
testImplementation "org.assertj:assertj-core:${assertj_version}"
|
||||||
|
|
||||||
// Guava: Google utilities library.
|
// Guava: Google utilities library.
|
||||||
compile "com.google.guava:guava:$guava_version"
|
implementation "com.google.guava:guava:$guava_version"
|
||||||
|
|
||||||
// For caches rather than guava
|
// For caches rather than guava
|
||||||
compile "com.github.ben-manes.caffeine:caffeine:$caffeine_version"
|
implementation "com.github.ben-manes.caffeine:caffeine:$caffeine_version"
|
||||||
|
|
||||||
// RxJava: observable streams of events.
|
// RxJava: observable streams of events.
|
||||||
compile "io.reactivex:rxjava:$rxjava_version"
|
implementation "io.reactivex:rxjava:$rxjava_version"
|
||||||
|
|
||||||
compile "org.apache.commons:commons-lang3:$commons_lang_version"
|
implementation "org.apache.commons:commons-lang3:$commons_lang3_version"
|
||||||
|
|
||||||
// Java ed25519 implementation. See https://github.com/str4d/ed25519-java/
|
// Java ed25519 implementation. See https://github.com/str4d/ed25519-java/
|
||||||
compile "net.i2p.crypto:eddsa:$eddsa_version"
|
implementation "net.i2p.crypto:eddsa:$eddsa_version"
|
||||||
|
|
||||||
// Bouncy castle support needed for X509 certificate manipulation
|
// Bouncy castle support needed for X509 certificate manipulation
|
||||||
compile "org.bouncycastle:bcprov-jdk18on:${bouncycastle_version}"
|
implementation "org.bouncycastle:bcprov-jdk18on:${bouncycastle_version}"
|
||||||
compile "org.bouncycastle:bcpkix-jdk18on:${bouncycastle_version}"
|
testImplementation "org.bouncycastle:bcpkix-jdk18on:${bouncycastle_version}"
|
||||||
|
|
||||||
// JPA 2.2 annotations.
|
|
||||||
compile "javax.persistence:javax.persistence-api:2.2"
|
|
||||||
|
|
||||||
// required to use @Type annotation
|
// required to use @Type annotation
|
||||||
compile "org.hibernate:hibernate-core:$hibernate_version"
|
implementation "org.hibernate:hibernate-core:$hibernate_version"
|
||||||
|
|
||||||
// FastThreadLocal
|
// FastThreadLocal
|
||||||
compile "io.netty:netty-common:$netty_version"
|
implementation "io.netty:netty-common:$netty_version"
|
||||||
|
|
||||||
compile group: "io.github.classgraph", name: "classgraph", version: class_graph_version
|
implementation group: "io.github.classgraph", name: "classgraph", version: class_graph_version
|
||||||
|
|
||||||
testCompile "org.ow2.asm:asm:$asm_version"
|
testImplementation "org.ow2.asm:asm:$asm_version"
|
||||||
|
|
||||||
// JDK11: required by Quasar at run-time
|
// JDK11: required by Quasar at run-time
|
||||||
testRuntimeOnly "com.esotericsoftware:kryo:$kryo_version"
|
testRuntimeOnly "com.esotericsoftware:kryo:$kryo_version"
|
||||||
|
testRuntimeOnly "org.slf4j:slf4j-simple:$slf4j_version"
|
||||||
|
|
||||||
testCompile "com.nhaarman:mockito-kotlin:$mockito_kotlin_version"
|
testImplementation "org.mockito.kotlin:mockito-kotlin:$mockito_kotlin_version"
|
||||||
testCompile "org.mockito:mockito-core:$mockito_version"
|
testImplementation "org.mockito:mockito-core:$mockito_version"
|
||||||
testCompile "org.assertj:assertj-core:$assertj_version"
|
testImplementation "org.assertj:assertj-core:$assertj_version"
|
||||||
testCompile "com.natpryce:hamkrest:$hamkrest_version"
|
testImplementation "com.natpryce:hamkrest:$hamkrest_version"
|
||||||
testCompile 'org.hamcrest:hamcrest-library:2.1'
|
testImplementation 'org.hamcrest:hamcrest-library:2.1'
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO Consider moving it to quasar-utils in the future (introduced with PR-1388)
|
// TODO Consider moving it to quasar-utils in the future (introduced with PR-1388)
|
||||||
task copyQuasarJar(type: Copy) {
|
tasks.register('copyQuasarJar', Copy) {
|
||||||
from configurations.quasar
|
from configurations.quasar
|
||||||
into "$project.rootProject.projectDir/lib"
|
into "$project.rootProject.projectDir/lib"
|
||||||
rename { filename -> "quasar.jar" }
|
rename { filename -> "quasar.jar" }
|
||||||
@ -110,13 +97,16 @@ jar {
|
|||||||
finalizedBy(copyQuasarJar)
|
finalizedBy(copyQuasarJar)
|
||||||
archiveBaseName = 'corda-core'
|
archiveBaseName = 'corda-core'
|
||||||
archiveClassifier = ''
|
archiveClassifier = ''
|
||||||
|
|
||||||
|
manifest {
|
||||||
|
attributes('Add-Opens': 'java.base/java.net java.base/java.nio')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
configurations {
|
configurations {
|
||||||
testArtifacts.extendsFrom testRuntimeClasspath
|
testArtifacts.extendsFrom testRuntimeClasspath
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
processTestResources {
|
processTestResources {
|
||||||
inputs.files(jar)
|
inputs.files(jar)
|
||||||
into("zip") {
|
into("zip") {
|
||||||
@ -126,11 +116,23 @@ processTestResources {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
compileTestJava {
|
||||||
|
options.compilerArgs += [
|
||||||
|
'--add-exports', 'java.base/sun.security.util=ALL-UNNAMED',
|
||||||
|
'--add-exports', 'java.base/sun.security.x509=ALL-UNNAMED'
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
test {
|
test {
|
||||||
|
jvmArgs += [
|
||||||
|
'--add-exports', 'java.base/sun.security.util=ALL-UNNAMED',
|
||||||
|
'--add-exports', 'java.base/sun.security.x509=ALL-UNNAMED'
|
||||||
|
]
|
||||||
maxParallelForks = (System.env.CORDA_CORE_TESTING_FORKS == null) ? 1 : "$System.env.CORDA_CORE_TESTING_FORKS".toInteger()
|
maxParallelForks = (System.env.CORDA_CORE_TESTING_FORKS == null) ? 1 : "$System.env.CORDA_CORE_TESTING_FORKS".toInteger()
|
||||||
}
|
}
|
||||||
|
|
||||||
task testJar(type: Jar) {
|
tasks.register('testJar', Jar) {
|
||||||
|
dependsOn testClasses
|
||||||
classifier "tests"
|
classifier "tests"
|
||||||
from sourceSets.test.output
|
from sourceSets.test.output
|
||||||
}
|
}
|
||||||
@ -165,11 +167,6 @@ quasar {
|
|||||||
"io.opentelemetry.**")
|
"io.opentelemetry.**")
|
||||||
}
|
}
|
||||||
|
|
||||||
artifacts {
|
|
||||||
testArtifacts testJar
|
|
||||||
publish testJar
|
|
||||||
}
|
|
||||||
|
|
||||||
scanApi {
|
scanApi {
|
||||||
excludeClasses = [
|
excludeClasses = [
|
||||||
// Kotlin should probably have declared this class as "synthetic".
|
// Kotlin should probably have declared this class as "synthetic".
|
||||||
@ -177,13 +174,23 @@ scanApi {
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
publish {
|
|
||||||
name jar.baseName
|
|
||||||
}
|
|
||||||
|
|
||||||
tasks.register("writeTestResources", JavaExec) {
|
tasks.register("writeTestResources", JavaExec) {
|
||||||
classpath sourceSets.obfuscator.output
|
classpath sourceSets.obfuscator.output
|
||||||
classpath sourceSets.obfuscator.runtimeClasspath
|
classpath sourceSets.obfuscator.runtimeClasspath
|
||||||
main 'net.corda.core.internal.utilities.TestResourceWriter'
|
main 'net.corda.core.internal.utilities.TestResourceWriter'
|
||||||
args new File(sourceSets.test.resources.srcDirs.first(), "zip").toString()
|
args new File(sourceSets.test.resources.srcDirs.first(), "zip").toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
artifacts {
|
||||||
|
testArtifacts testJar
|
||||||
|
}
|
||||||
|
|
||||||
|
publishing {
|
||||||
|
publications {
|
||||||
|
maven(MavenPublication) {
|
||||||
|
artifactId 'corda-core'
|
||||||
|
artifact testJar
|
||||||
|
from components.java
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
@file:JvmName("ConcurrencyUtils")
|
@file:JvmName("ConcurrencyUtils")
|
||||||
package net.corda.core.concurrent
|
package net.corda.core.concurrent
|
||||||
|
|
||||||
import net.corda.core.internal.VisibleForTesting
|
import net.corda.core.CordaInternal
|
||||||
import net.corda.core.internal.concurrent.openFuture
|
import net.corda.core.internal.concurrent.openFuture
|
||||||
import net.corda.core.utilities.getOrThrow
|
import net.corda.core.utilities.getOrThrow
|
||||||
import org.slf4j.Logger
|
import org.slf4j.Logger
|
||||||
@ -27,9 +27,8 @@ fun <V, W> Future<V>.match(success: (V) -> W, failure: (Throwable) -> W): W {
|
|||||||
fun <V, W> firstOf(vararg futures: CordaFuture<out V>, handler: (CordaFuture<out V>) -> W) = firstOf(futures, defaultLog, handler)
|
fun <V, W> firstOf(vararg futures: CordaFuture<out V>, handler: (CordaFuture<out V>) -> W) = firstOf(futures, defaultLog, handler)
|
||||||
|
|
||||||
private val defaultLog = LoggerFactory.getLogger("net.corda.core.concurrent")
|
private val defaultLog = LoggerFactory.getLogger("net.corda.core.concurrent")
|
||||||
@VisibleForTesting
|
|
||||||
internal const val shortCircuitedTaskFailedMessage = "Short-circuited task failed:"
|
|
||||||
|
|
||||||
|
@CordaInternal
|
||||||
internal fun <V, W> firstOf(futures: Array<out CordaFuture<out V>>, log: Logger, handler: (CordaFuture<out V>) -> W): CordaFuture<W> {
|
internal fun <V, W> firstOf(futures: Array<out CordaFuture<out V>>, log: Logger, handler: (CordaFuture<out V>) -> W): CordaFuture<W> {
|
||||||
val resultFuture = openFuture<W>()
|
val resultFuture = openFuture<W>()
|
||||||
val winnerChosen = AtomicBoolean()
|
val winnerChosen = AtomicBoolean()
|
||||||
@ -40,7 +39,7 @@ internal fun <V, W> firstOf(futures: Array<out CordaFuture<out V>>, log: Logger,
|
|||||||
it.isCancelled -> {
|
it.isCancelled -> {
|
||||||
// Do nothing.
|
// Do nothing.
|
||||||
}
|
}
|
||||||
else -> it.match({}, { log.error(shortCircuitedTaskFailedMessage, it) })
|
else -> it.match({}, { log.error("Short-circuited task failed:", it) })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -35,7 +35,6 @@ import java.util.jar.JarInputStream
|
|||||||
interface Attachment : NamedByHash {
|
interface Attachment : NamedByHash {
|
||||||
fun open(): InputStream
|
fun open(): InputStream
|
||||||
|
|
||||||
@JvmDefault
|
|
||||||
fun openAsJAR(): JarInputStream {
|
fun openAsJAR(): JarInputStream {
|
||||||
val stream = open()
|
val stream = open()
|
||||||
return try {
|
return try {
|
||||||
@ -49,7 +48,6 @@ interface Attachment : NamedByHash {
|
|||||||
* Finds the named file case insensitively and copies it to the output stream.
|
* Finds the named file case insensitively and copies it to the output stream.
|
||||||
* @throws [FileNotFoundException] if the given path doesn't exist in the attachment.
|
* @throws [FileNotFoundException] if the given path doesn't exist in the attachment.
|
||||||
*/
|
*/
|
||||||
@JvmDefault
|
|
||||||
fun extractFile(path: String, outputTo: OutputStream) = openAsJAR().use { it.extractFile(path, outputTo) }
|
fun extractFile(path: String, outputTo: OutputStream) = openAsJAR().use { it.extractFile(path, outputTo) }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -27,6 +27,7 @@ class ContractAttachment private constructor(
|
|||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
@CordaInternal
|
@CordaInternal
|
||||||
|
@JvmSynthetic
|
||||||
fun create(attachment: Attachment,
|
fun create(attachment: Attachment,
|
||||||
contract: ContractClassName,
|
contract: ContractClassName,
|
||||||
additionalContracts: Set<ContractClassName> = emptySet(),
|
additionalContracts: Set<ContractClassName> = emptySet(),
|
||||||
|
@ -24,6 +24,11 @@ class CordaSecurityProvider : Provider(PROVIDER_NAME, 0.1, "$PROVIDER_NAME secur
|
|||||||
put("Alg.Alias.Signature.$COMPOSITE_SIGNATURE", CompositeSignature.SIGNATURE_ALGORITHM)
|
put("Alg.Alias.Signature.$COMPOSITE_SIGNATURE", CompositeSignature.SIGNATURE_ALGORITHM)
|
||||||
put("Alg.Alias.Signature.OID.$COMPOSITE_SIGNATURE", CompositeSignature.SIGNATURE_ALGORITHM)
|
put("Alg.Alias.Signature.OID.$COMPOSITE_SIGNATURE", CompositeSignature.SIGNATURE_ALGORITHM)
|
||||||
putPlatformSecureRandomService()
|
putPlatformSecureRandomService()
|
||||||
|
|
||||||
|
// JDK11+ - Hack to set Provider#legacyChanged to false, without this SecureRandom will not
|
||||||
|
// pickup our random implementation (even if our provider is the first provider in
|
||||||
|
// the chain).
|
||||||
|
super.getService("UNDEFINED", "UNDEFINED")
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun putPlatformSecureRandomService() {
|
private fun putPlatformSecureRandomService() {
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
package net.corda.core.crypto.internal
|
package net.corda.core.crypto.internal
|
||||||
|
|
||||||
import net.corda.core.crypto.DigestAlgorithm
|
import net.corda.core.crypto.DigestAlgorithm
|
||||||
import java.lang.reflect.Constructor
|
import net.corda.core.internal.loadClassOfType
|
||||||
import java.security.MessageDigest
|
import java.security.MessageDigest
|
||||||
import java.security.NoSuchAlgorithmException
|
import java.security.NoSuchAlgorithmException
|
||||||
import java.util.*
|
import java.util.Collections
|
||||||
import java.util.concurrent.ConcurrentHashMap
|
import java.util.concurrent.ConcurrentHashMap
|
||||||
|
|
||||||
sealed class DigestAlgorithmFactory {
|
sealed class DigestAlgorithmFactory {
|
||||||
@ -28,9 +28,8 @@ sealed class DigestAlgorithmFactory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private class CustomAlgorithmFactory(className: String) : DigestAlgorithmFactory() {
|
private class CustomAlgorithmFactory(className: String) : DigestAlgorithmFactory() {
|
||||||
val constructor: Constructor<out DigestAlgorithm> = Class.forName(className, false, javaClass.classLoader)
|
private val constructor = loadClassOfType<DigestAlgorithm>(className, false, javaClass.classLoader).getConstructor()
|
||||||
.asSubclass(DigestAlgorithm::class.java)
|
|
||||||
.getConstructor()
|
|
||||||
override val algorithm: String = constructor.newInstance().algorithm
|
override val algorithm: String = constructor.newInstance().algorithm
|
||||||
|
|
||||||
override fun create(): DigestAlgorithm {
|
override fun create(): DigestAlgorithm {
|
||||||
|
@ -14,12 +14,13 @@ import java.io.InputStream
|
|||||||
import java.security.Provider
|
import java.security.Provider
|
||||||
import java.security.SecureRandom
|
import java.security.SecureRandom
|
||||||
import java.security.SecureRandomSpi
|
import java.security.SecureRandomSpi
|
||||||
|
import kotlin.system.exitProcess
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This has been migrated into a separate class so that it
|
* This has been migrated into a separate class so that it
|
||||||
* is easier to delete from the core-deterministic module.
|
* is easier to delete from the core-deterministic module.
|
||||||
*/
|
*/
|
||||||
internal val platformSecureRandom: () -> SecureRandom = when {
|
val platformSecureRandom: () -> SecureRandom = when {
|
||||||
SgxSupport.isInsideEnclave -> {
|
SgxSupport.isInsideEnclave -> {
|
||||||
{ DummySecureRandom }
|
{ DummySecureRandom }
|
||||||
}
|
}
|
||||||
@ -29,23 +30,22 @@ internal val platformSecureRandom: () -> SecureRandom = when {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class PlatformSecureRandomService(provider: Provider)
|
class PlatformSecureRandomService(provider: Provider)
|
||||||
: Provider.Service(provider, "SecureRandom", algorithm, PlatformSecureRandomSpi::javaClass.name, null, null) {
|
: Provider.Service(provider, "SecureRandom", ALGORITHM, PlatformSecureRandomSpi::javaClass.name, null, null) {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val algorithm = "CordaPRNG"
|
const val ALGORITHM = "CordaPRNG"
|
||||||
|
|
||||||
private val logger = loggerFor<PlatformSecureRandomService>()
|
private val logger = loggerFor<PlatformSecureRandomService>()
|
||||||
}
|
}
|
||||||
|
|
||||||
private val instance: SecureRandomSpi = if (SystemUtils.IS_OS_LINUX) tryAndUseLinuxSecureRandomSpi() else PlatformSecureRandomSpi()
|
private val instance: SecureRandomSpi = if (SystemUtils.IS_OS_LINUX) tryAndUseLinuxSecureRandomSpi() else PlatformSecureRandomSpi()
|
||||||
|
|
||||||
@Suppress("TooGenericExceptionCaught", "TooGenericExceptionThrown")
|
|
||||||
private fun tryAndUseLinuxSecureRandomSpi(): SecureRandomSpi = try {
|
private fun tryAndUseLinuxSecureRandomSpi(): SecureRandomSpi = try {
|
||||||
LinuxSecureRandomSpi()
|
LinuxSecureRandomSpi()
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
logger.error("Unable to initialise LinuxSecureRandomSpi. The exception logged with this message might assist with diagnosis." +
|
logger.error("Unable to initialise LinuxSecureRandomSpi. The exception logged with this message might assist with diagnosis." +
|
||||||
" The process will now exit.", e)
|
" The process will now exit.", e)
|
||||||
System.exit(1)
|
exitProcess(1)
|
||||||
throw RuntimeException("Never reached, but calms the compiler.")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun newInstance(constructorParameter: Any?) = instance
|
override fun newInstance(constructorParameter: Any?) = instance
|
||||||
@ -63,7 +63,7 @@ private class PlatformSecureRandomSpi : SecureRandomSpi() {
|
|||||||
override fun engineGenerateSeed(numBytes: Int): ByteArray = secureRandom.generateSeed(numBytes)
|
override fun engineGenerateSeed(numBytes: Int): ByteArray = secureRandom.generateSeed(numBytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("TooGenericExceptionCaught", "TooGenericExceptionThrown")
|
@Suppress("TooGenericExceptionThrown")
|
||||||
private class LinuxSecureRandomSpi : SecureRandomSpi() {
|
private class LinuxSecureRandomSpi : SecureRandomSpi() {
|
||||||
private fun openURandom(): InputStream {
|
private fun openURandom(): InputStream {
|
||||||
try {
|
try {
|
||||||
@ -91,5 +91,5 @@ private class LinuxSecureRandomSpi : SecureRandomSpi() {
|
|||||||
|
|
||||||
// This is safe to share because of the underlying implementation of SecureRandomSpi
|
// This is safe to share because of the underlying implementation of SecureRandomSpi
|
||||||
private val sharedSecureRandom: SecureRandom by lazy(LazyThreadSafetyMode.PUBLICATION) {
|
private val sharedSecureRandom: SecureRandom by lazy(LazyThreadSafetyMode.PUBLICATION) {
|
||||||
SecureRandom.getInstance(PlatformSecureRandomService.algorithm)
|
SecureRandom.getInstance(PlatformSecureRandomService.ALGORITHM)
|
||||||
}
|
}
|
||||||
|
@ -28,6 +28,7 @@ val cordaSecurityProvider = CordaSecurityProvider().also {
|
|||||||
// a SecureRandom implementation.
|
// a SecureRandom implementation.
|
||||||
Security.insertProviderAt(it, 1) // The position is 1-based.
|
Security.insertProviderAt(it, 1) // The position is 1-based.
|
||||||
}
|
}
|
||||||
|
|
||||||
// OID taken from https://tools.ietf.org/html/draft-ietf-curdle-pkix-00
|
// OID taken from https://tools.ietf.org/html/draft-ietf-curdle-pkix-00
|
||||||
val `id-Curve25519ph` = ASN1ObjectIdentifier("1.3.101.112")
|
val `id-Curve25519ph` = ASN1ObjectIdentifier("1.3.101.112")
|
||||||
val cordaBouncyCastleProvider = BouncyCastleProvider().apply {
|
val cordaBouncyCastleProvider = BouncyCastleProvider().apply {
|
||||||
@ -45,6 +46,23 @@ val cordaBouncyCastleProvider = BouncyCastleProvider().apply {
|
|||||||
// This registration is needed for reading back EdDSA key from java keystore.
|
// This registration is needed for reading back EdDSA key from java keystore.
|
||||||
// TODO: Find a way to make JKS work with bouncy castle provider or implement our own provide so we don't have to register bouncy castle provider.
|
// TODO: Find a way to make JKS work with bouncy castle provider or implement our own provide so we don't have to register bouncy castle provider.
|
||||||
Security.addProvider(it)
|
Security.addProvider(it)
|
||||||
|
|
||||||
|
// Remove providers that class with bouncy castle
|
||||||
|
val bcProviders = it.keys
|
||||||
|
|
||||||
|
// JDK 17: Add SunEC provider as lowest priority, as we use Bouncycastle for EDDSA
|
||||||
|
// and remove amy algorithms that conflict with Bouncycastle
|
||||||
|
val sunEC = Security.getProvider("SunEC")
|
||||||
|
if (sunEC != null) {
|
||||||
|
Security.removeProvider("SunEC")
|
||||||
|
Security.addProvider(sunEC)
|
||||||
|
|
||||||
|
for(alg in sunEC.keys) {
|
||||||
|
if (bcProviders.contains(alg)) {
|
||||||
|
sunEC.remove(alg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val bouncyCastlePQCProvider = BouncyCastlePQCProvider().apply {
|
val bouncyCastlePQCProvider = BouncyCastlePQCProvider().apply {
|
||||||
|
@ -573,7 +573,7 @@ abstract class FlowLogic<out T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun <R> associateSessionsToReceiveType(receiveType: Class<R>, sessions: List<FlowSession>): Map<FlowSession, Class<R>> {
|
private fun <R> associateSessionsToReceiveType(receiveType: Class<R>, sessions: List<FlowSession>): Map<FlowSession, Class<R>> {
|
||||||
return sessions.associateByTo(LinkedHashMap(), { it }, { receiveType })
|
return sessions.associateWithTo(LinkedHashMap()) { receiveType }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun <R> castMapValuesToKnownType(map: Map<FlowSession, UntrustworthyData<Any>>): List<UntrustworthyData<R>> {
|
private fun <R> castMapValuesToKnownType(map: Map<FlowSession, UntrustworthyData<Any>>): List<UntrustworthyData<R>> {
|
||||||
|
@ -11,6 +11,8 @@ import net.corda.core.internal.NetworkParametersStorage
|
|||||||
import net.corda.core.internal.PlatformVersionSwitches
|
import net.corda.core.internal.PlatformVersionSwitches
|
||||||
import net.corda.core.internal.RetrieveAnyTransactionPayload
|
import net.corda.core.internal.RetrieveAnyTransactionPayload
|
||||||
import net.corda.core.internal.ServiceHubCoreInternal
|
import net.corda.core.internal.ServiceHubCoreInternal
|
||||||
|
import net.corda.core.internal.getRequiredTransaction
|
||||||
|
import net.corda.core.internal.mapToSet
|
||||||
import net.corda.core.internal.readFully
|
import net.corda.core.internal.readFully
|
||||||
import net.corda.core.node.ServicesForResolution
|
import net.corda.core.node.ServicesForResolution
|
||||||
import net.corda.core.node.StatesToRecord
|
import net.corda.core.node.StatesToRecord
|
||||||
@ -169,13 +171,10 @@ open class DataVendingFlow(val otherSessions: Set<FlowSession>, val payload: Any
|
|||||||
is SignedTransaction -> TransactionAuthorisationFilter().addAuthorised(getInputTransactions(payload))
|
is SignedTransaction -> TransactionAuthorisationFilter().addAuthorised(getInputTransactions(payload))
|
||||||
is RetrieveAnyTransactionPayload -> TransactionAuthorisationFilter(acceptAll = true)
|
is RetrieveAnyTransactionPayload -> TransactionAuthorisationFilter(acceptAll = true)
|
||||||
is List<*> -> TransactionAuthorisationFilter().addAuthorised(payload.flatMap { someObject ->
|
is List<*> -> TransactionAuthorisationFilter().addAuthorised(payload.flatMap { someObject ->
|
||||||
if (someObject is StateAndRef<*>) {
|
when (someObject) {
|
||||||
getInputTransactions(serviceHub.validatedTransactions.getTransaction(someObject.ref.txhash)!!) + someObject.ref.txhash
|
is StateAndRef<*> -> getInputTransactions(serviceHub.getRequiredTransaction(someObject.ref.txhash)) + someObject.ref.txhash
|
||||||
}
|
is NamedByHash -> setOf(someObject.id)
|
||||||
else if (someObject is NamedByHash) {
|
else -> throw Exception("Unknown payload type: ${someObject!!::class.java} ?")
|
||||||
setOf(someObject.id)
|
|
||||||
} else {
|
|
||||||
throw Exception("Unknown payload type: ${someObject!!::class.java} ?")
|
|
||||||
}
|
}
|
||||||
}.toSet())
|
}.toSet())
|
||||||
else -> throw Exception("Unknown payload type: ${payload::class.java} ?")
|
else -> throw Exception("Unknown payload type: ${payload::class.java} ?")
|
||||||
@ -308,7 +307,7 @@ open class DataVendingFlow(val otherSessions: Set<FlowSession>, val payload: Any
|
|||||||
|
|
||||||
@Suspendable
|
@Suspendable
|
||||||
private fun getInputTransactions(tx: SignedTransaction): Set<SecureHash> {
|
private fun getInputTransactions(tx: SignedTransaction): Set<SecureHash> {
|
||||||
return tx.inputs.map { it.txhash }.toSet() + tx.references.map { it.txhash }.toSet()
|
return tx.inputs.mapToSet { it.txhash } + tx.references.mapToSet { it.txhash }
|
||||||
}
|
}
|
||||||
|
|
||||||
private class TransactionAuthorisationFilter(private val authorisedTransactions: MutableSet<SecureHash> = mutableSetOf(), val acceptAll: Boolean = false) {
|
private class TransactionAuthorisationFilter(private val authorisedTransactions: MutableSet<SecureHash> = mutableSetOf(), val acceptAll: Boolean = false) {
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package net.corda.core.identity
|
package net.corda.core.identity
|
||||||
|
|
||||||
import net.corda.core.internal.CertRole
|
import net.corda.core.internal.CertRole
|
||||||
import net.corda.core.internal.uncheckedCast
|
|
||||||
import net.corda.core.internal.validate
|
import net.corda.core.internal.validate
|
||||||
import net.corda.core.serialization.CordaSerializable
|
import net.corda.core.serialization.CordaSerializable
|
||||||
import java.security.PublicKey
|
import java.security.PublicKey
|
||||||
@ -52,7 +51,7 @@ class PartyAndCertificate(val certPath: CertPath) {
|
|||||||
// Apply Corda-specific validity rules to the chain. This only applies to chains with any roles present, so
|
// Apply Corda-specific validity rules to the chain. This only applies to chains with any roles present, so
|
||||||
// an all-null chain is in theory valid.
|
// an all-null chain is in theory valid.
|
||||||
var parentRole: CertRole? = CertRole.extract(result.trustAnchor.trustedCert)
|
var parentRole: CertRole? = CertRole.extract(result.trustAnchor.trustedCert)
|
||||||
val certChain: List<X509Certificate> = uncheckedCast(certPath.certificates)
|
val certChain = certPath.certificates as List<X509Certificate>
|
||||||
for (certIdx in (0 until certChain.size).reversed()) {
|
for (certIdx in (0 until certChain.size).reversed()) {
|
||||||
val certificate = certChain[certIdx]
|
val certificate = certChain[certIdx]
|
||||||
val role = CertRole.extract(certificate)
|
val role = CertRole.extract(certificate)
|
||||||
|
@ -21,8 +21,7 @@ import net.corda.core.serialization.internal.AttachmentURLStreamHandlerFactory.a
|
|||||||
fun <T: Any> createInstancesOfClassesImplementing(classloader: ClassLoader, clazz: Class<T>,
|
fun <T: Any> createInstancesOfClassesImplementing(classloader: ClassLoader, clazz: Class<T>,
|
||||||
classVersionRange: IntRange? = null): Set<T> {
|
classVersionRange: IntRange? = null): Set<T> {
|
||||||
return getNamesOfClassesImplementing(classloader, clazz, classVersionRange)
|
return getNamesOfClassesImplementing(classloader, clazz, classVersionRange)
|
||||||
.map { Class.forName(it, false, classloader).asSubclass(clazz) }
|
.mapToSet { loadClassOfType(clazz, it, false, classloader).kotlin.objectOrNewInstance() }
|
||||||
.mapTo(LinkedHashSet()) { it.kotlin.objectOrNewInstance() }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -36,7 +35,13 @@ fun <T: Any> createInstancesOfClassesImplementing(classloader: ClassLoader, claz
|
|||||||
*/
|
*/
|
||||||
fun <T: Any> getNamesOfClassesImplementing(classloader: ClassLoader, clazz: Class<T>,
|
fun <T: Any> getNamesOfClassesImplementing(classloader: ClassLoader, clazz: Class<T>,
|
||||||
classVersionRange: IntRange? = null): Set<String> {
|
classVersionRange: IntRange? = null): Set<String> {
|
||||||
return ClassGraph().overrideClassLoaders(classloader)
|
val isJava11 = JavaVersion.isVersionAtLeast(JavaVersion.Java_11)
|
||||||
|
|
||||||
|
return ClassGraph().apply {
|
||||||
|
if (!isJava11 || classloader !== ClassLoader.getSystemClassLoader()) {
|
||||||
|
overrideClassLoaders(classloader)
|
||||||
|
}
|
||||||
|
}
|
||||||
.enableURLScheme(attachmentScheme)
|
.enableURLScheme(attachmentScheme)
|
||||||
.ignoreParentClassLoaders()
|
.ignoreParentClassLoaders()
|
||||||
.enableClassInfo()
|
.enableClassInfo()
|
||||||
@ -50,10 +55,23 @@ fun <T: Any> getNamesOfClassesImplementing(classloader: ClassLoader, clazz: Clas
|
|||||||
}
|
}
|
||||||
result.getClassesImplementing(clazz.name)
|
result.getClassesImplementing(clazz.name)
|
||||||
.filterNot(ClassInfo::isAbstract)
|
.filterNot(ClassInfo::isAbstract)
|
||||||
.mapTo(LinkedHashSet(), ClassInfo::getName)
|
.mapToSet(ClassInfo::getName)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws ClassNotFoundException
|
||||||
|
* @throws ClassCastException
|
||||||
|
* @see Class.forName
|
||||||
|
*/
|
||||||
|
inline fun <reified T> loadClassOfType(className: String, initialize: Boolean = true, classLoader: ClassLoader? = null): Class<out T> {
|
||||||
|
return loadClassOfType(T::class.java, className, initialize, classLoader)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun <T> loadClassOfType(type: Class<T>, className: String, initialize: Boolean = true, classLoader: ClassLoader? = null): Class<out T> {
|
||||||
|
return Class.forName(className, initialize, classLoader).asSubclass(type)
|
||||||
|
}
|
||||||
|
|
||||||
fun <T: Any?> executeWithThreadContextClassLoader(classloader: ClassLoader, fn: () -> T): T {
|
fun <T: Any?> executeWithThreadContextClassLoader(classloader: ClassLoader, fn: () -> T): T {
|
||||||
val threadClassLoader = Thread.currentThread().contextClassLoader
|
val threadClassLoader = Thread.currentThread().contextClassLoader
|
||||||
try {
|
try {
|
||||||
@ -62,5 +80,4 @@ fun <T: Any?> executeWithThreadContextClassLoader(classloader: ClassLoader, fn:
|
|||||||
} finally {
|
} finally {
|
||||||
Thread.currentThread().contextClassLoader = threadClassLoader
|
Thread.currentThread().contextClassLoader = threadClassLoader
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,35 +1,29 @@
|
|||||||
@file:Suppress("TooManyFunctions")
|
@file:Suppress("TooManyFunctions")
|
||||||
package net.corda.core.internal
|
package net.corda.core.internal
|
||||||
|
|
||||||
import net.corda.core.contracts.Attachment
|
|
||||||
import net.corda.core.contracts.ContractClassName
|
import net.corda.core.contracts.ContractClassName
|
||||||
import net.corda.core.cordapp.CordappProvider
|
import net.corda.core.contracts.TransactionResolutionException
|
||||||
|
import net.corda.core.crypto.SecureHash
|
||||||
import net.corda.core.flows.DataVendingFlow
|
import net.corda.core.flows.DataVendingFlow
|
||||||
import net.corda.core.flows.FlowLogic
|
import net.corda.core.flows.FlowLogic
|
||||||
import net.corda.core.node.NetworkParameters
|
import net.corda.core.node.NetworkParameters
|
||||||
|
import net.corda.core.node.ServiceHub
|
||||||
import net.corda.core.node.ServicesForResolution
|
import net.corda.core.node.ServicesForResolution
|
||||||
import net.corda.core.node.ZoneVersionTooLowException
|
import net.corda.core.node.ZoneVersionTooLowException
|
||||||
import net.corda.core.node.services.AttachmentId
|
import net.corda.core.node.services.TransactionStorage
|
||||||
import net.corda.core.node.services.AttachmentStorage
|
|
||||||
import net.corda.core.node.services.vault.AttachmentQueryCriteria
|
|
||||||
import net.corda.core.node.services.vault.AttachmentSort
|
|
||||||
import net.corda.core.node.services.vault.Builder
|
|
||||||
import net.corda.core.node.services.vault.Sort
|
|
||||||
import net.corda.core.serialization.CordaSerializable
|
import net.corda.core.serialization.CordaSerializable
|
||||||
import net.corda.core.serialization.SerializationContext
|
import net.corda.core.serialization.SerializationContext
|
||||||
import net.corda.core.transactions.LedgerTransaction
|
|
||||||
import net.corda.core.transactions.SignedTransaction
|
import net.corda.core.transactions.SignedTransaction
|
||||||
import net.corda.core.transactions.TransactionBuilder
|
import net.corda.core.transactions.TransactionBuilder
|
||||||
import net.corda.core.transactions.WireTransaction
|
import net.corda.core.transactions.WireTransaction
|
||||||
import org.slf4j.MDC
|
import org.slf4j.MDC
|
||||||
import java.security.PublicKey
|
import java.security.PublicKey
|
||||||
import java.util.jar.JarInputStream
|
|
||||||
|
|
||||||
// *Internal* Corda-specific utilities.
|
// *Internal* Corda-specific utilities.
|
||||||
|
|
||||||
|
|
||||||
// When incrementing platformVersion make sure to update PLATFORM_VERSION in constants.properties as well.
|
// When incrementing platformVersion make sure to update PLATFORM_VERSION in constants.properties as well.
|
||||||
const val PLATFORM_VERSION = 13
|
const val PLATFORM_VERSION = 14
|
||||||
|
|
||||||
fun ServicesForResolution.ensureMinimumPlatformVersion(requiredMinPlatformVersion: Int, feature: String) {
|
fun ServicesForResolution.ensureMinimumPlatformVersion(requiredMinPlatformVersion: Int, feature: String) {
|
||||||
checkMinimumPlatformVersion(networkParameters.minimumPlatformVersion, requiredMinPlatformVersion, feature)
|
checkMinimumPlatformVersion(networkParameters.minimumPlatformVersion, requiredMinPlatformVersion, feature)
|
||||||
@ -68,11 +62,6 @@ fun TransactionBuilder.toWireTransaction(services: ServicesForResolution, serial
|
|||||||
return toWireTransactionWithContext(services, serializationContext)
|
return toWireTransactionWithContext(services, serializationContext)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Provide access to internal method for AttachmentClassLoaderTests. */
|
|
||||||
fun TransactionBuilder.toLedgerTransaction(services: ServicesForResolution, serializationContext: SerializationContext): LedgerTransaction {
|
|
||||||
return toLedgerTransactionWithContext(services, serializationContext)
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Checks if this flow is an idempotent flow. */
|
/** Checks if this flow is an idempotent flow. */
|
||||||
fun Class<out FlowLogic<*>>.isIdempotentFlow(): Boolean {
|
fun Class<out FlowLogic<*>>.isIdempotentFlow(): Boolean {
|
||||||
return IdempotentFlow::class.java.isAssignableFrom(this)
|
return IdempotentFlow::class.java.isAssignableFrom(this)
|
||||||
@ -125,40 +114,8 @@ fun noPackageOverlap(packages: Collection<String>): Boolean {
|
|||||||
return packages.all { outer -> packages.none { inner -> inner != outer && inner.startsWith("$outer.") } }
|
return packages.all { outer -> packages.none { inner -> inner != outer && inner.startsWith("$outer.") } }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
fun TransactionStorage.getRequiredTransaction(txhash: SecureHash): SignedTransaction {
|
||||||
* @return The set of [AttachmentId]s after the node's fix-up rules have been applied to [attachmentIds].
|
return getTransaction(txhash) ?: throw TransactionResolutionException(txhash)
|
||||||
*/
|
|
||||||
fun CordappProvider.internalFixupAttachmentIds(attachmentIds: Collection<AttachmentId>): Set<AttachmentId> {
|
|
||||||
return (this as CordappFixupInternal).fixupAttachmentIds(attachmentIds)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
fun ServiceHub.getRequiredTransaction(txhash: SecureHash): SignedTransaction = validatedTransactions.getRequiredTransaction(txhash)
|
||||||
* Scans trusted (installed locally) attachments to find all that contain the [className].
|
|
||||||
* This is required as a workaround until explicit cordapp dependencies are implemented.
|
|
||||||
* DO NOT USE IN CLIENT code.
|
|
||||||
*
|
|
||||||
* @return the attachments with the highest version.
|
|
||||||
*
|
|
||||||
* TODO: Should throw when the class is found in multiple contract attachments (not different versions).
|
|
||||||
*/
|
|
||||||
fun AttachmentStorage.internalFindTrustedAttachmentForClass(className: String): Attachment? {
|
|
||||||
val allTrusted = queryAttachments(
|
|
||||||
AttachmentQueryCriteria.AttachmentsQueryCriteria().withUploader(Builder.`in`(TRUSTED_UPLOADERS)),
|
|
||||||
AttachmentSort(listOf(AttachmentSort.AttachmentSortColumn(AttachmentSort.AttachmentSortAttribute.VERSION, Sort.Direction.DESC))))
|
|
||||||
|
|
||||||
// TODO - add caching if performance is affected.
|
|
||||||
for (attId in allTrusted) {
|
|
||||||
val attch = openAttachment(attId)!!
|
|
||||||
if (attch.openAsJAR().use { hasFile(it, "$className.class") }) return attch
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun hasFile(jarStream: JarInputStream, className: String): Boolean {
|
|
||||||
while (true) {
|
|
||||||
val e = jarStream.nextJarEntry ?: return false
|
|
||||||
if (e.name == className) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -1,7 +0,0 @@
|
|||||||
package net.corda.core.internal
|
|
||||||
|
|
||||||
import net.corda.core.node.services.AttachmentId
|
|
||||||
|
|
||||||
interface CordappFixupInternal {
|
|
||||||
fun fixupAttachmentIds(attachmentIds: Collection<AttachmentId>): Set<AttachmentId>
|
|
||||||
}
|
|
@ -1,4 +1,3 @@
|
|||||||
@file:JvmName("InternalUtils")
|
|
||||||
package net.corda.core.internal
|
package net.corda.core.internal
|
||||||
|
|
||||||
import net.corda.core.crypto.Crypto
|
import net.corda.core.crypto.Crypto
|
||||||
@ -23,22 +22,18 @@ import rx.subjects.UnicastSubject
|
|||||||
import java.io.ByteArrayOutputStream
|
import java.io.ByteArrayOutputStream
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
import java.io.OutputStream
|
|
||||||
import java.lang.reflect.Field
|
import java.lang.reflect.Field
|
||||||
import java.lang.reflect.Member
|
import java.lang.reflect.Member
|
||||||
import java.lang.reflect.Modifier
|
import java.lang.reflect.Modifier
|
||||||
import java.math.BigDecimal
|
|
||||||
import java.net.HttpURLConnection
|
import java.net.HttpURLConnection
|
||||||
import java.net.HttpURLConnection.HTTP_MOVED_PERM
|
import java.net.HttpURLConnection.HTTP_MOVED_PERM
|
||||||
import java.net.HttpURLConnection.HTTP_OK
|
import java.net.HttpURLConnection.HTTP_OK
|
||||||
import java.net.Proxy
|
import java.net.Proxy
|
||||||
import java.net.URI
|
|
||||||
import java.net.URL
|
import java.net.URL
|
||||||
import java.nio.ByteBuffer
|
import java.nio.ByteBuffer
|
||||||
import java.nio.file.CopyOption
|
import java.nio.file.CopyOption
|
||||||
import java.nio.file.Files
|
import java.nio.file.Files
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
import java.nio.file.Paths
|
|
||||||
import java.security.KeyPair
|
import java.security.KeyPair
|
||||||
import java.security.MessageDigest
|
import java.security.MessageDigest
|
||||||
import java.security.PrivateKey
|
import java.security.PrivateKey
|
||||||
@ -73,6 +68,7 @@ import java.util.stream.StreamSupport
|
|||||||
import java.util.zip.Deflater
|
import java.util.zip.Deflater
|
||||||
import java.util.zip.ZipEntry
|
import java.util.zip.ZipEntry
|
||||||
import java.util.zip.ZipOutputStream
|
import java.util.zip.ZipOutputStream
|
||||||
|
import kotlin.io.path.toPath
|
||||||
import kotlin.math.roundToLong
|
import kotlin.math.roundToLong
|
||||||
import kotlin.reflect.KClass
|
import kotlin.reflect.KClass
|
||||||
import kotlin.reflect.full.createInstance
|
import kotlin.reflect.full.createInstance
|
||||||
@ -95,8 +91,8 @@ infix fun Temporal.until(endExclusive: Temporal): Duration = Duration.between(th
|
|||||||
operator fun Duration.div(divider: Long): Duration = dividedBy(divider)
|
operator fun Duration.div(divider: Long): Duration = dividedBy(divider)
|
||||||
operator fun Duration.times(multiplicand: Long): Duration = multipliedBy(multiplicand)
|
operator fun Duration.times(multiplicand: Long): Duration = multipliedBy(multiplicand)
|
||||||
operator fun Duration.times(multiplicand: Double): Duration = Duration.ofNanos((toNanos() * multiplicand).roundToLong())
|
operator fun Duration.times(multiplicand: Double): Duration = Duration.ofNanos((toNanos() * multiplicand).roundToLong())
|
||||||
fun min(d1: Duration, d2: Duration): Duration = if (d1 <= d2) d1 else d2
|
|
||||||
|
|
||||||
|
fun min(d1: Duration, d2: Duration): Duration = if (d1 <= d2) d1 else d2
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the single element matching the given [predicate], or `null` if the collection is empty, or throws exception
|
* Returns the single element matching the given [predicate], or `null` if the collection is empty, or throws exception
|
||||||
@ -126,15 +122,6 @@ fun <T> List<T>.noneOrSingle(): T? {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns a random element in the list, or `null` if empty */
|
|
||||||
fun <T> List<T>.randomOrNull(): T? {
|
|
||||||
return when (size) {
|
|
||||||
0 -> null
|
|
||||||
1 -> this[0]
|
|
||||||
else -> this[(Math.random() * size).toInt()]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Returns the index of the given item or throws [IllegalArgumentException] if not found. */
|
/** Returns the index of the given item or throws [IllegalArgumentException] if not found. */
|
||||||
fun <T> List<T>.indexOfOrThrow(item: T): Int {
|
fun <T> List<T>.indexOfOrThrow(item: T): Int {
|
||||||
val i = indexOf(item)
|
val i = indexOf(item)
|
||||||
@ -142,6 +129,30 @@ fun <T> List<T>.indexOfOrThrow(item: T): Int {
|
|||||||
return i
|
return i
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Similar to [Iterable.map] except it maps to a [Set] which preserves the iteration order.
|
||||||
|
*/
|
||||||
|
inline fun <T, R> Iterable<T>.mapToSet(transform: (T) -> R): Set<R> {
|
||||||
|
if (this is Collection) {
|
||||||
|
when (size) {
|
||||||
|
0 -> return emptySet()
|
||||||
|
1 -> return setOf(transform(first()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return mapTo(LinkedHashSet(), transform)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Similar to [Iterable.flatMap] except it maps to a [Set] which preserves the iteration order.
|
||||||
|
*/
|
||||||
|
inline fun <T, R> Iterable<T>.flatMapToSet(transform: (T) -> Iterable<R>): Set<R> {
|
||||||
|
return if (this is Collection && isEmpty()) {
|
||||||
|
emptySet()
|
||||||
|
} else {
|
||||||
|
flatMapTo(LinkedHashSet(), transform)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun InputStream.copyTo(target: Path, vararg options: CopyOption): Long = Files.copy(this, target, *options)
|
fun InputStream.copyTo(target: Path, vararg options: CopyOption): Long = Files.copy(this, target, *options)
|
||||||
|
|
||||||
/** Same as [InputStream.readBytes] but also closes the stream. */
|
/** Same as [InputStream.readBytes] but also closes the stream. */
|
||||||
@ -165,16 +176,7 @@ fun InputStream.hash(): SecureHash {
|
|||||||
|
|
||||||
inline fun <reified T : Any> InputStream.readObject(): T = readFully().deserialize()
|
inline fun <reified T : Any> InputStream.readObject(): T = readFully().deserialize()
|
||||||
|
|
||||||
object NullOutputStream : OutputStream() {
|
fun String.abbreviate(maxWidth: Int): String = if (length <= maxWidth) this else "${take(maxWidth - 1)}…"
|
||||||
override fun write(b: Int) = Unit
|
|
||||||
override fun write(b: ByteArray) = Unit
|
|
||||||
override fun write(b: ByteArray, off: Int, len: Int) = Unit
|
|
||||||
}
|
|
||||||
|
|
||||||
fun String.abbreviate(maxWidth: Int): String = if (length <= maxWidth) this else take(maxWidth - 1) + "…"
|
|
||||||
|
|
||||||
/** Return the sum of an Iterable of [BigDecimal]s. */
|
|
||||||
fun Iterable<BigDecimal>.sum(): BigDecimal = fold(BigDecimal.ZERO) { a, b -> a + b }
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns an Observable that buffers events until subscribed.
|
* Returns an Observable that buffers events until subscribed.
|
||||||
@ -286,7 +288,7 @@ private fun IntProgression.toSpliterator(): Spliterator.OfInt {
|
|||||||
fun IntProgression.stream(parallel: Boolean = false): IntStream = StreamSupport.intStream(toSpliterator(), parallel)
|
fun IntProgression.stream(parallel: Boolean = false): IntStream = StreamSupport.intStream(toSpliterator(), parallel)
|
||||||
|
|
||||||
// When toArray has filled in the array, the component type is no longer T? but T (that may itself be nullable):
|
// When toArray has filled in the array, the component type is no longer T? but T (that may itself be nullable):
|
||||||
inline fun <reified T> Stream<out T>.toTypedArray(): Array<T> = uncheckedCast(toArray { size -> arrayOfNulls<T>(size) })
|
inline fun <reified T> Stream<out T>.toTypedArray(): Array<out T?>? = uncheckedCast(toArray { size -> arrayOfNulls<T>(size) })
|
||||||
|
|
||||||
inline fun <T, R : Any> Stream<T>.mapNotNull(crossinline transform: (T) -> R?): Stream<R> {
|
inline fun <T, R : Any> Stream<T>.mapNotNull(crossinline transform: (T) -> R?): Stream<R> {
|
||||||
return flatMap {
|
return flatMap {
|
||||||
@ -335,7 +337,10 @@ val <T : Any> Class<T>.kotlinObjectInstance: T? get() {
|
|||||||
field?.let {
|
field?.let {
|
||||||
if (it.type == this && it.isPublic && it.isStatic && it.isFinal) {
|
if (it.type == this && it.isPublic && it.isStatic && it.isFinal) {
|
||||||
it.isAccessible = true
|
it.isAccessible = true
|
||||||
uncheckedCast(it.get(null))
|
|
||||||
|
// TODO JDK17: Why does uncheckedCast(...) cause class cast exception?
|
||||||
|
// uncheckedCast(it.get(null))
|
||||||
|
it.get(null) as T
|
||||||
} else {
|
} else {
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
@ -431,8 +436,6 @@ inline val Member.isStatic: Boolean get() = Modifier.isStatic(modifiers)
|
|||||||
|
|
||||||
inline val Member.isFinal: Boolean get() = Modifier.isFinal(modifiers)
|
inline val Member.isFinal: Boolean get() = Modifier.isFinal(modifiers)
|
||||||
|
|
||||||
fun URI.toPath(): Path = Paths.get(this)
|
|
||||||
|
|
||||||
fun URL.toPath(): Path = toURI().toPath()
|
fun URL.toPath(): Path = toURI().toPath()
|
||||||
|
|
||||||
val DEFAULT_HTTP_CONNECT_TIMEOUT = 30.seconds.toMillis()
|
val DEFAULT_HTTP_CONNECT_TIMEOUT = 30.seconds.toMillis()
|
||||||
@ -529,11 +532,6 @@ fun ByteBuffer.copyBytes(): ByteArray = ByteArray(remaining()).also { get(it) }
|
|||||||
|
|
||||||
val PublicKey.hash: SecureHash get() = Crypto.encodePublicKey(this).sha256()
|
val PublicKey.hash: SecureHash get() = Crypto.encodePublicKey(this).sha256()
|
||||||
|
|
||||||
/**
|
|
||||||
* Extension method for providing a sumBy method that processes and returns a Long
|
|
||||||
*/
|
|
||||||
fun <T> Iterable<T>.sumByLong(selector: (T) -> Long): Long = this.map { selector(it) }.sum()
|
|
||||||
|
|
||||||
fun <T : Any> SerializedBytes<Any>.checkPayloadIs(type: Class<T>): UntrustworthyData<T> {
|
fun <T : Any> SerializedBytes<Any>.checkPayloadIs(type: Class<T>): UntrustworthyData<T> {
|
||||||
val payloadData: T = try {
|
val payloadData: T = try {
|
||||||
val serializer = SerializationDefaults.SERIALIZATION_FACTORY
|
val serializer = SerializationDefaults.SERIALIZATION_FACTORY
|
||||||
@ -560,6 +558,10 @@ fun <K, V> MutableMap<K, V>.toSynchronised(): MutableMap<K, V> = Collections.syn
|
|||||||
/** @see Collections.synchronizedSet */
|
/** @see Collections.synchronizedSet */
|
||||||
fun <E> MutableSet<E>.toSynchronised(): MutableSet<E> = Collections.synchronizedSet(this)
|
fun <E> MutableSet<E>.toSynchronised(): MutableSet<E> = Collections.synchronizedSet(this)
|
||||||
|
|
||||||
|
fun Collection<*>.equivalent(other: Collection<*>): Boolean {
|
||||||
|
return this.size == other.size && this.containsAll(other) && other.containsAll(this)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* List implementation that applies the expensive [transform] function only when the element is accessed and caches calculated values.
|
* List implementation that applies the expensive [transform] function only when the element is accessed and caches calculated values.
|
||||||
* Size is very cheap as it doesn't call [transform].
|
* Size is very cheap as it doesn't call [transform].
|
||||||
@ -613,5 +615,5 @@ fun Logger.warnOnce(warning: String) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const val JDK1_2_CLASS_FILE_FORMAT_MAJOR_VERSION = 46
|
const val JAVA_1_2_CLASS_FILE_FORMAT_MAJOR_VERSION = 46
|
||||||
const val JDK8_CLASS_FILE_FORMAT_MAJOR_VERSION = 52
|
const val JAVA_17_CLASS_FILE_FORMAT_MAJOR_VERSION = 61
|
||||||
|
@ -9,18 +9,24 @@ import com.github.benmanes.caffeine.cache.LoadingCache
|
|||||||
* Allow extra functionality to be injected to our caches.
|
* Allow extra functionality to be injected to our caches.
|
||||||
*/
|
*/
|
||||||
interface NamedCacheFactory {
|
interface NamedCacheFactory {
|
||||||
|
companion object {
|
||||||
|
private val allowedChars = Regex("""^[0-9A-Za-z_.]*$""")
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Restrict the allowed characters of a cache name - this ensures that each cache has a name, and that
|
* Restrict the allowed characters of a cache name - this ensures that each cache has a name, and that
|
||||||
* the name can be used to create a file name or a metric name.
|
* the name can be used to create a file name or a metric name.
|
||||||
*/
|
*/
|
||||||
fun checkCacheName(name: String) {
|
fun checkCacheName(name: String) {
|
||||||
require(!name.isBlank()){"Name must not be empty or only whitespace"}
|
require(name.isNotBlank()) { "Name must not be empty or only whitespace" }
|
||||||
require(allowedChars.matches(name)) { "Invalid characters in cache name" }
|
require(allowedChars.matches(name)) { "Invalid characters in cache name" }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun <K, V> buildNamed(name: String): Cache<K, V> = buildNamed(Caffeine.newBuilder(), name)
|
||||||
|
|
||||||
fun <K, V> buildNamed(caffeine: Caffeine<in K, in V>, name: String): Cache<K, V>
|
fun <K, V> buildNamed(caffeine: Caffeine<in K, in V>, name: String): Cache<K, V>
|
||||||
|
|
||||||
|
fun <K, V> buildNamed(name: String, loader: CacheLoader<K, V>): LoadingCache<K, V> = buildNamed(Caffeine.newBuilder(), name, loader)
|
||||||
|
|
||||||
fun <K, V> buildNamed(caffeine: Caffeine<in K, in V>, name: String, loader: CacheLoader<K, V>): LoadingCache<K, V>
|
fun <K, V> buildNamed(caffeine: Caffeine<in K, in V>, name: String, loader: CacheLoader<K, V>): LoadingCache<K, V>
|
||||||
}
|
}
|
||||||
|
|
||||||
private val allowedChars = Regex("^[0-9A-Za-z_.]*\$")
|
|
||||||
|
@ -2,41 +2,28 @@ package net.corda.core.internal
|
|||||||
|
|
||||||
import net.corda.core.crypto.SecureHash
|
import net.corda.core.crypto.SecureHash
|
||||||
import net.corda.core.serialization.deserialize
|
import net.corda.core.serialization.deserialize
|
||||||
import java.io.*
|
import java.io.IOException
|
||||||
import java.nio.charset.Charset
|
import java.io.InputStream
|
||||||
import java.nio.charset.StandardCharsets.UTF_8
|
import java.io.OutputStream
|
||||||
import java.nio.file.*
|
import java.nio.file.CopyOption
|
||||||
|
import java.nio.file.FileVisitResult
|
||||||
|
import java.nio.file.Files
|
||||||
|
import java.nio.file.LinkOption
|
||||||
|
import java.nio.file.OpenOption
|
||||||
|
import java.nio.file.Path
|
||||||
|
import java.nio.file.SimpleFileVisitor
|
||||||
import java.nio.file.attribute.BasicFileAttributes
|
import java.nio.file.attribute.BasicFileAttributes
|
||||||
import java.nio.file.attribute.FileAttribute
|
import kotlin.io.path.createDirectories
|
||||||
import java.nio.file.attribute.FileTime
|
import kotlin.io.path.deleteIfExists
|
||||||
import java.util.stream.Stream
|
import kotlin.io.path.div
|
||||||
import kotlin.streams.toList
|
import kotlin.io.path.exists
|
||||||
|
import kotlin.io.path.inputStream
|
||||||
/**
|
import kotlin.io.path.isDirectory
|
||||||
* Allows you to write code like: Paths.get("someDir") / "subdir" / "filename" but using the Paths API to avoid platform
|
import kotlin.io.path.isSymbolicLink
|
||||||
* separator problems.
|
import kotlin.io.path.name
|
||||||
* @see Path.resolve
|
import kotlin.io.path.outputStream
|
||||||
*/
|
import kotlin.io.path.readBytes
|
||||||
operator fun Path.div(other: String): Path = resolve(other)
|
import kotlin.io.path.readSymbolicLink
|
||||||
|
|
||||||
/**
|
|
||||||
* Allows you to write code like: "someDir" / "subdir" / "filename" but using the Paths API to avoid platform
|
|
||||||
* separator problems.
|
|
||||||
* @see Path.resolve
|
|
||||||
*/
|
|
||||||
operator fun String.div(other: String): Path = Paths.get(this) / other
|
|
||||||
|
|
||||||
/** @see Files.createFile */
|
|
||||||
fun Path.createFile(vararg attrs: FileAttribute<*>): Path = Files.createFile(this, *attrs)
|
|
||||||
|
|
||||||
/** @see Files.createDirectory */
|
|
||||||
fun Path.createDirectory(vararg attrs: FileAttribute<*>): Path = Files.createDirectory(this, *attrs)
|
|
||||||
|
|
||||||
/** @see Files.createDirectories */
|
|
||||||
fun Path.createDirectories(vararg attrs: FileAttribute<*>): Path = Files.createDirectories(this, *attrs)
|
|
||||||
|
|
||||||
/** @see Files.exists */
|
|
||||||
fun Path.exists(vararg options: LinkOption): Boolean = Files.exists(this, *options)
|
|
||||||
|
|
||||||
/** Copy the file into the target directory using [Files.copy]. */
|
/** Copy the file into the target directory using [Files.copy]. */
|
||||||
fun Path.copyToDirectory(targetDir: Path, vararg options: CopyOption): Path {
|
fun Path.copyToDirectory(targetDir: Path, vararg options: CopyOption): Path {
|
||||||
@ -50,107 +37,32 @@ fun Path.copyToDirectory(targetDir: Path, vararg options: CopyOption): Path {
|
|||||||
* Path.toString() is assumed safe because fileName should
|
* Path.toString() is assumed safe because fileName should
|
||||||
* not include any path separator characters.
|
* not include any path separator characters.
|
||||||
*/
|
*/
|
||||||
val targetFile = targetDir.resolve(fileName.toString())
|
val targetFile = targetDir / name
|
||||||
Files.copy(this, targetFile, *options)
|
Files.copy(this, targetFile, *options)
|
||||||
return targetFile
|
return targetFile
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @see Files.copy */
|
|
||||||
fun Path.copyTo(target: Path, vararg options: CopyOption): Path = Files.copy(this, target, *options)
|
|
||||||
|
|
||||||
/** @see Files.move */
|
|
||||||
fun Path.moveTo(target: Path, vararg options: CopyOption): Path = Files.move(this, target, *options)
|
|
||||||
|
|
||||||
/** @see Files.move */
|
|
||||||
fun Path.renameTo(fileName: String, vararg options: CopyOption): Path = moveTo(parent / fileName, *options)
|
|
||||||
|
|
||||||
/** See overload of [Files.copy] which takes in an [InputStream]. */
|
/** See overload of [Files.copy] which takes in an [InputStream]. */
|
||||||
fun Path.copyTo(out: OutputStream): Long = Files.copy(this, out)
|
fun Path.copyTo(out: OutputStream): Long = Files.copy(this, out)
|
||||||
|
|
||||||
/** @see Files.isRegularFile */
|
|
||||||
fun Path.isRegularFile(vararg options: LinkOption): Boolean = Files.isRegularFile(this, *options)
|
|
||||||
|
|
||||||
/** @see Files.isReadable */
|
|
||||||
inline val Path.isReadable: Boolean get() = Files.isReadable(this)
|
|
||||||
|
|
||||||
/** @see Files.size */
|
|
||||||
inline val Path.size: Long get() = Files.size(this)
|
|
||||||
|
|
||||||
/** @see Files.readAttributes */
|
/** @see Files.readAttributes */
|
||||||
fun Path.attributes(vararg options: LinkOption): BasicFileAttributes = Files.readAttributes(this, BasicFileAttributes::class.java, *options)
|
fun Path.attributes(vararg options: LinkOption): BasicFileAttributes = Files.readAttributes(this, BasicFileAttributes::class.java, *options)
|
||||||
|
|
||||||
/** @see Files.getLastModifiedTime */
|
|
||||||
fun Path.lastModifiedTime(vararg options: LinkOption): FileTime = Files.getLastModifiedTime(this, *options)
|
|
||||||
|
|
||||||
/** @see Files.isDirectory */
|
|
||||||
fun Path.isDirectory(vararg options: LinkOption): Boolean = Files.isDirectory(this, *options)
|
|
||||||
|
|
||||||
/** @see Files.isSameFile */
|
|
||||||
fun Path.isSameAs(other: Path): Boolean = Files.isSameFile(this, other)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Same as [Files.list] except it also closes the [Stream].
|
|
||||||
* @return the output of [block]
|
|
||||||
*/
|
|
||||||
inline fun <R> Path.list(block: (Stream<Path>) -> R): R = Files.list(this).use(block)
|
|
||||||
|
|
||||||
/** Same as [list] but materialises all the entiries into a list. */
|
|
||||||
fun Path.list(): List<Path> = list { it.toList() }
|
|
||||||
|
|
||||||
/** @see Files.walk */
|
|
||||||
inline fun <R> Path.walk(maxDepth: Int = Int.MAX_VALUE, vararg options: FileVisitOption, block: (Stream<Path>) -> R): R {
|
|
||||||
return Files.walk(this, maxDepth, *options).use(block)
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @see Files.delete */
|
|
||||||
fun Path.delete(): Unit = Files.delete(this)
|
|
||||||
|
|
||||||
/** @see Files.deleteIfExists */
|
|
||||||
fun Path.deleteIfExists(): Boolean = Files.deleteIfExists(this)
|
|
||||||
|
|
||||||
/** Deletes this path (if it exists) and if it's a directory, all its child paths recursively. */
|
/** Deletes this path (if it exists) and if it's a directory, all its child paths recursively. */
|
||||||
fun Path.deleteRecursively() {
|
fun Path.deleteRecursively() {
|
||||||
if (!exists()) return
|
if (!exists()) return
|
||||||
Files.walkFileTree(this, object : SimpleFileVisitor<Path>() {
|
Files.walkFileTree(this, object : SimpleFileVisitor<Path>() {
|
||||||
override fun visitFile(file: Path, attrs: BasicFileAttributes): FileVisitResult {
|
override fun visitFile(file: Path, attrs: BasicFileAttributes): FileVisitResult {
|
||||||
file.delete()
|
file.deleteIfExists()
|
||||||
return FileVisitResult.CONTINUE
|
return FileVisitResult.CONTINUE
|
||||||
}
|
}
|
||||||
override fun postVisitDirectory(dir: Path, exception: IOException?): FileVisitResult {
|
override fun postVisitDirectory(dir: Path, exception: IOException?): FileVisitResult {
|
||||||
dir.delete()
|
dir.deleteIfExists()
|
||||||
return FileVisitResult.CONTINUE
|
return FileVisitResult.CONTINUE
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @see Files.newOutputStream */
|
|
||||||
fun Path.outputStream(vararg options: OpenOption): OutputStream = Files.newOutputStream(this, *options)
|
|
||||||
|
|
||||||
/** @see Files.newInputStream */
|
|
||||||
fun Path.inputStream(vararg options: OpenOption): InputStream = Files.newInputStream(this, *options)
|
|
||||||
|
|
||||||
/** @see Files.newBufferedReader */
|
|
||||||
fun Path.reader(charset: Charset = UTF_8): BufferedReader = Files.newBufferedReader(this, charset)
|
|
||||||
|
|
||||||
/** @see Files.newBufferedWriter */
|
|
||||||
fun Path.writer(charset: Charset = UTF_8, vararg options: OpenOption): BufferedWriter {
|
|
||||||
return Files.newBufferedWriter(this, charset, *options)
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @see Files.readAllBytes */
|
|
||||||
fun Path.readAll(): ByteArray = Files.readAllBytes(this)
|
|
||||||
|
|
||||||
/** Read in this entire file as a string using the given encoding. */
|
|
||||||
fun Path.readText(charset: Charset = UTF_8): String = reader(charset).use(Reader::readText)
|
|
||||||
|
|
||||||
/** @see Files.write */
|
|
||||||
fun Path.write(bytes: ByteArray, vararg options: OpenOption): Path = Files.write(this, bytes, *options)
|
|
||||||
|
|
||||||
/** Write the given string to this file. */
|
|
||||||
fun Path.writeText(text: String, charset: Charset = UTF_8, vararg options: OpenOption) {
|
|
||||||
writer(charset, *options).use { it.write(text) }
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Same as [inputStream] except it also closes the [InputStream].
|
* Same as [inputStream] except it also closes the [InputStream].
|
||||||
* @return the output of [block]
|
* @return the output of [block]
|
||||||
@ -169,35 +81,14 @@ inline fun Path.write(createDirs: Boolean = false, vararg options: OpenOption =
|
|||||||
outputStream(*options).use(block)
|
outputStream(*options).use(block)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Same as [Files.lines] except it also closes the [Stream]
|
|
||||||
* @return the output of [block]
|
|
||||||
*/
|
|
||||||
inline fun <R> Path.readLines(charset: Charset = UTF_8, block: (Stream<String>) -> R): R {
|
|
||||||
return Files.lines(this, charset).use(block)
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @see Files.readAllLines */
|
|
||||||
fun Path.readAllLines(charset: Charset = UTF_8): List<String> = Files.readAllLines(this, charset)
|
|
||||||
|
|
||||||
fun Path.writeLines(lines: Iterable<CharSequence>, charset: Charset = UTF_8, vararg options: OpenOption): Path {
|
|
||||||
return Files.write(this, lines, charset, *options)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Read in this file as an AMQP serialised blob of type [T].
|
* Read in this file as an AMQP serialised blob of type [T].
|
||||||
* @see [deserialize]
|
* @see [deserialize]
|
||||||
*/
|
*/
|
||||||
inline fun <reified T : Any> Path.readObject(): T = readAll().deserialize()
|
inline fun <reified T : Any> Path.readObject(): T = readBytes().deserialize()
|
||||||
|
|
||||||
/** Calculate the hash of the contents of this file. */
|
/** Calculate the hash of the contents of this file. */
|
||||||
inline val Path.hash: SecureHash get() = read { it.hash() }
|
inline val Path.hash: SecureHash get() = read { it.hash() }
|
||||||
|
|
||||||
/* Check if the Path is symbolic link */
|
/* Check if the Path is symbolic link */
|
||||||
fun Path.safeSymbolicRead(): Path {
|
fun Path.safeSymbolicRead(): Path = if (isSymbolicLink()) readSymbolicLink() else this
|
||||||
if (Files.isSymbolicLink(this)) {
|
|
||||||
return (Files.readSymbolicLink(this))
|
|
||||||
} else {
|
|
||||||
return (this)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -6,19 +6,15 @@ import net.corda.core.crypto.TransactionSignature
|
|||||||
import net.corda.core.flows.TransactionMetadata
|
import net.corda.core.flows.TransactionMetadata
|
||||||
import net.corda.core.identity.CordaX500Name
|
import net.corda.core.identity.CordaX500Name
|
||||||
import net.corda.core.internal.notary.NotaryService
|
import net.corda.core.internal.notary.NotaryService
|
||||||
import net.corda.core.node.ServiceHub
|
import net.corda.core.internal.verification.VerifyingServiceHub
|
||||||
import net.corda.core.node.StatesToRecord
|
import net.corda.core.node.StatesToRecord
|
||||||
import net.corda.core.serialization.internal.AttachmentsClassLoaderCache
|
|
||||||
import net.corda.core.transactions.SignedTransaction
|
import net.corda.core.transactions.SignedTransaction
|
||||||
import java.util.concurrent.ExecutorService
|
import java.util.concurrent.ExecutorService
|
||||||
|
|
||||||
// TODO: This should really be called ServiceHubInternal but that name is already taken by net.corda.node.services.api.ServiceHubInternal.
|
// TODO: This should really be called ServiceHubInternal but that name is already taken by net.corda.node.services.api.ServiceHubInternal.
|
||||||
interface ServiceHubCoreInternal : ServiceHub {
|
interface ServiceHubCoreInternal : VerifyingServiceHub {
|
||||||
|
|
||||||
val externalOperationExecutor: ExecutorService
|
val externalOperationExecutor: ExecutorService
|
||||||
|
|
||||||
val attachmentTrustCalculator: AttachmentTrustCalculator
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Optional `NotaryService` which will be `null` for all non-Notary nodes.
|
* Optional `NotaryService` which will be `null` for all non-Notary nodes.
|
||||||
*/
|
*/
|
||||||
@ -26,8 +22,6 @@ interface ServiceHubCoreInternal : ServiceHub {
|
|||||||
|
|
||||||
fun createTransactionsResolver(flow: ResolveTransactionsFlow): TransactionsResolver
|
fun createTransactionsResolver(flow: ResolveTransactionsFlow): TransactionsResolver
|
||||||
|
|
||||||
val attachmentsClassLoaderCache: AttachmentsClassLoaderCache
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stores [SignedTransaction] and participant signatures without the notary signature in the local transaction storage,
|
* Stores [SignedTransaction] and participant signatures without the notary signature in the local transaction storage,
|
||||||
* inclusive of flow recovery metadata.
|
* inclusive of flow recovery metadata.
|
||||||
|
@ -172,11 +172,13 @@ fun createComponentGroups(inputs: List<StateRef>,
|
|||||||
return componentGroupMap
|
return componentGroupMap
|
||||||
}
|
}
|
||||||
|
|
||||||
|
typealias SerializedTransactionState = SerializedBytes<TransactionState<ContractState>>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A SerializedStateAndRef is a pair (BinaryStateRepresentation, StateRef).
|
* A SerializedStateAndRef is a pair (BinaryStateRepresentation, StateRef).
|
||||||
* The [serializedState] is the actual component from the original wire transaction.
|
* The [serializedState] is the actual component from the original wire transaction.
|
||||||
*/
|
*/
|
||||||
data class SerializedStateAndRef(val serializedState: SerializedBytes<TransactionState<ContractState>>, val ref: StateRef) {
|
data class SerializedStateAndRef(val serializedState: SerializedTransactionState, val ref: StateRef) {
|
||||||
fun toStateAndRef(factory: SerializationFactory, context: SerializationContext) = StateAndRef(serializedState.deserialize(factory, context), ref)
|
fun toStateAndRef(factory: SerializationFactory, context: SerializationContext) = StateAndRef(serializedState.deserialize(factory, context), ref)
|
||||||
fun toStateAndRef(): StateAndRef<ContractState> {
|
fun toStateAndRef(): StateAndRef<ContractState> {
|
||||||
val factory = SerializationFactory.defaultFactory
|
val factory = SerializationFactory.defaultFactory
|
||||||
|
@ -84,7 +84,6 @@ fun <ELEMENT> CordaFuture<out ELEMENT>.mapError(transform: (Throwable) -> Throwa
|
|||||||
* But if this future or the transform fails, the returned future's outcome is the same throwable.
|
* But if this future or the transform fails, the returned future's outcome is the same throwable.
|
||||||
* In the case where this future fails, the transform is not invoked.
|
* In the case where this future fails, the transform is not invoked.
|
||||||
*/
|
*/
|
||||||
@Suppress("TooGenericExceptionCaught")
|
|
||||||
fun <V, W> CordaFuture<out V>.flatMap(transform: (V) -> CordaFuture<out W>): CordaFuture<W> = CordaFutureImpl<W>().also { result ->
|
fun <V, W> CordaFuture<out V>.flatMap(transform: (V) -> CordaFuture<out W>): CordaFuture<W> = CordaFutureImpl<W>().also { result ->
|
||||||
thenMatch(success@ {
|
thenMatch(success@ {
|
||||||
result.captureLater(try {
|
result.captureLater(try {
|
||||||
@ -146,7 +145,6 @@ interface ValueOrException<in V> {
|
|||||||
fun captureLater(f: CordaFuture<out V>) = f.then { capture { f.getOrThrow() } }
|
fun captureLater(f: CordaFuture<out V>) = f.then { capture { f.getOrThrow() } }
|
||||||
|
|
||||||
/** Run the given block (in the foreground) and set this future to its outcome. */
|
/** Run the given block (in the foreground) and set this future to its outcome. */
|
||||||
@Suppress("TooGenericExceptionCaught")
|
|
||||||
fun capture(block: () -> V): Boolean {
|
fun capture(block: () -> V): Boolean {
|
||||||
return set(try {
|
return set(try {
|
||||||
block()
|
block()
|
||||||
@ -164,18 +162,17 @@ interface OpenFuture<V> : ValueOrException<V>, CordaFuture<V>
|
|||||||
|
|
||||||
/** Unless you really want this particular implementation, use [openFuture] to make one. */
|
/** Unless you really want this particular implementation, use [openFuture] to make one. */
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
internal class CordaFutureImpl<V>(private val impl: CompletableFuture<V> = CompletableFuture()) : Future<V> by impl, OpenFuture<V> {
|
class CordaFutureImpl<V>(private val impl: CompletableFuture<V> = CompletableFuture()) : Future<V> by impl, OpenFuture<V> {
|
||||||
companion object {
|
companion object {
|
||||||
private val defaultLog = contextLogger()
|
private val defaultLog = contextLogger()
|
||||||
internal const val listenerFailedMessage = "Future listener failed:"
|
const val listenerFailedMessage = "Future listener failed:"
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun set(value: V) = impl.complete(value)
|
override fun set(value: V) = impl.complete(value)
|
||||||
override fun setException(t: Throwable) = impl.completeExceptionally(t)
|
override fun setException(t: Throwable) = impl.completeExceptionally(t)
|
||||||
override fun <W> then(callback: (CordaFuture<V>) -> W) = thenImpl(defaultLog, callback)
|
override fun <W> then(callback: (CordaFuture<V>) -> W) = thenImpl(defaultLog, callback)
|
||||||
/** For testing only. */
|
/** For testing only. */
|
||||||
@Suppress("TooGenericExceptionCaught")
|
fun <W> thenImpl(log: Logger, callback: (CordaFuture<V>) -> W) {
|
||||||
internal fun <W> thenImpl(log: Logger, callback: (CordaFuture<V>) -> W) {
|
|
||||||
impl.whenComplete { _, _ ->
|
impl.whenComplete { _, _ ->
|
||||||
try {
|
try {
|
||||||
callback(this)
|
callback(this)
|
||||||
@ -198,4 +195,4 @@ internal class CordaFutureImpl<V>(private val impl: CompletableFuture<V> = Compl
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun <V> Future<V>.get(timeout: Duration? = null): V = if (timeout == null) get() else get(timeout.toNanos(), TimeUnit.NANOSECONDS)
|
fun <V> Future<V>.get(timeout: Duration? = null): V = if (timeout == null) get() else get(timeout.toNanos(), TimeUnit.NANOSECONDS)
|
||||||
|
@ -15,6 +15,7 @@ import net.corda.core.serialization.SerializationWhitelist
|
|||||||
import net.corda.core.serialization.SerializeAsToken
|
import net.corda.core.serialization.SerializeAsToken
|
||||||
import java.net.URL
|
import java.net.URL
|
||||||
import java.nio.file.Paths
|
import java.nio.file.Paths
|
||||||
|
import kotlin.io.path.name
|
||||||
|
|
||||||
data class CordappImpl(
|
data class CordappImpl(
|
||||||
override val contractClassNames: List<String>,
|
override val contractClassNames: List<String>,
|
||||||
@ -49,7 +50,7 @@ data class CordappImpl(
|
|||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun jarName(url: URL): String = (url.toPath().fileName ?: "").toString().removeSuffix(".jar")
|
fun jarName(url: URL): String = url.toPath().name.removeSuffix(".jar")
|
||||||
|
|
||||||
/** CorDapp manifest entries */
|
/** CorDapp manifest entries */
|
||||||
const val CORDAPP_CONTRACT_NAME = "Cordapp-Contract-Name"
|
const val CORDAPP_CONTRACT_NAME = "Cordapp-Contract-Name"
|
||||||
|
@ -0,0 +1,13 @@
|
|||||||
|
package net.corda.core.internal.cordapp
|
||||||
|
|
||||||
|
import net.corda.core.cordapp.Cordapp
|
||||||
|
import net.corda.core.cordapp.CordappProvider
|
||||||
|
import net.corda.core.flows.FlowLogic
|
||||||
|
import net.corda.core.internal.verification.AttachmentFixups
|
||||||
|
|
||||||
|
interface CordappProviderInternal : CordappProvider {
|
||||||
|
val appClassLoader: ClassLoader
|
||||||
|
val attachmentFixups: AttachmentFixups
|
||||||
|
val cordapps: List<CordappImpl>
|
||||||
|
fun getCordappForFlow(flowLogic: FlowLogic<*>): Cordapp?
|
||||||
|
}
|
@ -1,5 +1,6 @@
|
|||||||
package net.corda.core.internal.telemetry
|
package net.corda.core.internal.telemetry
|
||||||
|
|
||||||
|
import co.paralleluniverse.fibers.instrument.DontInstrument
|
||||||
import io.opentelemetry.api.GlobalOpenTelemetry
|
import io.opentelemetry.api.GlobalOpenTelemetry
|
||||||
import io.opentelemetry.api.OpenTelemetry
|
import io.opentelemetry.api.OpenTelemetry
|
||||||
import io.opentelemetry.api.baggage.Baggage
|
import io.opentelemetry.api.baggage.Baggage
|
||||||
@ -34,7 +35,7 @@ data class SpanInfo(val name: String, val span: Span, val spanScope: Scope,
|
|||||||
|
|
||||||
class TracerSetup(serviceName: String) {
|
class TracerSetup(serviceName: String) {
|
||||||
private var openTelemetryDriver: Any? = null
|
private var openTelemetryDriver: Any? = null
|
||||||
val openTelemetry: OpenTelemetry by lazy {
|
val openTelemetry: OpenTelemetry by lazy @DontInstrument {
|
||||||
try {
|
try {
|
||||||
openTelemetryDriver = OpenTelemetryDriver(serviceName)
|
openTelemetryDriver = OpenTelemetryDriver(serviceName)
|
||||||
(openTelemetryDriver as OpenTelemetryDriver).openTelemetry
|
(openTelemetryDriver as OpenTelemetryDriver).openTelemetry
|
||||||
|
@ -178,7 +178,6 @@ class TelemetryServiceImpl : SingletonSerializeAsToken(), TelemetryService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("TooGenericExceptionCaught")
|
|
||||||
inline fun <R> span(name: String, attributes: Map<String, String> = emptyMap(), flowLogic: FlowLogic<*>? = null, block: () -> R): R {
|
inline fun <R> span(name: String, attributes: Map<String, String> = emptyMap(), flowLogic: FlowLogic<*>? = null, block: () -> R): R {
|
||||||
val telemetryId = startSpan(name, attributes, flowLogic)
|
val telemetryId = startSpan(name, attributes, flowLogic)
|
||||||
try {
|
try {
|
||||||
@ -195,7 +194,7 @@ class TelemetryServiceImpl : SingletonSerializeAsToken(), TelemetryService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@CordaInternal
|
@CordaInternal
|
||||||
@Suppress("LongParameterList", "TooGenericExceptionCaught")
|
@Suppress("LongParameterList")
|
||||||
inline fun <R> spanForFlow(name: String, attributes: Map<String, String>, flowLogic: FlowLogic<*>? = null, remoteSerializedTelemetry: SerializedTelemetry? = null, block: () -> R): R {
|
inline fun <R> spanForFlow(name: String, attributes: Map<String, String>, flowLogic: FlowLogic<*>? = null, remoteSerializedTelemetry: SerializedTelemetry? = null, block: () -> R): R {
|
||||||
val telemetryId = startSpanForFlow(name, attributes, flowLogic, remoteSerializedTelemetry)
|
val telemetryId = startSpanForFlow(name, attributes, flowLogic, remoteSerializedTelemetry)
|
||||||
try {
|
try {
|
||||||
|
@ -0,0 +1,79 @@
|
|||||||
|
package net.corda.core.internal.verification
|
||||||
|
|
||||||
|
import net.corda.core.crypto.SecureHash
|
||||||
|
import net.corda.core.internal.mapNotNull
|
||||||
|
import net.corda.core.node.services.AttachmentFixup
|
||||||
|
import net.corda.core.node.services.AttachmentId
|
||||||
|
import net.corda.core.node.services.AttachmentStorage
|
||||||
|
import net.corda.core.transactions.TransactionBuilder
|
||||||
|
import net.corda.core.utilities.loggerFor
|
||||||
|
import java.net.JarURLConnection
|
||||||
|
import java.net.URL
|
||||||
|
|
||||||
|
class AttachmentFixups {
|
||||||
|
private val fixupRules = ArrayList<AttachmentFixup>()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads the "fixup" rules from all META-INF/Corda-Fixups files.
|
||||||
|
* These files have the following format:
|
||||||
|
* <AttachmentId>,<AttachmentId>...=><AttachmentId>,<AttachmentId>,...
|
||||||
|
* where each <AttachmentId> is the SHA256 of a CorDapp JAR that [TransactionBuilder] will expect to find inside [AttachmentStorage].
|
||||||
|
*
|
||||||
|
* These rules are for repairing broken CorDapps. A correctly written CorDapp should not require them.
|
||||||
|
*/
|
||||||
|
fun load(appClassLoader: ClassLoader) {
|
||||||
|
for (url in appClassLoader.resources("META-INF/Corda-Fixups")) {
|
||||||
|
val connection = toValidFixupResource(url) ?: continue
|
||||||
|
connection.inputStream.bufferedReader().lines().use { lines ->
|
||||||
|
lines.mapNotNull(::cleanLine).forEach { line ->
|
||||||
|
val tokens = line.split("=>")
|
||||||
|
require(tokens.size == 2) {
|
||||||
|
"Invalid fix-up line '$line' in '${connection.jarFile.name}'"
|
||||||
|
}
|
||||||
|
val sourceIds = parseIds(tokens[0])
|
||||||
|
require(sourceIds.isNotEmpty()) {
|
||||||
|
"Forbidden empty list of source attachment IDs in '${connection.jarFile.name}'"
|
||||||
|
}
|
||||||
|
val targetIds = parseIds(tokens[1])
|
||||||
|
fixupRules += AttachmentFixup(sourceIds, targetIds)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun toValidFixupResource(url: URL): JarURLConnection? {
|
||||||
|
val connection = url.openConnection() as? JarURLConnection ?: return null
|
||||||
|
val isValid = connection.jarFile.stream().allMatch { it.name.startsWith("META-INF/") }
|
||||||
|
if (!isValid) {
|
||||||
|
loggerFor<AttachmentFixups>().warn("FixUp '{}' contains files outside META-INF/ - IGNORING!", connection.jarFile.name)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
return connection
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun cleanLine(line: String): String? = line.substringBefore('#').trim().takeIf(String::isNotEmpty)
|
||||||
|
|
||||||
|
private fun parseIds(ids: String): Set<AttachmentId> {
|
||||||
|
return ids.splitToSequence(",")
|
||||||
|
.map(String::trim)
|
||||||
|
.filterNot(String::isEmpty)
|
||||||
|
.mapTo(LinkedHashSet(), SecureHash.Companion::create)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Apply this node's attachment fix-up rules to the given attachment IDs.
|
||||||
|
*
|
||||||
|
* @param attachmentIds A collection of [AttachmentId]s, e.g. as provided by a transaction.
|
||||||
|
* @return The [attachmentIds] with the fix-up rules applied.
|
||||||
|
*/
|
||||||
|
fun fixupAttachmentIds(attachmentIds: Collection<AttachmentId>): Set<AttachmentId> {
|
||||||
|
val replacementIds = LinkedHashSet(attachmentIds)
|
||||||
|
for ((sourceIds, targetIds) in fixupRules) {
|
||||||
|
if (replacementIds.containsAll(sourceIds)) {
|
||||||
|
replacementIds.removeAll(sourceIds)
|
||||||
|
replacementIds.addAll(targetIds)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return replacementIds
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,157 @@
|
|||||||
|
package net.corda.core.internal.verification
|
||||||
|
|
||||||
|
import net.corda.core.contracts.Attachment
|
||||||
|
import net.corda.core.contracts.ComponentGroupEnum
|
||||||
|
import net.corda.core.contracts.StateRef
|
||||||
|
import net.corda.core.contracts.TransactionResolutionException
|
||||||
|
import net.corda.core.crypto.SecureHash
|
||||||
|
import net.corda.core.identity.Party
|
||||||
|
import net.corda.core.internal.AttachmentTrustCalculator
|
||||||
|
import net.corda.core.internal.SerializedTransactionState
|
||||||
|
import net.corda.core.internal.TRUSTED_UPLOADERS
|
||||||
|
import net.corda.core.internal.getRequiredTransaction
|
||||||
|
import net.corda.core.node.NetworkParameters
|
||||||
|
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.node.services.vault.AttachmentQueryCriteria.AttachmentsQueryCriteria
|
||||||
|
import net.corda.core.node.services.vault.AttachmentSort
|
||||||
|
import net.corda.core.node.services.vault.AttachmentSort.AttachmentSortAttribute
|
||||||
|
import net.corda.core.node.services.vault.AttachmentSort.AttachmentSortColumn
|
||||||
|
import net.corda.core.node.services.vault.Builder
|
||||||
|
import net.corda.core.node.services.vault.Sort
|
||||||
|
import net.corda.core.serialization.deserialize
|
||||||
|
import net.corda.core.serialization.internal.AttachmentsClassLoaderBuilder
|
||||||
|
import net.corda.core.serialization.serialize
|
||||||
|
import net.corda.core.transactions.ContractUpgradeLedgerTransaction
|
||||||
|
import net.corda.core.transactions.ContractUpgradeWireTransaction
|
||||||
|
import net.corda.core.transactions.MissingContractAttachments
|
||||||
|
import net.corda.core.transactions.NotaryChangeLedgerTransaction
|
||||||
|
import net.corda.core.transactions.NotaryChangeWireTransaction
|
||||||
|
import net.corda.core.transactions.WireTransaction
|
||||||
|
import java.security.PublicKey
|
||||||
|
import java.util.jar.JarInputStream
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements [VerificationSupport] in terms of node-based services.
|
||||||
|
*/
|
||||||
|
interface VerificationService : VerificationSupport {
|
||||||
|
val transactionStorage: TransactionStorage
|
||||||
|
|
||||||
|
val identityService: IdentityService
|
||||||
|
|
||||||
|
val attachmentStorage: AttachmentStorage
|
||||||
|
|
||||||
|
val networkParametersService: NetworkParametersService
|
||||||
|
|
||||||
|
val attachmentTrustCalculator: AttachmentTrustCalculator
|
||||||
|
|
||||||
|
val attachmentFixups: AttachmentFixups
|
||||||
|
|
||||||
|
// TODO Bulk party lookup?
|
||||||
|
override fun getParties(keys: Collection<PublicKey>): List<Party?> = keys.map(identityService::partyFromKey)
|
||||||
|
|
||||||
|
override fun getAttachment(id: SecureHash): Attachment? = attachmentStorage.openAttachment(id)
|
||||||
|
|
||||||
|
override fun getNetworkParameters(id: SecureHash?): NetworkParameters? {
|
||||||
|
return networkParametersService.lookup(id ?: networkParametersService.defaultHash)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is the main logic that knows how to retrieve the binary representation of [StateRef]s.
|
||||||
|
*
|
||||||
|
* For [ContractUpgradeWireTransaction] or [NotaryChangeWireTransaction] it knows how to recreate the output state in the
|
||||||
|
* correct classloader independent of the node's classpath.
|
||||||
|
*/
|
||||||
|
override fun getSerializedState(stateRef: StateRef): SerializedTransactionState {
|
||||||
|
val coreTransaction = transactionStorage.getRequiredTransaction(stateRef.txhash).coreTransaction
|
||||||
|
return when (coreTransaction) {
|
||||||
|
is WireTransaction -> getRegularOutput(coreTransaction, stateRef.index)
|
||||||
|
is ContractUpgradeWireTransaction -> getContractUpdateOutput(coreTransaction, stateRef.index)
|
||||||
|
is NotaryChangeWireTransaction -> getNotaryChangeOutput(coreTransaction, stateRef.index)
|
||||||
|
else -> throw UnsupportedOperationException("Attempting to resolve input ${stateRef.index} of a ${coreTransaction.javaClass} " +
|
||||||
|
"transaction. This is not supported.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getRegularOutput(coreTransaction: WireTransaction, outputIndex: Int): SerializedTransactionState {
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
return coreTransaction.componentGroups
|
||||||
|
.first { it.groupIndex == ComponentGroupEnum.OUTPUTS_GROUP.ordinal }
|
||||||
|
.components[outputIndex] as SerializedTransactionState
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a binary serialized component for a virtual output state serialised and executed with the attachments from the transaction.
|
||||||
|
*/
|
||||||
|
@Suppress("ThrowsCount")
|
||||||
|
private fun getContractUpdateOutput(wtx: ContractUpgradeWireTransaction, outputIndex: Int): SerializedTransactionState {
|
||||||
|
val binaryInput = getSerializedState(wtx.inputs[outputIndex])
|
||||||
|
val legacyContractAttachment = getAttachment(wtx.legacyContractAttachmentId) ?: throw MissingContractAttachments(emptyList())
|
||||||
|
val upgradedContractAttachment = getAttachment(wtx.upgradedContractAttachmentId) ?: throw MissingContractAttachments(emptyList())
|
||||||
|
val networkParameters = getNetworkParameters(wtx.networkParametersHash) ?: throw TransactionResolutionException(wtx.id)
|
||||||
|
|
||||||
|
return AttachmentsClassLoaderBuilder.withAttachmentsClassloaderContext(
|
||||||
|
listOf(legacyContractAttachment, upgradedContractAttachment),
|
||||||
|
networkParameters,
|
||||||
|
wtx.id,
|
||||||
|
::isAttachmentTrusted,
|
||||||
|
attachmentsClassLoaderCache = attachmentsClassLoaderCache
|
||||||
|
) { serializationContext ->
|
||||||
|
val upgradedContract = ContractUpgradeLedgerTransaction.loadUpgradedContract(wtx.upgradedContractClassName, wtx.id, serializationContext.deserializationClassLoader)
|
||||||
|
val outputState = ContractUpgradeWireTransaction.calculateUpgradedState(binaryInput.deserialize(), upgradedContract, upgradedContractAttachment)
|
||||||
|
outputState.serialize()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This should return a serialized virtual output state, that will be used to verify spending transactions.
|
||||||
|
* The binary output should not depend on the classpath of the node that is verifying the transaction.
|
||||||
|
*
|
||||||
|
* Ideally the serialization engine would support partial deserialization so that only the Notary ( and the encumbrance can be replaced
|
||||||
|
* from the binary input state)
|
||||||
|
*/
|
||||||
|
// TODO - currently this uses the main classloader.
|
||||||
|
private fun getNotaryChangeOutput(wtx: NotaryChangeWireTransaction, outputIndex: Int): SerializedTransactionState {
|
||||||
|
val input = getStateAndRef(wtx.inputs[outputIndex])
|
||||||
|
val output = NotaryChangeLedgerTransaction.computeOutput(input, wtx.newNotary) { wtx.inputs }
|
||||||
|
return output.serialize()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scans trusted (installed locally) attachments to find all that contain the [className].
|
||||||
|
* This is required as a workaround until explicit cordapp dependencies are implemented.
|
||||||
|
*
|
||||||
|
* @return the attachments with the highest version.
|
||||||
|
*/
|
||||||
|
// TODO Should throw when the class is found in multiple contract attachments (not different versions).
|
||||||
|
override fun getTrustedClassAttachment(className: String): Attachment? {
|
||||||
|
val allTrusted = attachmentStorage.queryAttachments(
|
||||||
|
AttachmentsQueryCriteria().withUploader(Builder.`in`(TRUSTED_UPLOADERS)),
|
||||||
|
AttachmentSort(listOf(AttachmentSortColumn(AttachmentSortAttribute.VERSION, Sort.Direction.DESC)))
|
||||||
|
)
|
||||||
|
|
||||||
|
// TODO - add caching if performance is affected.
|
||||||
|
for (attId in allTrusted) {
|
||||||
|
val attch = attachmentStorage.openAttachment(attId)!!
|
||||||
|
if (attch.openAsJAR().use { hasFile(it, "$className.class") }) return attch
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun hasFile(jarStream: JarInputStream, className: String): Boolean {
|
||||||
|
while (true) {
|
||||||
|
val e = jarStream.nextJarEntry ?: return false
|
||||||
|
if (e.name == className) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun isAttachmentTrusted(attachment: Attachment): Boolean = attachmentTrustCalculator.calculate(attachment)
|
||||||
|
|
||||||
|
override fun fixupAttachmentIds(attachmentIds: Collection<SecureHash>): Set<SecureHash> {
|
||||||
|
return attachmentFixups.fixupAttachmentIds(attachmentIds)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,50 @@
|
|||||||
|
package net.corda.core.internal.verification
|
||||||
|
|
||||||
|
import net.corda.core.contracts.Attachment
|
||||||
|
import net.corda.core.contracts.StateAndRef
|
||||||
|
import net.corda.core.contracts.StateRef
|
||||||
|
import net.corda.core.crypto.SecureHash
|
||||||
|
import net.corda.core.identity.Party
|
||||||
|
import net.corda.core.internal.SerializedTransactionState
|
||||||
|
import net.corda.core.node.NetworkParameters
|
||||||
|
import net.corda.core.serialization.SerializationContext
|
||||||
|
import net.corda.core.serialization.deserialize
|
||||||
|
import net.corda.core.serialization.internal.AttachmentsClassLoaderCache
|
||||||
|
import net.corda.core.transactions.LedgerTransaction
|
||||||
|
import net.corda.core.transactions.defaultVerifier
|
||||||
|
import java.security.PublicKey
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents the operations required to resolve and verify a transaction.
|
||||||
|
*/
|
||||||
|
interface VerificationSupport {
|
||||||
|
val isResolutionLazy: Boolean get() = true
|
||||||
|
|
||||||
|
val appClassLoader: ClassLoader
|
||||||
|
|
||||||
|
val attachmentsClassLoaderCache: AttachmentsClassLoaderCache? get() = null
|
||||||
|
|
||||||
|
// TODO Use SequencedCollection if upgraded to Java 21
|
||||||
|
fun getParties(keys: Collection<PublicKey>): List<Party?>
|
||||||
|
|
||||||
|
fun getAttachment(id: SecureHash): Attachment?
|
||||||
|
|
||||||
|
// TODO Use SequencedCollection if upgraded to Java 21
|
||||||
|
fun getAttachments(ids: Collection<SecureHash>): List<Attachment?> = ids.map(::getAttachment)
|
||||||
|
|
||||||
|
fun isAttachmentTrusted(attachment: Attachment): Boolean
|
||||||
|
|
||||||
|
fun getTrustedClassAttachment(className: String): Attachment?
|
||||||
|
|
||||||
|
fun getNetworkParameters(id: SecureHash?): NetworkParameters?
|
||||||
|
|
||||||
|
fun getSerializedState(stateRef: StateRef): SerializedTransactionState
|
||||||
|
|
||||||
|
fun getStateAndRef(stateRef: StateRef): StateAndRef<*> = StateAndRef(getSerializedState(stateRef).deserialize(), stateRef)
|
||||||
|
|
||||||
|
fun fixupAttachmentIds(attachmentIds: Collection<SecureHash>): Set<SecureHash>
|
||||||
|
|
||||||
|
fun createVerifier(ltx: LedgerTransaction, serializationContext: SerializationContext): Verifier {
|
||||||
|
return defaultVerifier(ltx, serializationContext)
|
||||||
|
}
|
||||||
|
}
|
@ -1,7 +1,5 @@
|
|||||||
package net.corda.core.internal
|
package net.corda.core.internal.verification
|
||||||
|
|
||||||
import net.corda.core.concurrent.CordaFuture
|
|
||||||
import net.corda.core.contracts.Attachment
|
|
||||||
import net.corda.core.contracts.Contract
|
import net.corda.core.contracts.Contract
|
||||||
import net.corda.core.contracts.ContractAttachment
|
import net.corda.core.contracts.ContractAttachment
|
||||||
import net.corda.core.contracts.ContractClassName
|
import net.corda.core.contracts.ContractClassName
|
||||||
@ -30,21 +28,26 @@ import net.corda.core.contracts.TransactionVerificationException.TransactionNota
|
|||||||
import net.corda.core.contracts.TransactionVerificationException.TransactionRequiredContractUnspecifiedException
|
import net.corda.core.contracts.TransactionVerificationException.TransactionRequiredContractUnspecifiedException
|
||||||
import net.corda.core.crypto.CompositeKey
|
import net.corda.core.crypto.CompositeKey
|
||||||
import net.corda.core.crypto.SecureHash
|
import net.corda.core.crypto.SecureHash
|
||||||
|
import net.corda.core.internal.AttachmentWithContext
|
||||||
|
import net.corda.core.internal.MAX_NUMBER_OF_KEYS_IN_SIGNATURE_CONSTRAINT
|
||||||
|
import net.corda.core.internal.PlatformVersionSwitches
|
||||||
|
import net.corda.core.internal.canBeTransitionedFrom
|
||||||
|
import net.corda.core.internal.checkConstraintValidity
|
||||||
|
import net.corda.core.internal.checkMinimumPlatformVersion
|
||||||
|
import net.corda.core.internal.checkNotaryWhitelisted
|
||||||
|
import net.corda.core.internal.checkSupportedHashType
|
||||||
|
import net.corda.core.internal.contractHasAutomaticConstraintPropagation
|
||||||
|
import net.corda.core.internal.loadClassOfType
|
||||||
|
import net.corda.core.internal.mapToSet
|
||||||
|
import net.corda.core.internal.requiredContractClassName
|
||||||
import net.corda.core.internal.rules.StateContractValidationEnforcementRule
|
import net.corda.core.internal.rules.StateContractValidationEnforcementRule
|
||||||
|
import net.corda.core.internal.warnContractWithoutConstraintPropagation
|
||||||
|
import net.corda.core.internal.warnOnce
|
||||||
import net.corda.core.transactions.LedgerTransaction
|
import net.corda.core.transactions.LedgerTransaction
|
||||||
import net.corda.core.utilities.loggerFor
|
import net.corda.core.utilities.loggerFor
|
||||||
import java.util.function.Function
|
import java.util.function.Function
|
||||||
import java.util.function.Supplier
|
import java.util.function.Supplier
|
||||||
|
|
||||||
interface TransactionVerifierServiceInternal {
|
|
||||||
fun reverifyWithFixups(transaction: LedgerTransaction, missingClass: String?): CordaFuture<*>
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Defined here for visibility reasons.
|
|
||||||
*/
|
|
||||||
fun LedgerTransaction.prepareVerify(attachments: List<Attachment>) = internalPrepareVerify(attachments)
|
|
||||||
|
|
||||||
interface Verifier {
|
interface Verifier {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -142,10 +145,12 @@ private class Validator(private val ltx: LedgerTransaction, private val transact
|
|||||||
*/
|
*/
|
||||||
@Suppress("ThrowsCount")
|
@Suppress("ThrowsCount")
|
||||||
private fun getUniqueContractAttachmentsByContract(): Map<ContractClassName, ContractAttachment> {
|
private fun getUniqueContractAttachmentsByContract(): Map<ContractClassName, ContractAttachment> {
|
||||||
val contractClasses = allStates.mapTo(LinkedHashSet(), TransactionState<*>::contract)
|
val contractClasses = allStates.mapToSet { it.contract }
|
||||||
|
|
||||||
// Check that there are no duplicate attachments added.
|
// Check that there are no duplicate attachments added.
|
||||||
if (ltx.attachments.size != ltx.attachments.toSet().size) throw DuplicateAttachmentsRejection(ltx.id, ltx.attachments.groupBy { it }.filterValues { it.size > 1 }.keys.first())
|
if (ltx.attachments.size != ltx.attachments.toSet().size) {
|
||||||
|
throw DuplicateAttachmentsRejection(ltx.id, ltx.attachments.groupBy { it }.filterValues { it.size > 1 }.keys.first())
|
||||||
|
}
|
||||||
|
|
||||||
// For each attachment this finds all the relevant state contracts that it provides.
|
// For each attachment this finds all the relevant state contracts that it provides.
|
||||||
// And then maps them to the attachment.
|
// And then maps them to the attachment.
|
||||||
@ -393,7 +398,7 @@ private class Validator(private val ltx: LedgerTransaction, private val transact
|
|||||||
@Suppress("NestedBlockDepth", "MagicNumber")
|
@Suppress("NestedBlockDepth", "MagicNumber")
|
||||||
private fun verifyConstraints(contractAttachmentsByContract: Map<ContractClassName, ContractAttachment>) {
|
private fun verifyConstraints(contractAttachmentsByContract: Map<ContractClassName, ContractAttachment>) {
|
||||||
// For each contract/constraint pair check that the relevant attachment is valid.
|
// For each contract/constraint pair check that the relevant attachment is valid.
|
||||||
allStates.mapTo(LinkedHashSet()) { it.contract to it.constraint }.forEach { (contract, constraint) ->
|
allStates.mapToSet { it.contract to it.constraint }.forEach { (contract, constraint) ->
|
||||||
if (constraint is SignatureAttachmentConstraint) {
|
if (constraint is SignatureAttachmentConstraint) {
|
||||||
/**
|
/**
|
||||||
* Support for signature constraints has been added on
|
* Support for signature constraints has been added on
|
||||||
@ -435,12 +440,11 @@ private class Validator(private val ltx: LedgerTransaction, private val transact
|
|||||||
* Verify the given [LedgerTransaction]. This includes validating
|
* Verify the given [LedgerTransaction]. This includes validating
|
||||||
* its contents, as well as executing all of its smart contracts.
|
* its contents, as well as executing all of its smart contracts.
|
||||||
*/
|
*/
|
||||||
@Suppress("TooGenericExceptionCaught")
|
|
||||||
class TransactionVerifier(private val transactionClassLoader: ClassLoader) : Function<Supplier<LedgerTransaction>, Unit> {
|
class TransactionVerifier(private val transactionClassLoader: ClassLoader) : Function<Supplier<LedgerTransaction>, Unit> {
|
||||||
// Loads the contract class from the transactionClassLoader.
|
// Loads the contract class from the transactionClassLoader.
|
||||||
private fun createContractClass(id: SecureHash, contractClassName: ContractClassName): Class<out Contract> {
|
private fun createContractClass(id: SecureHash, contractClassName: ContractClassName): Class<out Contract> {
|
||||||
return try {
|
return try {
|
||||||
Class.forName(contractClassName, false, transactionClassLoader).asSubclass(Contract::class.java)
|
loadClassOfType<Contract>(contractClassName, false, transactionClassLoader)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
throw ContractCreationError(id, contractClassName, e)
|
throw ContractCreationError(id, contractClassName, e)
|
||||||
}
|
}
|
||||||
@ -448,7 +452,7 @@ class TransactionVerifier(private val transactionClassLoader: ClassLoader) : Fun
|
|||||||
|
|
||||||
private fun generateContracts(ltx: LedgerTransaction): List<Contract> {
|
private fun generateContracts(ltx: LedgerTransaction): List<Contract> {
|
||||||
return (ltx.inputs.map(StateAndRef<ContractState>::state) + ltx.outputs)
|
return (ltx.inputs.map(StateAndRef<ContractState>::state) + ltx.outputs)
|
||||||
.mapTo(LinkedHashSet(), TransactionState<*>::contract)
|
.mapToSet { it.contract }
|
||||||
.map { contractClassName ->
|
.map { contractClassName ->
|
||||||
createContractClass(ltx.id, contractClassName)
|
createContractClass(ltx.id, contractClassName)
|
||||||
}.map { contractClass ->
|
}.map { contractClass ->
|
@ -0,0 +1,102 @@
|
|||||||
|
package net.corda.core.internal.verification
|
||||||
|
|
||||||
|
import net.corda.core.contracts.Attachment
|
||||||
|
import net.corda.core.contracts.AttachmentResolutionException
|
||||||
|
import net.corda.core.contracts.ContractAttachment
|
||||||
|
import net.corda.core.contracts.ContractState
|
||||||
|
import net.corda.core.contracts.StateAndRef
|
||||||
|
import net.corda.core.contracts.StateRef
|
||||||
|
import net.corda.core.contracts.TransactionState
|
||||||
|
import net.corda.core.crypto.SecureHash
|
||||||
|
import net.corda.core.internal.cordapp.CordappProviderInternal
|
||||||
|
import net.corda.core.internal.getRequiredTransaction
|
||||||
|
import net.corda.core.node.ServiceHub
|
||||||
|
import net.corda.core.node.ServicesForResolution
|
||||||
|
import net.corda.core.node.services.AttachmentStorage
|
||||||
|
import net.corda.core.node.services.TransactionStorage
|
||||||
|
import net.corda.core.serialization.deserialize
|
||||||
|
import net.corda.core.transactions.ContractUpgradeWireTransaction
|
||||||
|
import net.corda.core.transactions.NotaryChangeWireTransaction
|
||||||
|
import net.corda.core.transactions.SignedTransaction
|
||||||
|
import net.corda.core.transactions.WireTransaction
|
||||||
|
|
||||||
|
@Suppress("TooManyFunctions", "ThrowsCount")
|
||||||
|
interface VerifyingServiceHub : ServiceHub, VerificationService {
|
||||||
|
override val cordappProvider: CordappProviderInternal
|
||||||
|
|
||||||
|
override val transactionStorage: TransactionStorage get() = validatedTransactions
|
||||||
|
|
||||||
|
override val attachmentStorage: AttachmentStorage get() = attachments
|
||||||
|
|
||||||
|
override val appClassLoader: ClassLoader get() = cordappProvider.appClassLoader
|
||||||
|
|
||||||
|
override val attachmentFixups: AttachmentFixups get() = cordappProvider.attachmentFixups
|
||||||
|
|
||||||
|
override fun loadContractAttachment(stateRef: StateRef): Attachment {
|
||||||
|
// We may need to recursively chase transactions if there are notary changes.
|
||||||
|
return loadContractAttachment(stateRef, null)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun loadContractAttachment(stateRef: StateRef, forContractClassName: String?): Attachment {
|
||||||
|
val stx = getRequiredTransaction(stateRef.txhash)
|
||||||
|
return when (val ctx = stx.coreTransaction) {
|
||||||
|
is WireTransaction -> {
|
||||||
|
val contractClassName = forContractClassName ?: ctx.outRef<ContractState>(stateRef.index).state.contract
|
||||||
|
ctx.attachments
|
||||||
|
.asSequence()
|
||||||
|
.mapNotNull { id -> loadAttachmentContainingContract(id, contractClassName) }
|
||||||
|
.firstOrNull() ?: throw AttachmentResolutionException(stateRef.txhash)
|
||||||
|
}
|
||||||
|
is ContractUpgradeWireTransaction -> {
|
||||||
|
attachments.openAttachment(ctx.upgradedContractAttachmentId) ?: throw AttachmentResolutionException(stateRef.txhash)
|
||||||
|
}
|
||||||
|
is NotaryChangeWireTransaction -> {
|
||||||
|
val transactionState = getSerializedState(stateRef).deserialize()
|
||||||
|
val input = ctx.inputs.firstOrNull() ?: throw AttachmentResolutionException(stateRef.txhash)
|
||||||
|
loadContractAttachment(input, transactionState.contract)
|
||||||
|
}
|
||||||
|
else -> throw UnsupportedOperationException("Attempting to resolve attachment for index ${stateRef.index} of a " +
|
||||||
|
"${ctx.javaClass} transaction. This is not supported.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun loadAttachmentContainingContract(id: SecureHash, contractClassName: String): Attachment? {
|
||||||
|
return attachments.openAttachment(id)?.takeIf { it is ContractAttachment && contractClassName in it.allContracts }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun loadState(stateRef: StateRef): TransactionState<*> = getSerializedState(stateRef).deserialize()
|
||||||
|
|
||||||
|
override fun loadStates(stateRefs: Set<StateRef>): Set<StateAndRef<ContractState>> = loadStatesInternal(stateRefs, LinkedHashSet())
|
||||||
|
|
||||||
|
fun <T : ContractState, C : MutableCollection<StateAndRef<T>>> loadStatesInternal(input: Iterable<StateRef>, output: C): C {
|
||||||
|
return input.mapTo(output, ::toStateAndRef)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Try to verify the given transaction on the external verifier, assuming it is available. It is not required to verify externally even
|
||||||
|
* if the verifier is available.
|
||||||
|
*
|
||||||
|
* The default implementation is to only do internal verification.
|
||||||
|
*
|
||||||
|
* @return true if the transaction should (also) be verified internally, regardless of whether it was verified externally.
|
||||||
|
*/
|
||||||
|
fun tryExternalVerification(stx: SignedTransaction, checkSufficientSignatures: Boolean): Boolean {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun ServicesForResolution.toVerifyingServiceHub(): VerifyingServiceHub {
|
||||||
|
if (this is VerifyingServiceHub) {
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
// All ServicesForResolution instances should also implement VerifyingServiceHub, which is something we can enforce with the
|
||||||
|
// @DoNotImplement annotation. The only exception however is MockServices, which does not since it's public API and VerifyingServiceHub
|
||||||
|
// is internal. Instead, MockServices has a private VerifyingServiceHub "view" which we get at via reflection.
|
||||||
|
var clazz: Class<*> = javaClass
|
||||||
|
while (true) {
|
||||||
|
if (clazz.name == "net.corda.testing.node.MockServices") {
|
||||||
|
return clazz.getDeclaredMethod("getVerifyingView").apply { isAccessible = true }.invoke(this) as VerifyingServiceHub
|
||||||
|
}
|
||||||
|
clazz = clazz.superclass ?: throw ClassCastException("${javaClass.name} is not a VerifyingServiceHub")
|
||||||
|
}
|
||||||
|
}
|
@ -11,6 +11,7 @@ import net.corda.core.crypto.TransactionSignature
|
|||||||
import net.corda.core.flows.ContractUpgradeFlow
|
import net.corda.core.flows.ContractUpgradeFlow
|
||||||
import net.corda.core.internal.PlatformVersionSwitches.TWO_PHASE_FINALITY
|
import net.corda.core.internal.PlatformVersionSwitches.TWO_PHASE_FINALITY
|
||||||
import net.corda.core.internal.telemetry.TelemetryComponent
|
import net.corda.core.internal.telemetry.TelemetryComponent
|
||||||
|
import net.corda.core.internal.uncheckedCast
|
||||||
import net.corda.core.node.services.*
|
import net.corda.core.node.services.*
|
||||||
import net.corda.core.node.services.diagnostics.DiagnosticsService
|
import net.corda.core.node.services.diagnostics.DiagnosticsService
|
||||||
import net.corda.core.serialization.CordaSerializable
|
import net.corda.core.serialization.CordaSerializable
|
||||||
@ -81,7 +82,6 @@ interface ServicesForResolution {
|
|||||||
/**
|
/**
|
||||||
* Provides a callback for the Node to customise the [LedgerTransaction].
|
* Provides a callback for the Node to customise the [LedgerTransaction].
|
||||||
*/
|
*/
|
||||||
@JvmDefault
|
|
||||||
fun specialise(ltx: LedgerTransaction): LedgerTransaction = ltx
|
fun specialise(ltx: LedgerTransaction): LedgerTransaction = ltx
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -171,12 +171,6 @@ interface ServiceHub : ServicesForResolution {
|
|||||||
*/
|
*/
|
||||||
val telemetryService: TelemetryService
|
val telemetryService: TelemetryService
|
||||||
|
|
||||||
/**
|
|
||||||
* INTERNAL. DO NOT USE.
|
|
||||||
* @suppress
|
|
||||||
*/
|
|
||||||
val transactionVerifierService: TransactionVerifierService
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A [Clock] representing the node's current time. This should be used in preference to directly accessing the
|
* A [Clock] representing the node's current time. This should be used in preference to directly accessing the
|
||||||
* clock so the current time can be controlled during unit testing.
|
* clock so the current time can be controlled during unit testing.
|
||||||
@ -284,8 +278,7 @@ interface ServiceHub : ServicesForResolution {
|
|||||||
*/
|
*/
|
||||||
@Throws(TransactionResolutionException::class)
|
@Throws(TransactionResolutionException::class)
|
||||||
fun <T : ContractState> toStateAndRef(stateRef: StateRef): StateAndRef<T> {
|
fun <T : ContractState> toStateAndRef(stateRef: StateRef): StateAndRef<T> {
|
||||||
val stx = validatedTransactions.getTransaction(stateRef.txhash) ?: throw TransactionResolutionException(stateRef.txhash)
|
return StateAndRef(uncheckedCast(loadState(stateRef)), stateRef)
|
||||||
return stx.resolveBaseTransaction(this).outRef(stateRef.index)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private val legalIdentityKey: PublicKey get() = this.myInfo.legalIdentitiesAndCerts.first().owningKey
|
private val legalIdentityKey: PublicKey get() = this.myInfo.legalIdentitiesAndCerts.first().owningKey
|
||||||
@ -425,8 +418,8 @@ interface ServiceHub : ServicesForResolution {
|
|||||||
* When used within a flow, this session automatically forms part of the enclosing flow transaction boundary,
|
* When used within a flow, this session automatically forms part of the enclosing flow transaction boundary,
|
||||||
* and thus queryable data will include everything committed as of the last checkpoint.
|
* and thus queryable data will include everything committed as of the last checkpoint.
|
||||||
*
|
*
|
||||||
* We want to make sure users have a restricted access to administrative functions, this function will return a [RestrictedConnection] instance.
|
* We want to make sure users have a restricted access to administrative functions, this function will return a [Connection] instance
|
||||||
* The following methods are blocked:
|
* with the following methods blocked:
|
||||||
* - abort(executor: Executor?)
|
* - abort(executor: Executor?)
|
||||||
* - clearWarnings()
|
* - clearWarnings()
|
||||||
* - close()
|
* - close()
|
||||||
|
@ -1,18 +0,0 @@
|
|||||||
package net.corda.core.node.services
|
|
||||||
|
|
||||||
import net.corda.core.DoNotImplement
|
|
||||||
import net.corda.core.concurrent.CordaFuture
|
|
||||||
import net.corda.core.transactions.LedgerTransaction
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Provides verification service. The implementation may be a simple in-memory verify() call or perhaps an IPC/RPC.
|
|
||||||
* @suppress
|
|
||||||
*/
|
|
||||||
@DoNotImplement
|
|
||||||
interface TransactionVerifierService {
|
|
||||||
/**
|
|
||||||
* @param transaction The transaction to be verified.
|
|
||||||
* @return A future that completes successfully if the transaction verified, or sets an exception the verifier threw.
|
|
||||||
*/
|
|
||||||
fun verify(transaction: LedgerTransaction): CordaFuture<*>
|
|
||||||
}
|
|
@ -308,9 +308,9 @@ class Vault<out T : ContractState>(val states: Iterable<StateAndRef<T>>) {
|
|||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
@Deprecated("No longer used. The vault does not emit empty updates")
|
@Deprecated("No longer used. The vault does not emit empty updates")
|
||||||
val NoUpdate = Update(emptySet(), emptySet(), type = UpdateType.GENERAL, references = emptySet())
|
val NoUpdate = Update<ContractState>(emptySet(), emptySet(), type = UpdateType.GENERAL, references = emptySet())
|
||||||
@Deprecated("No longer used. The vault does not emit empty updates")
|
@Deprecated("No longer used. The vault does not emit empty updates")
|
||||||
val NoNotaryUpdate = Update(emptySet(), emptySet(), type = UpdateType.NOTARY_CHANGE, references = emptySet())
|
val NoNotaryUpdate = Update<ContractState>(emptySet(), emptySet(), type = UpdateType.NOTARY_CHANGE, references = emptySet())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -33,7 +33,6 @@ class ResilientSubscriber<T>(actual: Subscriber<in T>) : SafeSubscriber<T>(actua
|
|||||||
* It only delegates to [SafeSubscriber.onError] if it wraps an [ActionSubscriber] which is
|
* It only delegates to [SafeSubscriber.onError] if it wraps an [ActionSubscriber] which is
|
||||||
* a leaf in an Subscribers' tree structure.
|
* a leaf in an Subscribers' tree structure.
|
||||||
*/
|
*/
|
||||||
@Suppress("TooGenericExceptionCaught")
|
|
||||||
override fun onNext(t: T) {
|
override fun onNext(t: T) {
|
||||||
try {
|
try {
|
||||||
actual.onNext(t)
|
actual.onNext(t)
|
||||||
@ -62,7 +61,6 @@ class ResilientSubscriber<T>(actual: Subscriber<in T>) : SafeSubscriber<T>(actua
|
|||||||
/**
|
/**
|
||||||
* Duplicate of [SafeSubscriber._onError]. However, it will not call [Subscriber.unsubscribe].
|
* Duplicate of [SafeSubscriber._onError]. However, it will not call [Subscriber.unsubscribe].
|
||||||
*/
|
*/
|
||||||
@Suppress("TooGenericExceptionCaught")
|
|
||||||
override fun _onError(e: Throwable) {
|
override fun _onError(e: Throwable) {
|
||||||
@Suppress("DEPRECATION")
|
@Suppress("DEPRECATION")
|
||||||
RxJavaPlugins.getInstance().errorHandler.handleError(e)
|
RxJavaPlugins.getInstance().errorHandler.handleError(e)
|
||||||
|
@ -8,8 +8,8 @@ import net.corda.core.contracts.TransactionVerificationException
|
|||||||
import net.corda.core.contracts.TransactionVerificationException.OverlappingAttachmentsException
|
import net.corda.core.contracts.TransactionVerificationException.OverlappingAttachmentsException
|
||||||
import net.corda.core.contracts.TransactionVerificationException.PackageOwnershipException
|
import net.corda.core.contracts.TransactionVerificationException.PackageOwnershipException
|
||||||
import net.corda.core.crypto.SecureHash
|
import net.corda.core.crypto.SecureHash
|
||||||
import net.corda.core.internal.JDK1_2_CLASS_FILE_FORMAT_MAJOR_VERSION
|
import net.corda.core.internal.JAVA_17_CLASS_FILE_FORMAT_MAJOR_VERSION
|
||||||
import net.corda.core.internal.JDK8_CLASS_FILE_FORMAT_MAJOR_VERSION
|
import net.corda.core.internal.JAVA_1_2_CLASS_FILE_FORMAT_MAJOR_VERSION
|
||||||
import net.corda.core.internal.JarSignatureCollector
|
import net.corda.core.internal.JarSignatureCollector
|
||||||
import net.corda.core.internal.NamedCacheFactory
|
import net.corda.core.internal.NamedCacheFactory
|
||||||
import net.corda.core.internal.PlatformVersionSwitches
|
import net.corda.core.internal.PlatformVersionSwitches
|
||||||
@ -118,17 +118,12 @@ class AttachmentsClassLoader(attachments: List<Attachment>,
|
|||||||
// Reset the value to prevent Error due to a factory already defined
|
// Reset the value to prevent Error due to a factory already defined
|
||||||
factoryField.set(null, null)
|
factoryField.set(null, null)
|
||||||
// Set our custom factory and wrap the current one into it
|
// Set our custom factory and wrap the current one into it
|
||||||
URL.setURLStreamHandlerFactory(
|
URL.setURLStreamHandlerFactory { protocol ->
|
||||||
// Set the factory to a decorator
|
|
||||||
object : URLStreamHandlerFactory {
|
|
||||||
// route between our own and the pre-existing factory
|
// route between our own and the pre-existing factory
|
||||||
override fun createURLStreamHandler(protocol: String): URLStreamHandler? {
|
AttachmentURLStreamHandlerFactory.createURLStreamHandler(protocol)
|
||||||
return AttachmentURLStreamHandlerFactory.createURLStreamHandler(protocol)
|
|
||||||
?: existingFactory.createURLStreamHandler(protocol)
|
?: existingFactory.createURLStreamHandler(protocol)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -158,9 +153,7 @@ class AttachmentsClassLoader(attachments: List<Attachment>,
|
|||||||
checkAttachments(attachments)
|
checkAttachments(attachments)
|
||||||
}
|
}
|
||||||
|
|
||||||
private class AttachmentHashContext(
|
private class AttachmentHashContext(val buffer: ByteArray = ByteArray(DEFAULT_BUFFER_SIZE))
|
||||||
val txId: SecureHash,
|
|
||||||
val buffer: ByteArray = ByteArray(DEFAULT_BUFFER_SIZE))
|
|
||||||
|
|
||||||
private fun hash(inputStream : InputStream, ctx : AttachmentHashContext) : SecureHash.SHA256 {
|
private fun hash(inputStream : InputStream, ctx : AttachmentHashContext) : SecureHash.SHA256 {
|
||||||
val md = MessageDigest.getInstance(SecureHash.SHA2_256)
|
val md = MessageDigest.getInstance(SecureHash.SHA2_256)
|
||||||
@ -189,7 +182,7 @@ class AttachmentsClassLoader(attachments: List<Attachment>,
|
|||||||
// This function attempts to strike a balance between security and usability when it comes to the no-overlap rule.
|
// This function attempts to strike a balance between security and usability when it comes to the no-overlap rule.
|
||||||
// TODO - investigate potential exploits.
|
// TODO - investigate potential exploits.
|
||||||
private fun shouldCheckForNoOverlap(path: String, targetPlatformVersion: Int): Boolean {
|
private fun shouldCheckForNoOverlap(path: String, targetPlatformVersion: Int): Boolean {
|
||||||
require(path.toLowerCase() == path)
|
require(path.lowercase() == path)
|
||||||
require(!path.contains('\\'))
|
require(!path.contains('\\'))
|
||||||
|
|
||||||
return when {
|
return when {
|
||||||
@ -234,7 +227,7 @@ class AttachmentsClassLoader(attachments: List<Attachment>,
|
|||||||
// claim their parts of the Java package namespace via registration with the zone operator.
|
// claim their parts of the Java package namespace via registration with the zone operator.
|
||||||
|
|
||||||
val classLoaderEntries = mutableMapOf<String, SecureHash>()
|
val classLoaderEntries = mutableMapOf<String, SecureHash>()
|
||||||
val ctx = AttachmentHashContext(sampleTxId)
|
val ctx = AttachmentHashContext()
|
||||||
for (attachment in attachments) {
|
for (attachment in attachments) {
|
||||||
// We may have been given an attachment loaded from the database in which case, important info like
|
// We may have been given an attachment loaded from the database in which case, important info like
|
||||||
// signers is already calculated.
|
// signers is already calculated.
|
||||||
@ -270,7 +263,7 @@ class AttachmentsClassLoader(attachments: List<Attachment>,
|
|||||||
// filesystem tries to be case insensitive. This may break developers who attempt to use ProGuard.
|
// filesystem tries to be case insensitive. This may break developers who attempt to use ProGuard.
|
||||||
//
|
//
|
||||||
// Also convert to Unix path separators as all resource/class lookups will expect this.
|
// Also convert to Unix path separators as all resource/class lookups will expect this.
|
||||||
val path = entry.name.toLowerCase(Locale.US).replace('\\', '/')
|
val path = entry.name.lowercase(Locale.US).replace('\\', '/')
|
||||||
|
|
||||||
// Namespace ownership. We only check class files: resources are loaded relative to a JAR anyway.
|
// Namespace ownership. We only check class files: resources are loaded relative to a JAR anyway.
|
||||||
if (path.endsWith(".class")) {
|
if (path.endsWith(".class")) {
|
||||||
@ -285,7 +278,7 @@ class AttachmentsClassLoader(attachments: List<Attachment>,
|
|||||||
for ((namespace, pubkey) in params.packageOwnership) {
|
for ((namespace, pubkey) in params.packageOwnership) {
|
||||||
// Note that due to the toLowerCase() call above, we'll be comparing against a lowercased
|
// Note that due to the toLowerCase() call above, we'll be comparing against a lowercased
|
||||||
// version of the ownership claim.
|
// version of the ownership claim.
|
||||||
val ns = namespace.toLowerCase(Locale.US)
|
val ns = namespace.lowercase(Locale.US)
|
||||||
// We need an additional . to avoid matching com.foo.Widget against com.foobar.Zap
|
// We need an additional . to avoid matching com.foo.Widget against com.foobar.Zap
|
||||||
if (pkgName == ns || pkgName.startsWith("$ns.")) {
|
if (pkgName == ns || pkgName.startsWith("$ns.")) {
|
||||||
if (pubkey !in signers)
|
if (pubkey !in signers)
|
||||||
@ -358,12 +351,12 @@ object AttachmentsClassLoaderBuilder {
|
|||||||
val attachmentIds = attachments.mapTo(LinkedHashSet(), Attachment::id)
|
val attachmentIds = attachments.mapTo(LinkedHashSet(), Attachment::id)
|
||||||
|
|
||||||
val cache = attachmentsClassLoaderCache ?: fallBackCache
|
val cache = attachmentsClassLoaderCache ?: fallBackCache
|
||||||
val cachedSerializationContext = cache.computeIfAbsent(AttachmentsClassLoaderKey(attachmentIds, params), Function { key ->
|
val cachedSerializationContext = cache.computeIfAbsent(AttachmentsClassLoaderKey(attachmentIds, params)) { key ->
|
||||||
// Create classloader and load serializers, whitelisted classes
|
// Create classloader and load serializers, whitelisted classes
|
||||||
val transactionClassLoader = AttachmentsClassLoader(attachments, key.params, txId, isAttachmentTrusted, parent)
|
val transactionClassLoader = AttachmentsClassLoader(attachments, key.params, txId, isAttachmentTrusted, parent)
|
||||||
val serializers = try {
|
val serializers = try {
|
||||||
createInstancesOfClassesImplementing(transactionClassLoader, SerializationCustomSerializer::class.java,
|
createInstancesOfClassesImplementing(transactionClassLoader, SerializationCustomSerializer::class.java,
|
||||||
JDK1_2_CLASS_FILE_FORMAT_MAJOR_VERSION..JDK8_CLASS_FILE_FORMAT_MAJOR_VERSION)
|
JAVA_1_2_CLASS_FILE_FORMAT_MAJOR_VERSION..JAVA_17_CLASS_FILE_FORMAT_MAJOR_VERSION)
|
||||||
} catch (ex: UnsupportedClassVersionError) {
|
} catch (ex: UnsupportedClassVersionError) {
|
||||||
throw TransactionVerificationException.UnsupportedClassVersionError(txId, ex.message!!, ex)
|
throw TransactionVerificationException.UnsupportedClassVersionError(txId, ex.message!!, ex)
|
||||||
}
|
}
|
||||||
@ -380,9 +373,9 @@ object AttachmentsClassLoaderBuilder {
|
|||||||
.withWhitelist(whitelistedClasses)
|
.withWhitelist(whitelistedClasses)
|
||||||
.withCustomSerializers(serializers)
|
.withCustomSerializers(serializers)
|
||||||
.withoutCarpenter()
|
.withoutCarpenter()
|
||||||
})
|
}
|
||||||
|
|
||||||
val serializationContext = cachedSerializationContext.withProperties(mapOf<Any, Any>(
|
val serializationContext = cachedSerializationContext.withProperties(mapOf(
|
||||||
// Duplicate the SerializationContext from the cache and give
|
// Duplicate the SerializationContext from the cache and give
|
||||||
// it these extra properties, just for this transaction.
|
// it these extra properties, just for this transaction.
|
||||||
// However, keep a strong reference to the cached SerializationContext so we can
|
// However, keep a strong reference to the cached SerializationContext so we can
|
||||||
@ -489,7 +482,6 @@ class AttachmentsClassLoaderCacheImpl(cacheFactory: NamedCacheFactory) : Singlet
|
|||||||
private val toBeClosed = ConcurrentHashMap.newKeySet<ToBeClosed>()
|
private val toBeClosed = ConcurrentHashMap.newKeySet<ToBeClosed>()
|
||||||
private val expiryQueue = ReferenceQueue<SerializationContext>()
|
private val expiryQueue = ReferenceQueue<SerializationContext>()
|
||||||
|
|
||||||
@Suppress("TooGenericExceptionCaught")
|
|
||||||
private fun purgeExpiryQueue() {
|
private fun purgeExpiryQueue() {
|
||||||
// Close the AttachmentsClassLoader for every SerializationContext
|
// Close the AttachmentsClassLoader for every SerializationContext
|
||||||
// that has already been garbage-collected.
|
// that has already been garbage-collected.
|
||||||
|
@ -1,28 +1,44 @@
|
|||||||
package net.corda.core.transactions
|
package net.corda.core.transactions
|
||||||
|
|
||||||
import net.corda.core.CordaInternal
|
import net.corda.core.CordaInternal
|
||||||
import net.corda.core.contracts.*
|
import net.corda.core.contracts.Attachment
|
||||||
|
import net.corda.core.contracts.AttachmentResolutionException
|
||||||
|
import net.corda.core.contracts.ContractAttachment
|
||||||
|
import net.corda.core.contracts.ContractClassName
|
||||||
|
import net.corda.core.contracts.ContractState
|
||||||
|
import net.corda.core.contracts.HashAttachmentConstraint
|
||||||
|
import net.corda.core.contracts.PrivacySalt
|
||||||
|
import net.corda.core.contracts.StateAndRef
|
||||||
|
import net.corda.core.contracts.StateRef
|
||||||
|
import net.corda.core.contracts.TransactionResolutionException
|
||||||
|
import net.corda.core.contracts.TransactionState
|
||||||
|
import net.corda.core.contracts.TransactionVerificationException
|
||||||
|
import net.corda.core.contracts.UpgradedContract
|
||||||
|
import net.corda.core.contracts.UpgradedContractWithLegacyConstraint
|
||||||
|
import net.corda.core.contracts.WhitelistedByZoneAttachmentConstraint
|
||||||
import net.corda.core.crypto.DigestService
|
import net.corda.core.crypto.DigestService
|
||||||
import net.corda.core.crypto.SecureHash
|
import net.corda.core.crypto.SecureHash
|
||||||
import net.corda.core.crypto.TransactionSignature
|
import net.corda.core.crypto.TransactionSignature
|
||||||
import net.corda.core.identity.Party
|
import net.corda.core.identity.Party
|
||||||
import net.corda.core.internal.AttachmentWithContext
|
import net.corda.core.internal.AttachmentWithContext
|
||||||
import net.corda.core.internal.ServiceHubCoreInternal
|
|
||||||
import net.corda.core.internal.combinedHash
|
import net.corda.core.internal.combinedHash
|
||||||
|
import net.corda.core.internal.loadClassOfType
|
||||||
|
import net.corda.core.internal.mapToSet
|
||||||
|
import net.corda.core.internal.verification.VerificationSupport
|
||||||
|
import net.corda.core.internal.verification.toVerifyingServiceHub
|
||||||
import net.corda.core.node.NetworkParameters
|
import net.corda.core.node.NetworkParameters
|
||||||
import net.corda.core.node.ServicesForResolution
|
import net.corda.core.node.ServicesForResolution
|
||||||
import net.corda.core.serialization.CordaSerializable
|
import net.corda.core.serialization.CordaSerializable
|
||||||
import net.corda.core.serialization.DeprecatedConstructorForDeserialization
|
import net.corda.core.serialization.DeprecatedConstructorForDeserialization
|
||||||
import net.corda.core.serialization.SerializedBytes
|
|
||||||
import net.corda.core.serialization.deserialize
|
import net.corda.core.serialization.deserialize
|
||||||
import net.corda.core.serialization.internal.AttachmentsClassLoaderBuilder
|
|
||||||
import net.corda.core.serialization.serialize
|
|
||||||
import net.corda.core.transactions.ContractUpgradeFilteredTransaction.FilteredComponent
|
import net.corda.core.transactions.ContractUpgradeFilteredTransaction.FilteredComponent
|
||||||
import net.corda.core.transactions.ContractUpgradeLedgerTransaction.Companion.loadUpgradedContract
|
|
||||||
import net.corda.core.transactions.ContractUpgradeLedgerTransaction.Companion.retrieveAppClassLoader
|
|
||||||
import net.corda.core.transactions.ContractUpgradeWireTransaction.Companion.calculateUpgradedState
|
import net.corda.core.transactions.ContractUpgradeWireTransaction.Companion.calculateUpgradedState
|
||||||
import net.corda.core.transactions.ContractUpgradeWireTransaction.Component.*
|
import net.corda.core.transactions.ContractUpgradeWireTransaction.Component.INPUTS
|
||||||
import net.corda.core.transactions.WireTransaction.Companion.resolveStateRefBinaryComponent
|
import net.corda.core.transactions.ContractUpgradeWireTransaction.Component.LEGACY_ATTACHMENT
|
||||||
|
import net.corda.core.transactions.ContractUpgradeWireTransaction.Component.NOTARY
|
||||||
|
import net.corda.core.transactions.ContractUpgradeWireTransaction.Component.PARAMETERS_HASH
|
||||||
|
import net.corda.core.transactions.ContractUpgradeWireTransaction.Component.UPGRADED_ATTACHMENT
|
||||||
|
import net.corda.core.transactions.ContractUpgradeWireTransaction.Component.UPGRADED_CONTRACT
|
||||||
import net.corda.core.utilities.OpaqueBytes
|
import net.corda.core.utilities.OpaqueBytes
|
||||||
import net.corda.core.utilities.toBase58String
|
import net.corda.core.utilities.toBase58String
|
||||||
import java.security.PublicKey
|
import java.security.PublicKey
|
||||||
@ -52,7 +68,10 @@ data class ContractUpgradeWireTransaction(
|
|||||||
* Runs the explicit upgrade logic.
|
* Runs the explicit upgrade logic.
|
||||||
*/
|
*/
|
||||||
@CordaInternal
|
@CordaInternal
|
||||||
internal fun <T : ContractState, S : ContractState> calculateUpgradedState(state: TransactionState<T>, upgradedContract: UpgradedContract<T, S>, upgradedContractAttachment: Attachment): TransactionState<S> {
|
@JvmSynthetic
|
||||||
|
internal fun <T : ContractState, S : ContractState> calculateUpgradedState(state: TransactionState<T>,
|
||||||
|
upgradedContract: UpgradedContract<T, S>,
|
||||||
|
upgradedContractAttachment: Attachment): TransactionState<S> {
|
||||||
// TODO: if there are encumbrance states in the inputs, just copy them across without modifying
|
// TODO: if there are encumbrance states in the inputs, just copy them across without modifying
|
||||||
val upgradedState: S = upgradedContract.upgrade(state.data)
|
val upgradedState: S = upgradedContract.upgrade(state.data)
|
||||||
val inputConstraint = state.constraint
|
val inputConstraint = state.constraint
|
||||||
@ -121,60 +140,12 @@ data class ContractUpgradeWireTransaction(
|
|||||||
|
|
||||||
/** Resolves input states and contract attachments, and builds a ContractUpgradeLedgerTransaction. */
|
/** Resolves input states and contract attachments, and builds a ContractUpgradeLedgerTransaction. */
|
||||||
fun resolve(services: ServicesForResolution, sigs: List<TransactionSignature>): ContractUpgradeLedgerTransaction {
|
fun resolve(services: ServicesForResolution, sigs: List<TransactionSignature>): ContractUpgradeLedgerTransaction {
|
||||||
val resolvedInputs = services.loadStates(inputs.toSet()).toList()
|
return ContractUpgradeLedgerTransaction.resolve(services.toVerifyingServiceHub(), this, sigs)
|
||||||
val legacyContractAttachment = services.attachments.openAttachment(legacyContractAttachmentId)
|
|
||||||
?: throw AttachmentResolutionException(legacyContractAttachmentId)
|
|
||||||
val upgradedContractAttachment = services.attachments.openAttachment(upgradedContractAttachmentId)
|
|
||||||
?: throw AttachmentResolutionException(upgradedContractAttachmentId)
|
|
||||||
val hashToResolve = networkParametersHash ?: services.networkParametersService.defaultHash
|
|
||||||
val resolvedNetworkParameters = services.networkParametersService.lookup(hashToResolve) ?: throw TransactionResolutionException(id)
|
|
||||||
return ContractUpgradeLedgerTransaction.create(
|
|
||||||
resolvedInputs,
|
|
||||||
notary,
|
|
||||||
legacyContractAttachment,
|
|
||||||
upgradedContractAttachment,
|
|
||||||
id,
|
|
||||||
privacySalt,
|
|
||||||
sigs,
|
|
||||||
resolvedNetworkParameters,
|
|
||||||
loadUpgradedContract(upgradedContractClassName, retrieveAppClassLoader(services))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun upgradedContract(className: ContractClassName, classLoader: ClassLoader): UpgradedContract<ContractState, ContractState> = try {
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
|
||||||
Class.forName(className, false, classLoader).asSubclass(UpgradedContract::class.java).getDeclaredConstructor().newInstance() as UpgradedContract<ContractState, ContractState>
|
|
||||||
} catch (e: Exception) {
|
|
||||||
throw TransactionVerificationException.ContractCreationError(id, className, e)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a binary serialized component for a virtual output state serialised and executed with the attachments from the transaction.
|
|
||||||
*/
|
|
||||||
@CordaInternal
|
|
||||||
internal fun resolveOutputComponent(services: ServicesForResolution, stateRef: StateRef, params: NetworkParameters): SerializedBytes<TransactionState<ContractState>> {
|
|
||||||
val binaryInput: SerializedBytes<TransactionState<ContractState>> = resolveStateRefBinaryComponent(inputs[stateRef.index], services)!!
|
|
||||||
val legacyAttachment = services.attachments.openAttachment(legacyContractAttachmentId)
|
|
||||||
?: throw MissingContractAttachments(emptyList())
|
|
||||||
val upgradedAttachment = services.attachments.openAttachment(upgradedContractAttachmentId)
|
|
||||||
?: throw MissingContractAttachments(emptyList())
|
|
||||||
|
|
||||||
return AttachmentsClassLoaderBuilder.withAttachmentsClassloaderContext(
|
|
||||||
listOf(legacyAttachment, upgradedAttachment),
|
|
||||||
params,
|
|
||||||
id,
|
|
||||||
{ (services as ServiceHubCoreInternal).attachmentTrustCalculator.calculate(it) },
|
|
||||||
attachmentsClassLoaderCache = (services as ServiceHubCoreInternal).attachmentsClassLoaderCache) { serializationContext ->
|
|
||||||
val resolvedInput = binaryInput.deserialize()
|
|
||||||
val upgradedContract = upgradedContract(upgradedContractClassName, serializationContext.deserializationClassLoader)
|
|
||||||
val outputState = calculateUpgradedState(resolvedInput, upgradedContract, upgradedAttachment)
|
|
||||||
outputState.serialize()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Constructs a filtered transaction: the inputs, the notary party and network parameters hash are always visible, while the rest are hidden. */
|
/** Constructs a filtered transaction: the inputs, the notary party and network parameters hash are always visible, while the rest are hidden. */
|
||||||
fun buildFilteredTransaction(): ContractUpgradeFilteredTransaction {
|
fun buildFilteredTransaction(): ContractUpgradeFilteredTransaction {
|
||||||
val totalComponents = (0 until serializedComponents.size).toSet()
|
val totalComponents = serializedComponents.indices.toSet()
|
||||||
val visibleComponents = mapOf(
|
val visibleComponents = mapOf(
|
||||||
INPUTS.ordinal to FilteredComponent(serializedComponents[INPUTS.ordinal], nonces[INPUTS.ordinal]),
|
INPUTS.ordinal to FilteredComponent(serializedComponents[INPUTS.ordinal], nonces[INPUTS.ordinal]),
|
||||||
NOTARY.ordinal to FilteredComponent(serializedComponents[NOTARY.ordinal], nonces[NOTARY.ordinal]),
|
NOTARY.ordinal to FilteredComponent(serializedComponents[NOTARY.ordinal], nonces[NOTARY.ordinal]),
|
||||||
@ -287,39 +258,46 @@ private constructor(
|
|||||||
get() = upgradedContract::class.java.name
|
get() = upgradedContract::class.java.name
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
@CordaInternal
|
@CordaInternal
|
||||||
internal fun create(
|
@JvmSynthetic
|
||||||
inputs: List<StateAndRef<ContractState>>,
|
@Suppress("ThrowsCount")
|
||||||
notary: Party,
|
fun resolve(verificationSupport: VerificationSupport,
|
||||||
legacyContractAttachment: Attachment,
|
wtx: ContractUpgradeWireTransaction,
|
||||||
upgradedContractAttachment: Attachment,
|
sigs: List<TransactionSignature>): ContractUpgradeLedgerTransaction {
|
||||||
id: SecureHash,
|
val inputs = wtx.inputs.map(verificationSupport::getStateAndRef)
|
||||||
privacySalt: PrivacySalt,
|
val (legacyContractAttachment, upgradedContractAttachment) = verificationSupport.getAttachments(listOf(
|
||||||
sigs: List<TransactionSignature>,
|
wtx.legacyContractAttachmentId,
|
||||||
networkParameters: NetworkParameters,
|
wtx.upgradedContractAttachmentId
|
||||||
upgradedContract: UpgradedContract<ContractState, *>
|
))
|
||||||
): ContractUpgradeLedgerTransaction {
|
val networkParameters = verificationSupport.getNetworkParameters(wtx.networkParametersHash)
|
||||||
return ContractUpgradeLedgerTransaction(inputs, notary, legacyContractAttachment, upgradedContractAttachment, id, privacySalt, sigs, networkParameters, upgradedContract)
|
?: throw TransactionResolutionException(wtx.id)
|
||||||
|
val upgradedContract = loadUpgradedContract(wtx.upgradedContractClassName, wtx.id, verificationSupport.appClassLoader)
|
||||||
|
return ContractUpgradeLedgerTransaction(
|
||||||
|
inputs,
|
||||||
|
wtx.notary,
|
||||||
|
legacyContractAttachment ?: throw AttachmentResolutionException(wtx.legacyContractAttachmentId),
|
||||||
|
upgradedContractAttachment ?: throw AttachmentResolutionException(wtx.upgradedContractAttachmentId),
|
||||||
|
wtx.id,
|
||||||
|
wtx.privacySalt,
|
||||||
|
sigs,
|
||||||
|
networkParameters,
|
||||||
|
upgradedContract
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO - this has to use a classloader created from the upgraded attachment.
|
// TODO There is an inconsistency with the class loader used with this method. Transaction resolution uses the app class loader,
|
||||||
|
// whilst TransactionStorageVerification.getContractUpdateOutput uses an attachments class loder comprised of the the legacy and
|
||||||
|
// upgraded attachments
|
||||||
@CordaInternal
|
@CordaInternal
|
||||||
internal fun loadUpgradedContract(upgradedContractClassName: ContractClassName, classLoader: ClassLoader): UpgradedContract<ContractState, *> {
|
@JvmSynthetic
|
||||||
@Suppress("UNCHECKED_CAST")
|
internal fun loadUpgradedContract(className: ContractClassName, id: SecureHash, classLoader: ClassLoader): UpgradedContract<ContractState, *> {
|
||||||
return Class.forName(upgradedContractClassName, false, classLoader)
|
return try {
|
||||||
.asSubclass(Contract::class.java)
|
loadClassOfType<UpgradedContract<ContractState, *>>(className, false, classLoader)
|
||||||
.getConstructor()
|
.getDeclaredConstructor()
|
||||||
.newInstance() as UpgradedContract<ContractState, *>
|
.newInstance()
|
||||||
|
} catch (e: Exception) {
|
||||||
|
throw TransactionVerificationException.ContractCreationError(id, className, e)
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is a "hack" to retrieve the CordappsClassloader from the services without having access to all classes.
|
|
||||||
@CordaInternal
|
|
||||||
internal fun retrieveAppClassLoader(services: ServicesForResolution): ClassLoader {
|
|
||||||
val cordappLoader = services.cordappProvider::class.java.getMethod("getCordappLoader").invoke(services.cordappProvider)
|
|
||||||
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
|
||||||
return cordappLoader::class.java.getMethod("getAppClassLoader").invoke(cordappLoader) as ClassLoader
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -366,7 +344,7 @@ private constructor(
|
|||||||
|
|
||||||
/** The required signers are the set of all input states' participants. */
|
/** The required signers are the set of all input states' participants. */
|
||||||
override val requiredSigningKeys: Set<PublicKey>
|
override val requiredSigningKeys: Set<PublicKey>
|
||||||
get() = inputs.flatMap { it.state.data.participants }.map { it.owningKey }.toSet() + notary.owningKey
|
get() = inputs.flatMap { it.state.data.participants }.mapToSet { it.owningKey } + notary.owningKey
|
||||||
|
|
||||||
override fun getKeyDescriptions(keys: Set<PublicKey>): List<String> {
|
override fun getKeyDescriptions(keys: Set<PublicKey>): List<String> {
|
||||||
return keys.map { it.toBase58String() }
|
return keys.map { it.toBase58String() }
|
||||||
@ -401,7 +379,7 @@ private constructor(
|
|||||||
privacySalt: PrivacySalt,
|
privacySalt: PrivacySalt,
|
||||||
sigs: List<TransactionSignature>,
|
sigs: List<TransactionSignature>,
|
||||||
networkParameters: NetworkParameters
|
networkParameters: NetworkParameters
|
||||||
) : this(inputs, notary, legacyContractAttachment, upgradedContractAttachment, id, privacySalt, sigs, networkParameters, loadUpgradedContract(upgradedContractClassName, ContractUpgradeLedgerTransaction::class.java.classLoader))
|
) : this(inputs, notary, legacyContractAttachment, upgradedContractAttachment, id, privacySalt, sigs, networkParameters, loadUpgradedContract(upgradedContractClassName, id, ContractUpgradeLedgerTransaction::class.java.classLoader))
|
||||||
|
|
||||||
@Deprecated("ContractUpgradeLedgerTransaction should not be created directly, use ContractUpgradeWireTransaction.resolve instead.")
|
@Deprecated("ContractUpgradeLedgerTransaction should not be created directly, use ContractUpgradeWireTransaction.resolve instead.")
|
||||||
fun copy(
|
fun copy(
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user