mirror of
https://github.com/corda/corda.git
synced 2025-01-30 08:04:16 +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
|
||||
* and it acts as a fail-safe inside {@code forwardMerger} pipeline
|
||||
*/
|
||||
String originBranch = 'release/os/4.11'
|
||||
String originBranch = 'release/os/4.12'
|
||||
|
||||
/**
|
||||
* the branch name of target branch, it should be the branch with the next version
|
||||
* after the one in current branch.
|
||||
*/
|
||||
String targetBranch = 'release/os/4.12'
|
||||
String targetBranch = 'release/os/4.13'
|
||||
|
||||
/**
|
||||
* Forward merge any changes between #originBranch and #targetBranch
|
||||
|
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_USERNAME = "${env.ARTIFACTORY_CREDENTIALS_USR}"
|
||||
CORDA_USE_CACHE = "corda-remotes"
|
||||
JAVA_HOME = "/usr/lib/jvm/java-17-amazon-corretto"
|
||||
}
|
||||
|
||||
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')
|
||||
CORDA_ARTIFACTORY_USERNAME = "${env.ARTIFACTORY_CREDENTIALS_USR}"
|
||||
CORDA_ARTIFACTORY_PASSWORD = "${env.ARTIFACTORY_CREDENTIALS_PSW}"
|
||||
JAVA_HOME = "/usr/lib/jvm/java-17-amazon-corretto"
|
||||
}
|
||||
|
||||
stages {
|
||||
|
@ -35,6 +35,7 @@ pipeline {
|
||||
ARTIFACTORY_BUILD_NAME = "Corda / Publish / Publish Nightly to Artifactory"
|
||||
.replaceAll("/", " :: ")
|
||||
DOCKER_URL = "https://index.docker.io/v1/"
|
||||
JAVA_HOME = "/usr/lib/jvm/java-17-amazon-corretto"
|
||||
}
|
||||
|
||||
stages {
|
||||
|
@ -24,6 +24,7 @@ pipeline {
|
||||
// in the name
|
||||
ARTIFACTORY_BUILD_NAME = "Corda / Publish / Publish Preview to Artifactory"
|
||||
.replaceAll("/", " :: ")
|
||||
JAVA_HOME = "/usr/lib/jvm/java-17-amazon-corretto"
|
||||
}
|
||||
|
||||
stages {
|
||||
|
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_TOKEN = credentials('c4-os-snyk-api-token-secret') //Jenkins credential type: Secret text
|
||||
C4_OS_SNYK_ORG_ID = credentials('corda4-os-snyk-org-id')
|
||||
JAVA_HOME = "/usr/lib/jvm/java-17-amazon-corretto"
|
||||
}
|
||||
|
||||
stages {
|
||||
|
19
Jenkinsfile
vendored
19
Jenkinsfile
vendored
@ -53,6 +53,7 @@ pipeline {
|
||||
CORDA_ARTIFACTORY_USERNAME = "${env.ARTIFACTORY_CREDENTIALS_USR}"
|
||||
CORDA_GRADLE_SCAN_KEY = credentials('gradle-build-scans-key')
|
||||
CORDA_USE_CACHE = "corda-remotes"
|
||||
JAVA_HOME="/usr/lib/jvm/java-17-amazon-corretto"
|
||||
}
|
||||
|
||||
stages {
|
||||
@ -119,6 +120,24 @@ pipeline {
|
||||
].join(' ')
|
||||
}
|
||||
}
|
||||
stage('Smoke Test') {
|
||||
steps {
|
||||
sh script: [
|
||||
'./gradlew',
|
||||
COMMON_GRADLE_PARAMS,
|
||||
'smokeTest'
|
||||
].join(' ')
|
||||
}
|
||||
}
|
||||
stage('Slow Integration Test') {
|
||||
steps {
|
||||
sh script: [
|
||||
'./gradlew',
|
||||
COMMON_GRADLE_PARAMS,
|
||||
'slowIntegrationTest'
|
||||
].join(' ')
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
stage('Same agent') {
|
||||
|
@ -1,11 +1,55 @@
|
||||
plugins {
|
||||
id 'groovy-gradle-plugin'
|
||||
}
|
||||
|
||||
Properties constants = new Properties()
|
||||
file("$rootDir/../constants.properties").withInputStream { constants.load(it) }
|
||||
|
||||
def internalPublishVersion = constants.getProperty('internalPublishVersion')
|
||||
def artifactoryContextUrl = constants.getProperty('artifactoryContextUrl')
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
def cordaUseCache = System.getenv("CORDA_USE_CACHE")
|
||||
if (cordaUseCache != null) {
|
||||
maven {
|
||||
url = "${artifactoryContextUrl}/${cordaUseCache}"
|
||||
name = "R3 Maven remote repositories"
|
||||
authentication {
|
||||
basic(BasicAuthentication)
|
||||
}
|
||||
credentials {
|
||||
username = findProperty('cordaArtifactoryUsername') ?: System.getenv('CORDA_ARTIFACTORY_USERNAME')
|
||||
password = findProperty('cordaArtifactoryPassword') ?: System.getenv('CORDA_ARTIFACTORY_PASSWORD')
|
||||
}
|
||||
metadataSources {
|
||||
mavenPom()
|
||||
artifact()
|
||||
ignoreGradleMetadataRedirection()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
maven {
|
||||
url "${artifactoryContextUrl}/engineering-tools-maven"
|
||||
authentication {
|
||||
basic(BasicAuthentication)
|
||||
}
|
||||
credentials {
|
||||
username = findProperty('cordaArtifactoryUsername') ?: System.getenv('CORDA_ARTIFACTORY_USERNAME')
|
||||
password = findProperty('cordaArtifactoryPassword') ?: System.getenv('CORDA_ARTIFACTORY_PASSWORD')
|
||||
}
|
||||
content {
|
||||
includeGroupByRegex 'com\\.r3\\.internal(\\..*)?'
|
||||
}
|
||||
}
|
||||
gradlePluginPortal()
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile group: 'com.github.docker-java', name: 'docker-java', version: constants.dockerJavaVersion
|
||||
compile group: 'com.github.docker-java', name: 'docker-java-transport-httpclient5', version: constants.dockerJavaVersion
|
||||
implementation group: 'com.github.docker-java', name: 'docker-java', 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: 'kotlin'
|
||||
apply plugin: 'net.corda.plugins.publish-utils'
|
||||
apply plugin: 'org.jetbrains.kotlin.jvm'
|
||||
apply plugin: 'net.corda.plugins.api-scanner'
|
||||
apply plugin: 'com.jfrog.artifactory'
|
||||
apply plugin: 'corda.common-publishing'
|
||||
|
||||
dependencies {
|
||||
compile project(':serialization')
|
||||
api project(':core')
|
||||
|
||||
implementation project(':serialization')
|
||||
|
||||
compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
|
||||
// Jackson and its plugins: parsing to/from JSON and other textual formats.
|
||||
compile("com.fasterxml.jackson.module:jackson-module-kotlin:$jackson_kotlin_version") {
|
||||
implementation("com.fasterxml.jackson.module:jackson-module-kotlin:$jackson_kotlin_version") {
|
||||
exclude module: "jackson-databind"
|
||||
}
|
||||
// Yaml is useful for parsing strings to method calls.
|
||||
compile "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:$jackson_version"
|
||||
implementation "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:$jackson_version"
|
||||
// This adds support for java.time types.
|
||||
compile "com.fasterxml.jackson.datatype:jackson-datatype-jsr310:$jackson_version"
|
||||
compile "com.google.guava:guava:$guava_version"
|
||||
implementation "com.fasterxml.jackson.datatype:jackson-datatype-jsr310:$jackson_version"
|
||||
implementation "com.google.guava:guava:$guava_version"
|
||||
|
||||
testCompile project(':test-utils')
|
||||
testCompile project(path: ':core', configuration: 'testArtifacts')
|
||||
// Bouncy castle support needed for X509 certificate manipulation
|
||||
implementation "org.bouncycastle:bcprov-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 "junit:junit:$junit_version"
|
||||
@ -28,7 +38,7 @@ dependencies {
|
||||
testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:${junit_jupiter_version}"
|
||||
testRuntimeOnly "org.junit.platform:junit-platform-launcher:${junit_platform_version}"
|
||||
|
||||
testCompile "org.jetbrains.kotlin:kotlin-test:$kotlin_version"
|
||||
testImplementation "org.jetbrains.kotlin:kotlin-test:$kotlin_version"
|
||||
|
||||
}
|
||||
|
||||
@ -39,6 +49,11 @@ jar {
|
||||
}
|
||||
}
|
||||
|
||||
publish {
|
||||
name jar.baseName
|
||||
publishing {
|
||||
publications {
|
||||
maven(MavenPublication) {
|
||||
artifactId jar.baseName
|
||||
from components.java
|
||||
}
|
||||
}
|
||||
}
|
@ -315,7 +315,8 @@ object JacksonSupport {
|
||||
|
||||
private class CertPathSerializer : JsonSerializer<CertPath>() {
|
||||
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.dataformat.yaml.YAMLFactory
|
||||
import com.fasterxml.jackson.module.kotlin.convertValue
|
||||
import com.nhaarman.mockito_kotlin.any
|
||||
import com.nhaarman.mockito_kotlin.doReturn
|
||||
import com.nhaarman.mockito_kotlin.mock
|
||||
import com.nhaarman.mockito_kotlin.whenever
|
||||
import com.nhaarman.mockito_kotlin.spy
|
||||
import net.corda.client.jackson.internal.childrenAs
|
||||
import net.corda.client.jackson.internal.valueAs
|
||||
import net.corda.core.contracts.*
|
||||
import net.corda.core.contracts.Amount
|
||||
import net.corda.core.contracts.Command
|
||||
import net.corda.core.contracts.LinearState
|
||||
import net.corda.core.contracts.PrivacySalt
|
||||
import net.corda.core.contracts.StateRef
|
||||
import net.corda.core.contracts.TimeWindow
|
||||
import net.corda.core.contracts.TransactionState
|
||||
import net.corda.core.contracts.UniqueIdentifier
|
||||
import net.corda.core.cordapp.CordappProvider
|
||||
import net.corda.core.crypto.*
|
||||
import net.corda.core.crypto.CompositeKey
|
||||
import net.corda.core.crypto.Crypto
|
||||
import net.corda.core.crypto.DigestService
|
||||
import net.corda.core.crypto.DigitalSignature
|
||||
import net.corda.core.crypto.PartialMerkleTree
|
||||
import net.corda.core.crypto.PartialMerkleTree.PartialTree
|
||||
import net.corda.core.identity.*
|
||||
import net.corda.core.internal.AbstractAttachment
|
||||
import net.corda.core.crypto.SecureHash
|
||||
import net.corda.core.crypto.SignatureMetadata
|
||||
import net.corda.core.crypto.SignatureScheme
|
||||
import net.corda.core.crypto.TransactionSignature
|
||||
import net.corda.core.crypto.secureRandomBytes
|
||||
import net.corda.core.identity.AbstractParty
|
||||
import net.corda.core.identity.AnonymousParty
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.core.identity.PartyAndCertificate
|
||||
import net.corda.core.internal.DigitalSignatureWithCert
|
||||
import net.corda.core.node.NodeInfo
|
||||
import net.corda.core.node.ServiceHub
|
||||
import net.corda.core.node.services.AttachmentStorage
|
||||
import net.corda.core.node.services.IdentityService
|
||||
import net.corda.core.node.services.NetworkParametersService
|
||||
import net.corda.core.node.services.TransactionStorage
|
||||
import net.corda.core.serialization.CordaSerializable
|
||||
import net.corda.core.serialization.SerializedBytes
|
||||
import net.corda.core.serialization.deserialize
|
||||
@ -37,14 +46,27 @@ import net.corda.core.transactions.CoreTransaction
|
||||
import net.corda.core.transactions.SignedTransaction
|
||||
import net.corda.core.transactions.TransactionBuilder
|
||||
import net.corda.core.transactions.WireTransaction
|
||||
import net.corda.core.utilities.*
|
||||
import net.corda.core.utilities.ByteSequence
|
||||
import net.corda.core.utilities.NetworkHostAndPort
|
||||
import net.corda.core.utilities.OpaqueBytes
|
||||
import net.corda.core.utilities.days
|
||||
import net.corda.core.utilities.hours
|
||||
import net.corda.core.utilities.toBase58String
|
||||
import net.corda.core.utilities.toBase64
|
||||
import net.corda.core.utilities.toHexString
|
||||
import net.corda.coretesting.internal.createNodeInfoAndSigned
|
||||
import net.corda.coretesting.internal.rigorousMock
|
||||
import net.corda.finance.USD
|
||||
import net.corda.nodeapi.internal.crypto.x509Certificates
|
||||
import net.corda.testing.common.internal.testNetworkParameters
|
||||
import net.corda.testing.contracts.DummyContract
|
||||
import net.corda.testing.core.*
|
||||
import net.corda.coretesting.internal.createNodeInfoAndSigned
|
||||
import net.corda.coretesting.internal.rigorousMock
|
||||
import net.corda.testing.core.ALICE_NAME
|
||||
import net.corda.testing.core.BOB_NAME
|
||||
import net.corda.testing.core.DUMMY_NOTARY_NAME
|
||||
import net.corda.testing.core.DummyCommandData
|
||||
import net.corda.testing.core.SerializationEnvironmentRule
|
||||
import net.corda.testing.core.TestIdentity
|
||||
import net.corda.testing.node.MockServices
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.assertj.core.api.Assertions.assertThatThrownBy
|
||||
import org.junit.Before
|
||||
@ -54,15 +76,22 @@ import org.junit.jupiter.api.TestFactory
|
||||
import org.junit.runner.RunWith
|
||||
import org.junit.runners.Parameterized
|
||||
import org.junit.runners.Parameterized.Parameters
|
||||
import org.mockito.kotlin.spy
|
||||
import org.mockito.kotlin.whenever
|
||||
import java.math.BigInteger
|
||||
import java.nio.charset.StandardCharsets.UTF_8
|
||||
import java.security.PublicKey
|
||||
import java.security.cert.CertPath
|
||||
import java.security.cert.X509Certificate
|
||||
import java.time.Instant
|
||||
import java.util.*
|
||||
import java.util.Currency
|
||||
import java.util.Date
|
||||
import java.util.UUID
|
||||
import javax.security.auth.x500.X500Principal
|
||||
import kotlin.collections.ArrayList
|
||||
import kotlin.collections.component1
|
||||
import kotlin.collections.component2
|
||||
import kotlin.collections.component3
|
||||
import kotlin.collections.component4
|
||||
|
||||
@RunWith(Parameterized::class)
|
||||
class JacksonSupportTest(@Suppress("unused") private val name: String, factory: JsonFactory) {
|
||||
@ -90,23 +119,12 @@ class JacksonSupportTest(@Suppress("unused") private val name: String, factory:
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
val unsignedAttachment = object : AbstractAttachment({ byteArrayOf() }, "test") {
|
||||
override val id: SecureHash get() = throw UnsupportedOperationException()
|
||||
}
|
||||
|
||||
val attachments = rigorousMock<AttachmentStorage>().also {
|
||||
doReturn(unsignedAttachment).whenever(it).openAttachment(any())
|
||||
}
|
||||
services = rigorousMock()
|
||||
services = MockServices(
|
||||
listOf("net.corda.testing.contracts"),
|
||||
MINI_CORP,
|
||||
testNetworkParameters(minimumPlatformVersion = 4)
|
||||
)
|
||||
cordappProvider = rigorousMock()
|
||||
val networkParameters = testNetworkParameters(minimumPlatformVersion = 4)
|
||||
val networkParametersService = rigorousMock<NetworkParametersService>().also {
|
||||
doReturn(networkParameters.serialize().hash).whenever(it).currentHash
|
||||
}
|
||||
doReturn(networkParametersService).whenever(services).networkParametersService
|
||||
doReturn(cordappProvider).whenever(services).cordappProvider
|
||||
doReturn(networkParameters).whenever(services).networkParameters
|
||||
doReturn(attachments).whenever(services).attachments
|
||||
}
|
||||
|
||||
@Test(timeout=300_000)
|
||||
@ -263,17 +281,6 @@ class JacksonSupportTest(@Suppress("unused") private val name: String, factory:
|
||||
@Test(timeout=300_000)
|
||||
fun `SignedTransaction (WireTransaction)`() {
|
||||
val attachmentId = SecureHash.randomSHA256()
|
||||
doReturn(attachmentId).whenever(cordappProvider).getContractAttachmentID(DummyContract.PROGRAM_ID)
|
||||
val attachmentStorage = rigorousMock<AttachmentStorage>()
|
||||
doReturn(attachmentStorage).whenever(services).attachments
|
||||
doReturn(mock<TransactionStorage>()).whenever(services).validatedTransactions
|
||||
doReturn(mock<IdentityService>()).whenever(services).identityService
|
||||
val attachment = rigorousMock<ContractAttachment>()
|
||||
doReturn(attachment).whenever(attachmentStorage).openAttachment(attachmentId)
|
||||
doReturn(attachmentId).whenever(attachment).id
|
||||
doReturn(emptyList<Party>()).whenever(attachment).signerKeys
|
||||
doReturn(setOf(DummyContract.PROGRAM_ID)).whenever(attachment).allContracts
|
||||
doReturn("app").whenever(attachment).uploader
|
||||
|
||||
val wtx = TransactionBuilder(
|
||||
notary = DUMMY_NOTARY,
|
||||
|
@ -1,28 +1,27 @@
|
||||
// JDK 11 JavaFX
|
||||
plugins {
|
||||
id 'org.openjfx.javafxplugin' version '0.0.7' apply false
|
||||
id 'corda.common-publishing'
|
||||
}
|
||||
|
||||
if (JavaVersion.current().isJava9Compatible()) {
|
||||
apply plugin: 'org.openjfx.javafxplugin'
|
||||
javafx {
|
||||
version = "11.0.2"
|
||||
modules = ['javafx.controls',
|
||||
modules = [
|
||||
'javafx.controls',
|
||||
'javafx.fxml'
|
||||
]
|
||||
}
|
||||
}
|
||||
apply plugin: 'kotlin'
|
||||
|
||||
apply plugin: 'org.jetbrains.kotlin.jvm'
|
||||
apply plugin: 'net.corda.plugins.quasar-utils'
|
||||
apply plugin: 'net.corda.plugins.publish-utils'
|
||||
apply plugin: 'com.jfrog.artifactory'
|
||||
|
||||
description 'Corda client JavaFX modules'
|
||||
|
||||
//noinspection GroovyAssignabilityCheck
|
||||
configurations {
|
||||
integrationTestCompile.extendsFrom testCompile
|
||||
integrationTestRuntime.extendsFrom testRuntime
|
||||
integrationTestImplementation.extendsFrom testImplementation
|
||||
integrationTestRuntime.extendsFrom testRuntimeOnly
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
@ -39,23 +38,26 @@ sourceSets {
|
||||
// build/reports/project/dependencies/index.html for green highlighted parts of the tree.
|
||||
|
||||
dependencies {
|
||||
compile project(':core')
|
||||
compile project(':finance:contracts')
|
||||
compile project(':finance:workflows')
|
||||
compile project(':client:rpc')
|
||||
implementation project(':core')
|
||||
implementation project(':finance:contracts')
|
||||
implementation project(':finance:workflows')
|
||||
implementation project(':client:rpc')
|
||||
|
||||
compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
|
||||
compile "com.google.guava:guava:$guava_version"
|
||||
implementation "com.google.guava:guava:$guava_version"
|
||||
implementation "io.reactivex:rxjava:$rxjava_version"
|
||||
|
||||
// For caches rather than guava
|
||||
implementation "com.github.ben-manes.caffeine:caffeine:$caffeine_version"
|
||||
|
||||
// ReactFX: Functional reactive UI programming.
|
||||
compile 'org.reactfx:reactfx:2.0-M5'
|
||||
compile 'org.fxmisc.easybind:easybind:1.0.3'
|
||||
implementation 'org.reactfx:reactfx:2.0-M5'
|
||||
implementation 'org.fxmisc.easybind:easybind:1.0.3'
|
||||
|
||||
// Artemis Client: ability to connect to an Artemis broker and control it.
|
||||
// TODO: remove the forced update of commons-collections and beanutils when artemis updates them
|
||||
compile "org.apache.commons:commons-collections4:${commons_collections_version}"
|
||||
compile "commons-beanutils:commons-beanutils:${beanutils_version}"
|
||||
compile("org.apache.activemq:artemis-core-client:${artemis_version}") {
|
||||
implementation "org.apache.commons:commons-collections4:${commons_collections_version}"
|
||||
implementation "commons-beanutils:commons-beanutils:${beanutils_version}"
|
||||
implementation("org.apache.activemq:artemis-core-client:${artemis_version}") {
|
||||
exclude group: 'org.jgroups', module: 'jgroups'
|
||||
}
|
||||
|
||||
@ -67,13 +69,14 @@ dependencies {
|
||||
testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:${junit_jupiter_version}"
|
||||
testRuntimeOnly "org.junit.platform:junit-platform-launcher:${junit_platform_version}"
|
||||
|
||||
testCompile "org.assertj:assertj-core:${assertj_version}"
|
||||
testImplementation "org.jetbrains.kotlin:kotlin-test:$kotlin_version"
|
||||
testImplementation "org.assertj:assertj-core:${assertj_version}"
|
||||
|
||||
testCompile project(':test-utils')
|
||||
testImplementation project(':test-utils')
|
||||
|
||||
// Integration test helpers
|
||||
integrationTestCompile "junit:junit:$junit_version"
|
||||
integrationTestCompile project(':node-driver')
|
||||
integrationTestImplementation "junit:junit:$junit_version"
|
||||
integrationTestImplementation project(':node-driver')
|
||||
}
|
||||
|
||||
task integrationTest(type: Test) {
|
||||
@ -88,6 +91,11 @@ jar {
|
||||
}
|
||||
}
|
||||
|
||||
publish {
|
||||
name jar.baseName
|
||||
publishing {
|
||||
publications {
|
||||
maven(MavenPublication) {
|
||||
artifactId jar.baseName
|
||||
from components.java
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -14,13 +14,14 @@ import java.util.stream.Collectors
|
||||
* Utility bindings for the [Amount] type, similar in spirit to [Bindings]
|
||||
*/
|
||||
object AmountBindings {
|
||||
@Suppress("SpreadOperator")
|
||||
fun <T : Any> sum(amounts: ObservableList<Amount<T>>, token: T): MonadicBinding<Amount<T>> = EasyBind.map(
|
||||
Bindings.createLongBinding({
|
||||
amounts.stream().collect(Collectors.summingLong {
|
||||
require(it.token == token)
|
||||
it.quantity
|
||||
})
|
||||
}, arrayOf(amounts))
|
||||
}, *arrayOf(amounts))
|
||||
) { sum -> Amount(sum.toLong(), token) }
|
||||
|
||||
fun exchange(
|
||||
@ -35,6 +36,7 @@ object AmountBindings {
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("SpreadOperator")
|
||||
fun sumAmountExchange(
|
||||
amounts: ObservableList<Amount<Currency>>,
|
||||
currency: ObservableValue<Currency>,
|
||||
@ -45,7 +47,7 @@ object AmountBindings {
|
||||
EasyBind.map(
|
||||
Bindings.createLongBinding({
|
||||
amounts.stream().collect(Collectors.summingLong { exchange(it) })
|
||||
}, arrayOf(amounts))
|
||||
}, *arrayOf(amounts))
|
||||
) { Amount(it.toLong(), currencyValue) }
|
||||
}
|
||||
}
|
||||
|
@ -120,7 +120,7 @@ fun <A> ObservableList<out A>.filter(predicate: ObservableValue<(A) -> Boolean>)
|
||||
*/
|
||||
fun <A> ObservableList<out A?>.filterNotNull(): ObservableList<A> {
|
||||
//TODO This is a tactical work round for an issue with SAM conversion (https://youtrack.jetbrains.com/issue/ALL-1552) so that the M10 explorer works.
|
||||
return uncheckedCast(uncheckedCast<Any, ObservableList<A?>>(this).filtered { t -> t != null })
|
||||
return uncheckedCast(uncheckedCast<Any, ObservableList<A>>(this).filtered { t -> t != null })
|
||||
}
|
||||
|
||||
/**
|
||||
@ -128,6 +128,7 @@ fun <A> ObservableList<out A?>.filterNotNull(): ObservableList<A> {
|
||||
* val concatenatedNames = people.foldObservable("", { names, person -> names + person.name })
|
||||
* val concatenatedNames2 = people.map(Person::name).fold("", String::plus)
|
||||
*/
|
||||
@Suppress("SpreadOperator")
|
||||
fun <A, B> ObservableList<out A>.foldObservable(initial: B, folderFunction: (B, A) -> B): ObservableValue<B> {
|
||||
return Bindings.createObjectBinding({
|
||||
var current = initial
|
||||
@ -135,7 +136,7 @@ fun <A, B> ObservableList<out A>.foldObservable(initial: B, folderFunction: (B,
|
||||
current = folderFunction(current, it)
|
||||
}
|
||||
current
|
||||
}, arrayOf(this))
|
||||
}, *arrayOf(this))
|
||||
}
|
||||
|
||||
/**
|
||||
@ -285,6 +286,7 @@ fun <A> ObservableList<A>.first(): ObservableValue<A?> {
|
||||
return getValueAt(0)
|
||||
}
|
||||
|
||||
@Suppress("SpreadOperator")
|
||||
fun <A> ObservableList<A>.last(): ObservableValue<A?> {
|
||||
return Bindings.createObjectBinding({
|
||||
if (size > 0) {
|
||||
@ -292,7 +294,7 @@ fun <A> ObservableList<A>.last(): ObservableValue<A?> {
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}, arrayOf(this))
|
||||
}, *arrayOf(this))
|
||||
}
|
||||
|
||||
fun <T : Any> ObservableList<T>.unique(): ObservableList<T> {
|
||||
@ -303,24 +305,27 @@ fun <T : Any, K : Any> ObservableList<T>.distinctBy(toKey: (T) -> K): Observable
|
||||
return AggregatedList(this, toKey, { _, entryList -> entryList[0] })
|
||||
}
|
||||
|
||||
@Suppress("SpreadOperator")
|
||||
fun ObservableValue<*>.isNotNull(): BooleanBinding {
|
||||
return Bindings.createBooleanBinding({ this.value != null }, arrayOf(this))
|
||||
return Bindings.createBooleanBinding({ this.value != null }, *arrayOf(this))
|
||||
}
|
||||
|
||||
/**
|
||||
* Return first element of the observable list as observable value.
|
||||
* Return provided default value if the list is empty.
|
||||
*/
|
||||
@Suppress("SpreadOperator")
|
||||
fun <A> ObservableList<A>.firstOrDefault(default: ObservableValue<A?>, predicate: (A) -> Boolean): ObservableValue<A?> {
|
||||
return Bindings.createObjectBinding({ this.firstOrNull(predicate) ?: default.value }, arrayOf(this, default))
|
||||
return Bindings.createObjectBinding({ this.firstOrNull(predicate) ?: default.value }, *arrayOf(this, default))
|
||||
}
|
||||
|
||||
/**
|
||||
* Return first element of the observable list as observable value.
|
||||
* Return ObservableValue(null) if the list is empty.
|
||||
*/
|
||||
@Suppress("SpreadOperator")
|
||||
fun <A> ObservableList<A>.firstOrNullObservable(predicate: (A) -> Boolean): ObservableValue<A?> {
|
||||
return Bindings.createObjectBinding({ this.firstOrNull(predicate) }, arrayOf(this))
|
||||
return Bindings.createObjectBinding({ this.firstOrNull(predicate) }, *arrayOf(this))
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1,7 +1,6 @@
|
||||
apply plugin: 'kotlin'
|
||||
apply plugin: 'org.jetbrains.kotlin.jvm'
|
||||
apply plugin: 'net.corda.plugins.quasar-utils'
|
||||
apply plugin: 'net.corda.plugins.publish-utils'
|
||||
apply plugin: 'com.jfrog.artifactory'
|
||||
apply plugin: 'corda.common-publishing'
|
||||
|
||||
description 'Corda client mock modules'
|
||||
|
||||
@ -9,9 +8,9 @@ description 'Corda client mock modules'
|
||||
// build/reports/project/dependencies/index.html for green highlighted parts of the tree.
|
||||
|
||||
dependencies {
|
||||
compile project(":core")
|
||||
compile project(':finance:workflows')
|
||||
compile project(':finance:contracts')
|
||||
implementation project(":core")
|
||||
implementation project(':finance:workflows')
|
||||
implementation project(':finance:contracts')
|
||||
|
||||
testImplementation "org.junit.jupiter:junit-jupiter-api:${junit_jupiter_version}"
|
||||
testImplementation "junit:junit:$junit_version"
|
||||
@ -21,9 +20,9 @@ dependencies {
|
||||
testRuntimeOnly "org.junit.platform:junit-platform-launcher:${junit_platform_version}"
|
||||
|
||||
// Unit testing helpers.
|
||||
testCompile "org.assertj:assertj-core:${assertj_version}"
|
||||
testImplementation "org.assertj:assertj-core:${assertj_version}"
|
||||
|
||||
testCompile project(':test-utils')
|
||||
testImplementation project(':test-utils')
|
||||
}
|
||||
|
||||
jar {
|
||||
@ -39,6 +38,11 @@ jar {
|
||||
}
|
||||
}
|
||||
|
||||
publish {
|
||||
name jar.baseName
|
||||
publishing {
|
||||
publications {
|
||||
maven(MavenPublication) {
|
||||
artifactId jar.baseName
|
||||
from components.java
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,26 +1,15 @@
|
||||
apply plugin: 'kotlin'
|
||||
apply plugin: 'org.jetbrains.kotlin.jvm'
|
||||
apply plugin: 'net.corda.plugins.quasar-utils'
|
||||
apply plugin: 'net.corda.plugins.publish-utils'
|
||||
apply plugin: 'net.corda.plugins.api-scanner'
|
||||
apply plugin: 'com.jfrog.artifactory'
|
||||
apply plugin: 'corda.common-publishing'
|
||||
apply plugin: 'us.kirchmeier.capsule'
|
||||
|
||||
description 'Corda client RPC modules'
|
||||
|
||||
//noinspection GroovyAssignabilityCheck
|
||||
configurations {
|
||||
integrationTestCompile.extendsFrom testCompile
|
||||
integrationTestImplementation.extendsFrom testImplementation
|
||||
integrationTestRuntimeOnly.extendsFrom testRuntimeOnly
|
||||
|
||||
smokeTestCompile.extendsFrom compile
|
||||
smokeTestRuntimeOnly.extendsFrom runtimeOnly
|
||||
}
|
||||
|
||||
compileKotlin {
|
||||
kotlinOptions.jvmTarget = "1.8"
|
||||
}
|
||||
|
||||
compileTestKotlin {
|
||||
kotlinOptions.jvmTarget = "1.8"
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
@ -39,46 +28,17 @@ sourceSets {
|
||||
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 {
|
||||
compile project(':core')
|
||||
compile project(':node-api')
|
||||
implementation project(':core')
|
||||
implementation project(':node-api')
|
||||
implementation project(':serialization')
|
||||
|
||||
// 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"
|
||||
|
||||
@ -86,38 +46,37 @@ dependencies {
|
||||
testRuntimeOnly "org.junit.platform:junit-platform-launcher:${junit_platform_version}"
|
||||
|
||||
// Unit testing helpers.
|
||||
testCompile "org.jetbrains.kotlin:kotlin-test:$kotlin_version"
|
||||
testCompile "org.assertj:assertj-core:${assertj_version}"
|
||||
testImplementation "org.jetbrains.kotlin:kotlin-test:$kotlin_version"
|
||||
testImplementation "org.assertj:assertj-core:${assertj_version}"
|
||||
testImplementation "io.dropwizard.metrics:metrics-core:$metrics_version"
|
||||
|
||||
testCompile project(':node-driver')
|
||||
testCompile project(':client:mock')
|
||||
integrationTestCompile project(path: ':node-api', configuration: 'testArtifacts')
|
||||
testImplementation project(':node')
|
||||
testImplementation project(':node-driver')
|
||||
testImplementation project(':client:mock')
|
||||
testImplementation project(':core-test-utils')
|
||||
|
||||
// Smoke tests do NOT have any Node code on the classpath!
|
||||
smokeTestCompile project(':smoke-test-utils')
|
||||
smokeTestCompile project(':finance:contracts')
|
||||
smokeTestCompile project(':finance:workflows')
|
||||
smokeTestCompile project(':testing:cordapps:sleeping')
|
||||
smokeTestCompile "org.apache.logging.log4j:log4j-slf4j-impl:$log4j_version"
|
||||
smokeTestCompile "org.apache.logging.log4j:log4j-core:$log4j_version"
|
||||
smokeTestCompile "org.jetbrains.kotlin:kotlin-test:$kotlin_version"
|
||||
smokeTestCompile "org.assertj:assertj-core:${assertj_version}"
|
||||
smokeTestImplementation "junit:junit:$junit_version"
|
||||
smokeTestRuntimeOnly "org.junit.vintage:junit-vintage-engine:${junit_vintage_version}"
|
||||
smokeTestRuntimeOnly "org.junit.platform:junit-platform-launcher:${junit_platform_version}"
|
||||
integrationTestImplementation project(path: ':node-api', configuration: 'testArtifacts')
|
||||
integrationTestImplementation project(':common-configuration-parsing')
|
||||
integrationTestImplementation project(':finance:contracts')
|
||||
integrationTestImplementation project(':finance:workflows')
|
||||
integrationTestImplementation project(':test-utils')
|
||||
|
||||
// JDK11: required by Quasar at run-time
|
||||
smokeTestRuntimeOnly "com.esotericsoftware:kryo:$kryo_version"
|
||||
integrationTestImplementation "co.paralleluniverse:quasar-core:$quasar_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) {
|
||||
testClassesDirs = sourceSets.integrationTest.output.classesDirs
|
||||
classpath = sourceSets.integrationTest.runtimeClasspath
|
||||
}
|
||||
|
||||
task smokeTest(type: Test) {
|
||||
testClassesDirs = sourceSets.smokeTest.output.classesDirs
|
||||
classpath = sourceSets.smokeTest.runtimeClasspath
|
||||
jvmArgs test_add_opens
|
||||
jvmArgs test_add_exports
|
||||
}
|
||||
|
||||
jar {
|
||||
@ -127,6 +86,11 @@ jar {
|
||||
}
|
||||
}
|
||||
|
||||
publish {
|
||||
name jar.baseName
|
||||
publishing {
|
||||
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`() {
|
||||
val financeLocation = Cash::class.java.location.toPath().toString()
|
||||
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
|
||||
node.services.startFlow(CashIssueFlow(100.POUNDS, OpaqueBytes.of(1), identity), InvocationContext.shell()).flatMap { it.resultFuture }.getOrThrow()
|
||||
val outOfProcessRpc = ProcessUtilities.startJavaProcess<StandaloneCashRpcClient>(
|
||||
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
|
||||
}
|
||||
|
@ -1,12 +1,12 @@
|
||||
package net.corda.client.rpc
|
||||
|
||||
import com.nhaarman.mockito_kotlin.any
|
||||
import com.nhaarman.mockito_kotlin.atLeastOnce
|
||||
import com.nhaarman.mockito_kotlin.mock
|
||||
import com.nhaarman.mockito_kotlin.never
|
||||
import com.nhaarman.mockito_kotlin.times
|
||||
import com.nhaarman.mockito_kotlin.verify
|
||||
import com.nhaarman.mockito_kotlin.whenever
|
||||
import org.mockito.kotlin.any
|
||||
import org.mockito.kotlin.atLeastOnce
|
||||
import org.mockito.kotlin.mock
|
||||
import org.mockito.kotlin.never
|
||||
import org.mockito.kotlin.times
|
||||
import org.mockito.kotlin.verify
|
||||
import org.mockito.kotlin.whenever
|
||||
import net.corda.client.rpc.internal.RPCClient
|
||||
import net.corda.client.rpc.ext.RPCConnectionListener
|
||||
import net.corda.core.messaging.RPCOps
|
||||
|
@ -1,6 +1,6 @@
|
||||
package net.corda.client.rpc
|
||||
|
||||
import com.nhaarman.mockito_kotlin.mock
|
||||
import org.mockito.kotlin.mock
|
||||
import net.corda.client.rpc.RPCMultipleInterfacesTests.StringRPCOpsImpl.testPhrase
|
||||
import net.corda.core.crypto.SecureHash
|
||||
import net.corda.core.messaging.CordaRPCOps
|
||||
|
@ -89,6 +89,7 @@ class RPCStabilityTests {
|
||||
}
|
||||
|
||||
@Test(timeout=300_000)
|
||||
@Ignore("TODO JDK17:Fixme")
|
||||
fun `client and server dont leak threads`() {
|
||||
fun startAndStop() {
|
||||
rpcDriver {
|
||||
@ -121,6 +122,7 @@ class RPCStabilityTests {
|
||||
}
|
||||
|
||||
@Test(timeout=300_000)
|
||||
@Ignore("TODO JDK17:Fixme")
|
||||
fun `client doesnt leak threads when it fails to start`() {
|
||||
fun startAndStop() {
|
||||
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.
|
||||
*/
|
||||
@Test(timeout=300_000)
|
||||
@Ignore("TODO JDK17:Fixme")
|
||||
fun `server cleans up queues after disconnected clients`() {
|
||||
rpcDriver {
|
||||
val trackSubscriberOpsImpl = object : TrackSubscriberOps {
|
||||
|
@ -37,6 +37,7 @@ import net.corda.testing.node.internal.enclosedCordapp
|
||||
import net.corda.testing.node.internal.rpcDriver
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.assertj.core.api.Assertions.assertThatThrownBy
|
||||
import org.junit.Ignore
|
||||
import org.junit.Test
|
||||
import java.lang.IllegalStateException
|
||||
import java.lang.RuntimeException
|
||||
@ -53,6 +54,7 @@ import kotlin.test.assertFalse
|
||||
import kotlin.test.assertNull
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
@Ignore("TODO JDK17: Fixme")
|
||||
class CordaRPCClientReconnectionTest {
|
||||
|
||||
private val portAllocator = incrementalPortAllocation()
|
||||
|
@ -150,7 +150,6 @@ internal class RPCClientProxyHandler(
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("TooGenericExceptionCaught")
|
||||
private fun closeObservable(observable: UnicastSubject<Notification<*>>) {
|
||||
// Notify listeners of the observables that the connection is being terminated.
|
||||
try {
|
||||
@ -300,7 +299,7 @@ internal class RPCClientProxyHandler(
|
||||
class FailoverHandler(private val detected: () -> Unit = {},
|
||||
private val completed: () -> Unit = {},
|
||||
private val failed: () -> Unit = {}): FailoverEventListener {
|
||||
override fun failoverEvent(eventType: FailoverEventType?) {
|
||||
override fun failoverEvent(eventType: FailoverEventType) {
|
||||
when (eventType) {
|
||||
FailoverEventType.FAILURE_DETECTED -> { detected() }
|
||||
FailoverEventType.FAILOVER_COMPLETED -> { completed() }
|
||||
@ -589,7 +588,6 @@ internal class RPCClientProxyHandler(
|
||||
}
|
||||
if (observableIds != null) {
|
||||
log.debug { "Reaping ${observableIds.size} observables" }
|
||||
@Suppress("TooGenericExceptionCaught")
|
||||
try {
|
||||
sendMessage(RPCApi.ClientToServer.ObservablesClosed(observableIds))
|
||||
} catch(ex: Exception) {
|
||||
|
@ -9,9 +9,11 @@ import net.corda.testing.core.SerializationEnvironmentRule
|
||||
import net.corda.testing.node.internal.rpcDriver
|
||||
import net.corda.testing.node.internal.startRpcClient
|
||||
import org.assertj.core.api.Assertions.assertThatThrownBy
|
||||
import org.junit.Ignore
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
|
||||
@Ignore("TODO JDK17: Fixme")
|
||||
class RPCFailureTests {
|
||||
@Rule
|
||||
@JvmField
|
||||
|
@ -1,15 +1,12 @@
|
||||
apply plugin: 'kotlin'
|
||||
|
||||
apply plugin: 'net.corda.plugins.publish-utils'
|
||||
apply plugin: 'com.jfrog.artifactory'
|
||||
apply plugin: 'org.jetbrains.kotlin.jvm'
|
||||
apply plugin: 'corda.common-publishing'
|
||||
|
||||
dependencies {
|
||||
compile group: "org.jetbrains.kotlin", name: "kotlin-stdlib-jdk8", version: kotlin_version
|
||||
compile group: "org.jetbrains.kotlin", name: "kotlin-reflect", version: kotlin_version
|
||||
implementation group: "org.jetbrains.kotlin", name: "kotlin-reflect", version: kotlin_version
|
||||
|
||||
compile group: "com.typesafe", name: "config", version: typesafe_config_version
|
||||
implementation group: "com.typesafe", name: "config", version: typesafe_config_version
|
||||
|
||||
compile project(":common-validation")
|
||||
implementation project(":common-validation")
|
||||
|
||||
testImplementation "org.junit.jupiter:junit-jupiter-api:${junit_jupiter_version}"
|
||||
testImplementation "junit:junit:$junit_version"
|
||||
@ -18,14 +15,20 @@ dependencies {
|
||||
testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:${junit_jupiter_version}"
|
||||
testRuntimeOnly "org.junit.platform:junit-platform-launcher:${junit_platform_version}"
|
||||
|
||||
testCompile group: "org.jetbrains.kotlin", name: "kotlin-test", version: kotlin_version
|
||||
testCompile group: "org.assertj", name: "assertj-core", version: assertj_version
|
||||
testImplementation group: "org.jetbrains.kotlin", name: "kotlin-test", version: kotlin_version
|
||||
testImplementation group: "org.assertj", name: "assertj-core", version: assertj_version
|
||||
}
|
||||
|
||||
jar {
|
||||
baseName 'corda-common-configuration-parsing'
|
||||
}
|
||||
|
||||
publish {
|
||||
name jar.baseName
|
||||
publishing {
|
||||
publications {
|
||||
maven(MavenPublication) {
|
||||
artifactId jar.baseName
|
||||
from components.java
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,28 +1,26 @@
|
||||
import org.apache.tools.ant.filters.ReplaceTokens
|
||||
|
||||
apply plugin: 'kotlin'
|
||||
apply plugin: 'net.corda.plugins.publish-utils'
|
||||
apply plugin: 'com.jfrog.artifactory'
|
||||
apply plugin: 'org.jetbrains.kotlin.jvm'
|
||||
apply plugin: 'corda.common-publishing'
|
||||
|
||||
dependencies {
|
||||
compile group: "org.jetbrains.kotlin", name: "kotlin-stdlib-jdk8", version: kotlin_version
|
||||
compile group: "org.jetbrains.kotlin", name: "kotlin-reflect", version: kotlin_version
|
||||
implementation group: "org.jetbrains.kotlin", name: "kotlin-reflect", version: kotlin_version
|
||||
|
||||
compile group: "com.typesafe", name: "config", version: typesafe_config_version
|
||||
implementation group: "com.typesafe", name: "config", version: typesafe_config_version
|
||||
|
||||
// Log4J: logging framework
|
||||
compile "org.apache.logging.log4j:log4j-core:$log4j_version"
|
||||
implementation "org.apache.logging.log4j:log4j-core:$log4j_version"
|
||||
|
||||
compile "com.jcabi:jcabi-manifests:$jcabi_manifests_version"
|
||||
implementation "com.jcabi:jcabi-manifests:$jcabi_manifests_version"
|
||||
|
||||
// Need to depend on one other Corda project in order to get hold of a valid manifest for the tests
|
||||
testCompile project(":common-validation")
|
||||
testImplementation project(":common-validation")
|
||||
|
||||
// test dependencies
|
||||
testImplementation "junit:junit:$junit_version"
|
||||
testCompile group: "org.jetbrains.kotlin", name: "kotlin-test", version: kotlin_version
|
||||
testCompile "org.mockito:mockito-core:$mockito_version"
|
||||
testCompile "com.natpryce:hamkrest:$hamkrest_version"
|
||||
testImplementation group: "org.jetbrains.kotlin", name: "kotlin-test", version: kotlin_version
|
||||
testImplementation "org.mockito:mockito-core:$mockito_version"
|
||||
testImplementation "com.natpryce:hamkrest:$hamkrest_version"
|
||||
}
|
||||
|
||||
|
||||
@ -38,6 +36,12 @@ jar {
|
||||
baseName 'corda-common-logging'
|
||||
}
|
||||
|
||||
publish {
|
||||
name jar.baseName
|
||||
publishing {
|
||||
publications {
|
||||
maven(MavenPublication) {
|
||||
artifactId jar.baseName
|
||||
from components.java
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -9,4 +9,4 @@ package net.corda.common.logging
|
||||
* (originally added to source control for ease of use)
|
||||
*/
|
||||
|
||||
internal const val CURRENT_MAJOR_RELEASE = "4.11-SNAPSHOT"
|
||||
internal const val CURRENT_MAJOR_RELEASE = "4.12-SNAPSHOT"
|
@ -1,17 +1,19 @@
|
||||
apply plugin: 'kotlin'
|
||||
|
||||
apply plugin: 'net.corda.plugins.publish-utils'
|
||||
apply plugin: 'com.jfrog.artifactory'
|
||||
apply plugin: 'org.jetbrains.kotlin.jvm'
|
||||
apply plugin: 'corda.common-publishing'
|
||||
|
||||
dependencies {
|
||||
compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
|
||||
compile "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
|
||||
implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
|
||||
}
|
||||
|
||||
jar {
|
||||
baseName 'corda-common-validation'
|
||||
}
|
||||
|
||||
publish {
|
||||
name jar.baseName
|
||||
publishing {
|
||||
publications {
|
||||
maven(MavenPublication) {
|
||||
artifactId jar.baseName
|
||||
from components.java
|
||||
}
|
||||
}
|
||||
}
|
@ -1,18 +1,23 @@
|
||||
// This contains the SwapIdentitiesFlow which can be used for exchanging confidential identities as part of a flow.
|
||||
// TODO: Merge this into core: the original plan was to develop it independently but in practice it's too widely used to break compatibility now, as finance uses it.
|
||||
apply plugin: 'kotlin'
|
||||
apply plugin: 'net.corda.plugins.publish-utils'
|
||||
apply plugin: 'net.corda.plugins.quasar-utils'
|
||||
apply plugin: 'net.corda.plugins.cordapp'
|
||||
apply plugin: 'com.jfrog.artifactory'
|
||||
apply plugin: 'corda.common-publishing'
|
||||
|
||||
description 'Corda Experimental Confidential Identities'
|
||||
|
||||
dependencies {
|
||||
cordaCompile project(':core')
|
||||
cordapp {
|
||||
targetPlatformVersion corda_platform_version.toInteger()
|
||||
}
|
||||
|
||||
testCompile "org.jetbrains.kotlin:kotlin-test:$kotlin_version"
|
||||
dependencies {
|
||||
cordaProvided project(':core')
|
||||
|
||||
api "org.slf4j:slf4j-api:$slf4j_version"
|
||||
|
||||
testImplementation "org.jetbrains.kotlin:kotlin-test:$kotlin_version"
|
||||
testImplementation "org.junit.jupiter:junit-jupiter-api:${junit_jupiter_version}"
|
||||
testImplementation "co.paralleluniverse:quasar-core:$quasar_version"
|
||||
testImplementation "junit:junit:$junit_version"
|
||||
|
||||
testRuntimeOnly "org.junit.vintage:junit-vintage-engine:${junit_vintage_version}"
|
||||
@ -20,19 +25,27 @@ dependencies {
|
||||
testRuntimeOnly "org.junit.platform:junit-platform-launcher:${junit_platform_version}"
|
||||
|
||||
// Guava: Google test library (collections test suite)
|
||||
testCompile "com.google.guava:guava-testlib:$guava_version"
|
||||
testImplementation "com.google.guava:guava-testlib:$guava_version"
|
||||
|
||||
// Bring in the MockNode infrastructure for writing protocol unit tests.
|
||||
testCompile project(":node-driver")
|
||||
testImplementation project(":node")
|
||||
testImplementation project(":node-api")
|
||||
testImplementation project(":node-driver")
|
||||
testImplementation project(":core-test-utils")
|
||||
testImplementation project(':finance:contracts')
|
||||
testImplementation project(':finance:workflows')
|
||||
|
||||
// AssertJ: for fluent assertions for testing
|
||||
testCompile "org.assertj:assertj-core:$assertj_version"
|
||||
testImplementation "org.assertj:assertj-core:$assertj_version"
|
||||
testImplementation "com.natpryce:hamkrest:$hamkrest_version"
|
||||
testImplementation "io.reactivex:rxjava:$rxjava_version"
|
||||
}
|
||||
|
||||
jar {
|
||||
baseName 'corda-confidential-identities'
|
||||
publishing {
|
||||
publications {
|
||||
maven(MavenPublication) {
|
||||
artifactId 'corda-confidential-identities'
|
||||
from components.cordapp
|
||||
}
|
||||
}
|
||||
|
||||
publish {
|
||||
name jar.baseName
|
||||
}
|
||||
|
@ -2,64 +2,26 @@
|
||||
<Configuration status="info" shutdownHook="disable">
|
||||
|
||||
<Properties>
|
||||
<Property name="log-path">${sys:log-path:-logs}</Property>
|
||||
<Property name="log-name">node-${hostName}</Property>
|
||||
<Property name="diagnostic-log-name">diagnostic-${hostName}</Property>
|
||||
<Property name="archive">${log-path}/archive</Property>
|
||||
<Property name="defaultLogLevel">${sys:defaultLogLevel:-info}</Property>
|
||||
<Property name="consoleLogLevel">${sys:consoleLogLevel:-error}</Property>
|
||||
<Property name="log_path">${sys:log-path:-logs}</Property>
|
||||
<Property name="log_name">node-${hostName}</Property>
|
||||
<Property name="diagnostic_log_name">diagnostic-${hostName}</Property>
|
||||
<Property name="archive">${log_path}/archive</Property>
|
||||
<Property name="default_log_level">${sys:defaultLogLevel:-info}</Property>
|
||||
<Property name="console_log_level">${sys:consoleLogLevel:-error}</Property>
|
||||
</Properties>
|
||||
|
||||
<Appenders>
|
||||
<ScriptAppenderSelector name="Console-Selector">
|
||||
<Script language="nashorn"><![CDATA[
|
||||
var System = Java.type('java.lang.System');
|
||||
var level = System.getProperty("consoleLogLevel");
|
||||
var enabled = System.getProperty("consoleLoggingEnabled");
|
||||
enabled == "true" && (level == "debug" || level == "trace") ? "Console-Debug-Appender" : "Console-Appender";
|
||||
]]></Script>
|
||||
<AppenderSet>
|
||||
|
||||
<!-- The default console appender - prints no exception information -->
|
||||
<Console name="Console-Appender" target="SYSTEM_OUT">
|
||||
<PatternLayout>
|
||||
<ScriptPatternSelector
|
||||
defaultPattern="%highlight{[%level{length=5}] %date{HH:mm:ssZ} [%t] %c{2}.%method - %msg%n%throwable{0}}{INFO=white,WARN=red,FATAL=bright red}">
|
||||
<Script name="MDCSelector" language="javascript"><![CDATA[
|
||||
result = null;
|
||||
if (!logEvent.getContextData().size() == 0) {
|
||||
result = "WithMDC";
|
||||
} else {
|
||||
result = null;
|
||||
}
|
||||
result;
|
||||
]]>
|
||||
</Script>
|
||||
<PatternMatch key="WithMDC" pattern="%highlight{[%level{length=5}] %date{HH:mm:ssZ} [%t] %c{2}.%method - %msg %X%n%throwable{0}}{INFO=white,WARN=red,FATAL=bright red}"/>
|
||||
</ScriptPatternSelector>
|
||||
</PatternLayout>
|
||||
<PatternLayout
|
||||
pattern="%highlight{[%level{length=5}] %date{HH:mm:ssZ} [%t] %c{2}.%method - %msg%n%throwable{0}}{INFO=white,WARN=red,FATAL=bright red}"/>
|
||||
</Console>
|
||||
|
||||
<!-- The console appender when debug or trace level logging is specified. Prints full stack trace -->
|
||||
<Console name="Console-Debug-Appender" target="SYSTEM_OUT">
|
||||
<PatternLayout>
|
||||
<ScriptPatternSelector defaultPattern="%highlight{[%level{length=5}] %date{HH:mm:ssZ} [%t] %c{2}.%method - %msg%n%throwable{}}{INFO=white,WARN=red,FATAL=bright red}">
|
||||
<Script name="MDCSelector" language="javascript"><![CDATA[
|
||||
result = null;
|
||||
if (!logEvent.getContextData().size() == 0) {
|
||||
result = "WithMDC";
|
||||
} else {
|
||||
result = null;
|
||||
}
|
||||
result;
|
||||
]]>
|
||||
</Script>
|
||||
<PatternMatch key="WithMDC" pattern="%highlight{[%level{length=5}] %date{HH:mm:ssZ} [%t] %c{2}.%method - %msg %X%n%throwable{}}{INFO=white,WARN=red,FATAL=bright red}"/>
|
||||
</ScriptPatternSelector>
|
||||
</PatternLayout>
|
||||
<PatternLayout
|
||||
pattern="%highlight{[%level{length=5}] %date{HH:mm:ssZ} [%t] %c{2}.%method - %msg%n%throwable{}}{INFO=white,WARN=red,FATAL=bright red}"/>
|
||||
</Console>
|
||||
</AppenderSet>
|
||||
</ScriptAppenderSelector>
|
||||
|
||||
<!-- Required for printBasicInfo -->
|
||||
<Console name="Console-Appender-Println" target="SYSTEM_OUT">
|
||||
@ -69,24 +31,10 @@
|
||||
<!-- Will generate up to 500 log files for a given day. Adjust this number according to the available storage.
|
||||
During every rollover it will delete those that are older than 60 days, but keep the most recent 10 GB -->
|
||||
<RollingRandomAccessFile name="RollingFile-Appender"
|
||||
fileName="${log-path}/${log-name}.log"
|
||||
filePattern="${archive}/${log-name}.%date{yyyy-MM-dd}-%i.log.gz">
|
||||
fileName="${log_path}/${log_name}.log"
|
||||
filePattern="${archive}/${log_name}.%date{yyyy-MM-dd}-%i.log.gz">
|
||||
|
||||
<PatternLayout>
|
||||
<ScriptPatternSelector defaultPattern="[%-5level] %date{ISO8601}{UTC}Z [%t] %c{2}.%method - %msg%n">
|
||||
<Script name="MDCSelector" language="javascript"><![CDATA[
|
||||
result = null;
|
||||
if (!logEvent.getContextData().size() == 0) {
|
||||
result = "WithMDC";
|
||||
} else {
|
||||
result = null;
|
||||
}
|
||||
result;
|
||||
]]>
|
||||
</Script>
|
||||
<PatternMatch key="WithMDC" pattern="[%-5level] %date{ISO8601}{UTC}Z [%t] %c{2}.%method - %msg %X%n"/>
|
||||
</ScriptPatternSelector>
|
||||
</PatternLayout>
|
||||
<PatternLayout pattern="[%-5level] %date{ISO8601}{UTC}Z [%t] %c{2}.%method - %msg%n"/>
|
||||
|
||||
<Policies>
|
||||
<TimeBasedTriggeringPolicy/>
|
||||
@ -95,7 +43,7 @@
|
||||
|
||||
<DefaultRolloverStrategy min="1" max="500">
|
||||
<Delete basePath="${archive}" maxDepth="1">
|
||||
<IfFileName glob="${log-name}*.log.gz"/>
|
||||
<IfFileName glob="${log_name}*.log.gz"/>
|
||||
<IfLastModified age="60d">
|
||||
<IfAny>
|
||||
<IfAccumulatedFileSize exceeds="10 GB"/>
|
||||
@ -109,24 +57,10 @@
|
||||
<!-- Will generate up to 100 log files for a given day. During every rollover it will delete
|
||||
those that are older than 60 days, but keep the most recent 10 GB -->
|
||||
<RollingRandomAccessFile name="Diagnostic-RollingFile-Appender"
|
||||
fileName="${log-path}/${diagnostic-log-name}.log"
|
||||
filePattern="${archive}/${diagnostic-log-name}.%date{yyyy-MM-dd}-%i.log.gz">
|
||||
fileName="${log_path}/${diagnostic_log_name}.log"
|
||||
filePattern="${archive}/${diagnostic_log_name}.%date{yyyy-MM-dd}-%i.log.gz">
|
||||
|
||||
<PatternLayout>
|
||||
<ScriptPatternSelector defaultPattern="[%-5level] %date{ISO8601}{UTC}Z [%t] %c{2}.%method - %msg%n">
|
||||
<Script name="MDCSelector" language="javascript"><![CDATA[
|
||||
result = null;
|
||||
if (!logEvent.getContextData().size() == 0) {
|
||||
result = "WithMDC";
|
||||
} else {
|
||||
result = null;
|
||||
}
|
||||
result;
|
||||
]]>
|
||||
</Script>
|
||||
<PatternMatch key="WithMDC" pattern="[%-5level] %date{ISO8601}{UTC}Z [%t] %c{2}.%method - %msg %X%n"/>
|
||||
</ScriptPatternSelector>
|
||||
</PatternLayout>
|
||||
<PatternLayout pattern="[%-5level] %date{ISO8601}{UTC}Z [%t] %c{2}.%method - %msg%n"/>
|
||||
|
||||
<Policies>
|
||||
<TimeBasedTriggeringPolicy/>
|
||||
@ -135,7 +69,7 @@
|
||||
|
||||
<DefaultRolloverStrategy min="1" max="100">
|
||||
<Delete basePath="${archive}" maxDepth="1">
|
||||
<IfFileName glob="${diagnostic-log-name}*.log.gz"/>
|
||||
<IfFileName glob="${diagnostic_log_name}*.log.gz"/>
|
||||
<IfLastModified age="60d">
|
||||
<IfAny>
|
||||
<IfAccumulatedFileSize exceeds="10 GB"/>
|
||||
@ -147,7 +81,7 @@
|
||||
</RollingRandomAccessFile>
|
||||
|
||||
<RollingFile name="Checkpoint-Agent-RollingFile-Appender"
|
||||
fileName="${log-path}/checkpoints_agent-${date:yyyyMMdd-HHmmss}.log"
|
||||
fileName="${log_path}/checkpoints_agent-${date:yyyyMMdd-HHmmss}.log"
|
||||
filePattern="${archive}/checkpoints_agent.%date{yyyy-MM-dd}-%i.log.gz">
|
||||
|
||||
<PatternLayout pattern="[%-5level] %date{ISO8601}{UTC}Z [%t] %c{2}.%method - %msg%n"/>
|
||||
@ -171,7 +105,7 @@
|
||||
</RollingFile>
|
||||
|
||||
<Rewrite name="Console-ErrorCode-Selector">
|
||||
<AppenderRef ref="Console-Selector"/>
|
||||
<AppenderRef ref="Console-Appender"/>
|
||||
</Rewrite>
|
||||
|
||||
<Rewrite name="Console-ErrorCode-Appender-Println">
|
||||
@ -187,8 +121,8 @@
|
||||
</Appenders>
|
||||
|
||||
<Loggers>
|
||||
<Root level="${defaultLogLevel}">
|
||||
<AppenderRef ref="Console-ErrorCode-Selector" level="${consoleLogLevel}"/>
|
||||
<Root level="${default_log_level}">
|
||||
<AppenderRef ref="Console-ErrorCode-Selector" level="${console_log_level}"/>
|
||||
<AppenderRef ref="RollingFile-ErrorCode-Appender"/>
|
||||
</Root>
|
||||
<Logger name="BasicInfo" additivity="false">
|
||||
|
@ -3,26 +3,24 @@
|
||||
# their own projects. So don't get fancy with syntax!
|
||||
# Fancy syntax - multi pass ${whatever} replacement
|
||||
|
||||
cordaVersion=4.11
|
||||
cordaVersion=4.12
|
||||
versionSuffix=SNAPSHOT
|
||||
gradlePluginsVersion=5.0.12
|
||||
kotlinVersion=1.2.71
|
||||
java8MinUpdateVersion=171
|
||||
cordaShellVersion=4.12-HC01
|
||||
gradlePluginsVersion=5.1.1
|
||||
artifactoryContextUrl=https://software.r3.com/artifactory
|
||||
internalPublishVersion=1.+
|
||||
# ***************************************************************#
|
||||
# When incrementing platformVersion make sure to update #
|
||||
# net.corda.core.internal.CordaUtilsKt.PLATFORM_VERSION as well. #
|
||||
# ***************************************************************#
|
||||
platformVersion=13
|
||||
platformVersion=14
|
||||
openTelemetryVersion=1.20.1
|
||||
openTelemetrySemConvVersion=1.20.1-alpha
|
||||
guavaVersion=28.0-jre
|
||||
# Quasar version to use with Java 8:
|
||||
quasarVersion=0.7.16_r3
|
||||
# Quasar version to use with Java 11:
|
||||
quasarVersion11=0.8.1_r3
|
||||
jdkClassifier11=jdk11
|
||||
quasarVersion=0.9.0_r3
|
||||
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.
|
||||
bouncycastleVersion=1.75
|
||||
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.
|
||||
# We must configure it manually to use the latest capsule version.
|
||||
capsuleVersion=1.0.3
|
||||
asmVersion=7.1
|
||||
artemisVersion=2.19.1
|
||||
capsuleVersion=1.0.4_r3
|
||||
asmVersion=9.5
|
||||
artemisVersion=2.29.0
|
||||
# TODO Upgrade Jackson only when corda is using kotlin 1.3.10
|
||||
jacksonVersion=2.13.5
|
||||
jacksonKotlinVersion=2.9.7
|
||||
@ -57,11 +55,11 @@ jerseyVersion=2.25
|
||||
servletVersion=4.0.1
|
||||
assertjVersion=3.12.2
|
||||
slf4JVersion=1.7.30
|
||||
log4JVersion=2.17.1
|
||||
okhttpVersion=3.14.9
|
||||
log4JVersion=2.20.0
|
||||
okhttpVersion=4.11.0
|
||||
nettyVersion=4.1.77.Final
|
||||
fileuploadVersion=1.4
|
||||
kryoVersion=4.0.2
|
||||
kryoVersion=5.5.0
|
||||
kryoSerializerVersion=0.43
|
||||
# Legacy JUnit 4 version
|
||||
junitVersion=4.12
|
||||
@ -71,8 +69,8 @@ junitVersion=4.12
|
||||
junitVintageVersion=5.5.0-RC1
|
||||
junitJupiterVersion=5.5.0-RC1
|
||||
junitPlatformVersion=1.5.0-RC1
|
||||
mockitoVersion=2.28.2
|
||||
mockitoKotlinVersion=1.6.0
|
||||
mockitoVersion=5.5.0
|
||||
mockitoKotlinVersion=4.1.0
|
||||
hamkrestVersion=1.7.0.0
|
||||
joptSimpleVersion=5.0.2
|
||||
jansiVersion=1.18
|
||||
@ -80,7 +78,7 @@ hibernateVersion=5.6.14.Final
|
||||
# h2Version - Update docs if renamed or removed.
|
||||
h2Version=2.2.224
|
||||
rxjavaVersion=1.3.8
|
||||
dokkaVersion=0.10.1
|
||||
dokkaVersion=1.8.20
|
||||
eddsaVersion=0.3.0
|
||||
dependencyCheckerVersion=5.2.0
|
||||
commonsCollectionsVersion=4.3
|
||||
@ -97,13 +95,8 @@ protonjVersion=0.33.0
|
||||
snappyVersion=0.4
|
||||
jcabiManifestsVersion=1.1
|
||||
picocliVersion=3.9.6
|
||||
commonsLangVersion=3.9
|
||||
commonsIoVersion=2.6
|
||||
controlsfxVersion=8.40.15
|
||||
# FontAwesomeFX for Java8
|
||||
fontawesomefxCommonsJava8Version=8.15
|
||||
fontawesomefxFontawesomeJava8Version=4.7.0-5
|
||||
# FontAwesomeFX for a more recent version of the Java Runtime (class file version 55.0)
|
||||
fontawesomefxCommonsVersion=11.0
|
||||
fontawesomefxFontawesomeVersion=4.7.0-11
|
||||
javaassistVersion=3.27.0-GA
|
||||
javaassistVersion=3.29.2-GA
|
||||
|
@ -1,11 +1,12 @@
|
||||
apply plugin: 'kotlin'
|
||||
apply plugin: 'kotlin-jpa'
|
||||
apply plugin: 'org.jetbrains.kotlin.jvm'
|
||||
apply plugin: 'org.jetbrains.kotlin.plugin.jpa'
|
||||
apply plugin: 'net.corda.plugins.quasar-utils'
|
||||
apply plugin: 'idea'
|
||||
|
||||
description 'Corda core tests'
|
||||
|
||||
configurations {
|
||||
integrationTestCompile.extendsFrom testCompile
|
||||
integrationTestImplementation.extendsFrom testImplementation
|
||||
integrationTestRuntimeOnly.extendsFrom testRuntimeOnly
|
||||
|
||||
smokeTestCompile.extendsFrom compile
|
||||
@ -41,47 +42,65 @@ sourceSets {
|
||||
|
||||
processSmokeTestResources {
|
||||
// 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'
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
||||
testImplementation "org.junit.jupiter:junit-jupiter-api:${junit_jupiter_version}"
|
||||
testImplementation "junit:junit:$junit_version"
|
||||
testRuntimeOnly "org.junit.vintage:junit-vintage-engine:${junit_vintage_version}"
|
||||
testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:${junit_jupiter_version}"
|
||||
testRuntimeOnly "org.junit.platform:junit-platform-launcher:${junit_platform_version}"
|
||||
|
||||
testCompile "commons-fileupload:commons-fileupload:$fileupload_version"
|
||||
testCompile project(":core")
|
||||
testCompile project(path: ':core', configuration: 'testArtifacts')
|
||||
testImplementation "commons-fileupload:commons-fileupload:$fileupload_version"
|
||||
testImplementation project(":core")
|
||||
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)
|
||||
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.
|
||||
testCompile project(":node-driver")
|
||||
testImplementation project(":node-driver")
|
||||
|
||||
compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
|
||||
compile "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
|
||||
testCompile "org.jetbrains.kotlin:kotlin-test:$kotlin_version"
|
||||
implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
|
||||
testImplementation "org.jetbrains.kotlin:kotlin-test:$kotlin_version"
|
||||
|
||||
// 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
|
||||
compile "org.slf4j:jcl-over-slf4j:$slf4j_version"
|
||||
compile "org.slf4j:slf4j-api:$slf4j_version"
|
||||
implementation "org.slf4j:jcl-over-slf4j:$slf4j_version"
|
||||
implementation "org.slf4j:slf4j-api:$slf4j_version"
|
||||
|
||||
// AssertJ: for fluent assertions for testing
|
||||
testCompile "org.assertj:assertj-core:${assertj_version}"
|
||||
testImplementation "org.assertj:assertj-core:${assertj_version}"
|
||||
|
||||
// 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!
|
||||
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 "junit:junit:$junit_version"
|
||||
|
||||
@ -93,11 +112,11 @@ dependencies {
|
||||
smokeTestCompile "org.assertj:assertj-core:${assertj_version}"
|
||||
|
||||
// used by FinalityFlowTests
|
||||
testCompile project(':testing:cordapps:cashobservers')
|
||||
testImplementation project(':testing:cordapps:cashobservers')
|
||||
}
|
||||
|
||||
configurations {
|
||||
testArtifacts.extendsFrom testRuntimeClasspath
|
||||
testArtifacts.extendsFrom testRuntimeOnlyClasspath
|
||||
}
|
||||
|
||||
tasks.withType(Test).configureEach {
|
||||
@ -124,6 +143,16 @@ task smokeTest(type: Test) {
|
||||
dependsOn smokeTestJar
|
||||
testClassesDirs = sourceSets.smokeTest.output.classesDirs
|
||||
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
|
||||
|
@ -4,7 +4,8 @@ import co.paralleluniverse.fibers.Suspendable
|
||||
import net.corda.core.flows.FlowLogic
|
||||
import net.corda.core.flows.StartableByRPC
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.internal.*
|
||||
import net.corda.core.internal.PLATFORM_VERSION
|
||||
import net.corda.core.internal.copyToDirectory
|
||||
import net.corda.core.messaging.startFlow
|
||||
import net.corda.core.utilities.getOrThrow
|
||||
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.Before
|
||||
import org.junit.Test
|
||||
import java.nio.file.Paths
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
import java.util.jar.JarFile
|
||||
import kotlin.streams.toList
|
||||
import kotlin.io.path.Path
|
||||
import kotlin.io.path.createDirectories
|
||||
import kotlin.io.path.div
|
||||
import kotlin.io.path.listDirectoryEntries
|
||||
|
||||
class NodeVersioningTest {
|
||||
private companion object {
|
||||
@ -67,9 +70,7 @@ class NodeVersioningTest {
|
||||
fun `platform version from RPC`() {
|
||||
val cordappsDir = (factory.baseDirectory(aliceConfig) / NodeProcess.CORDAPPS_DIR_NAME).createDirectories()
|
||||
// Find the jar file for the smoke tests of this module
|
||||
val selfCordapp = Paths.get("build", "libs").list {
|
||||
it.filter { "-smokeTests" in it.toString() }.toList().single()
|
||||
}
|
||||
val selfCordapp = Path("build", "libs").listDirectoryEntries("*-smokeTests*").single()
|
||||
selfCordapp.copyToDirectory(cordappsDir)
|
||||
|
||||
factory.create(aliceConfig).use { alice ->
|
||||
|
@ -3,11 +3,16 @@ package net.corda.coretests.cordapp
|
||||
import co.paralleluniverse.fibers.Suspendable
|
||||
import net.corda.core.crypto.Crypto.generateKeyPair
|
||||
import net.corda.core.crypto.sign
|
||||
import net.corda.core.flows.*
|
||||
import net.corda.core.flows.FlowInfo
|
||||
import net.corda.core.flows.FlowLogic
|
||||
import net.corda.core.flows.FlowSession
|
||||
import net.corda.core.flows.InitiatedBy
|
||||
import net.corda.core.flows.InitiatingFlow
|
||||
import net.corda.core.flows.StartableByRPC
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.core.identity.PartyAndCertificate
|
||||
import net.corda.core.internal.*
|
||||
import net.corda.core.internal.copyToDirectory
|
||||
import net.corda.core.messaging.startFlow
|
||||
import net.corda.core.node.NodeInfo
|
||||
import net.corda.core.serialization.serialize
|
||||
@ -29,12 +34,16 @@ import org.junit.After
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import java.nio.file.Path
|
||||
import java.nio.file.Paths
|
||||
import java.security.KeyPair
|
||||
import java.security.PrivateKey
|
||||
import java.security.PublicKey
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
import kotlin.streams.toList
|
||||
import kotlin.io.path.Path
|
||||
import kotlin.io.path.createDirectories
|
||||
import kotlin.io.path.div
|
||||
import kotlin.io.path.name
|
||||
import kotlin.io.path.useDirectoryEntries
|
||||
import kotlin.io.path.writeBytes
|
||||
|
||||
class CordappSmokeTest {
|
||||
private companion object {
|
||||
@ -79,9 +88,7 @@ class CordappSmokeTest {
|
||||
val baseDir = factory.baseDirectory(aliceConfig)
|
||||
val cordappsDir = (baseDir / CORDAPPS_DIR_NAME).createDirectories()
|
||||
// Find the jar file for the smoke tests of this module
|
||||
val selfCordapp = Paths.get("build", "libs").list {
|
||||
it.filter { "-smokeTests" in it.toString() }.toList().single()
|
||||
}
|
||||
val selfCordapp = Path("build", "libs").useDirectoryEntries { it.single { "-smokeTests" in it.toString() } }
|
||||
selfCordapp.copyToDirectory(cordappsDir)
|
||||
|
||||
// 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 future = connectionToAlice.proxy.startFlow(CordappSmokeTest::GatherContextsFlow, aliceIdentity).returnValue
|
||||
val (sessionInitContext, sessionConfirmContext) = future.getOrThrow()
|
||||
val selfCordappName = selfCordapp.fileName.toString().removeSuffix(".jar")
|
||||
val selfCordappName = selfCordapp.name.removeSuffix(".jar")
|
||||
assertThat(sessionInitContext.appName).isEqualTo(selfCordappName)
|
||||
assertThat(sessionConfirmContext.appName).isEqualTo(selfCordappName)
|
||||
}
|
||||
@ -139,7 +146,7 @@ class CordappSmokeTest {
|
||||
val dummyKeyPair = generateKeyPair()
|
||||
val nodeInfo = createNodeInfoWithSingleIdentity(CordaX500Name(organisation = "Bob Corp", locality = "Madrid", country = "ES"), dummyKeyPair, dummyKeyPair.public)
|
||||
val signedNodeInfo = signWith(nodeInfo, listOf(dummyKeyPair.private))
|
||||
(additionalNodeInfoDir / "nodeInfo-41408E093F95EAD51F6892C34DEB65AE1A3569A4B0E5744769A1B485AF8E04B5").write(signedNodeInfo.serialize().bytes)
|
||||
(additionalNodeInfoDir / "nodeInfo-41408E093F95EAD51F6892C34DEB65AE1A3569A4B0E5744769A1B485AF8E04B5").writeBytes(signedNodeInfo.serialize().bytes)
|
||||
}
|
||||
|
||||
private fun createNodeInfoWithSingleIdentity(name: CordaX500Name, nodeKeyPair: KeyPair, identityCertPublicKey: PublicKey): NodeInfo {
|
||||
|
@ -1,9 +1,17 @@
|
||||
package net.corda.coretests.contracts
|
||||
|
||||
import com.nhaarman.mockito_kotlin.doReturn
|
||||
import com.nhaarman.mockito_kotlin.mock
|
||||
import com.nhaarman.mockito_kotlin.whenever
|
||||
import net.corda.core.contracts.*
|
||||
import net.corda.core.contracts.AlwaysAcceptAttachmentConstraint
|
||||
import net.corda.core.contracts.AutomaticPlaceholderConstraint
|
||||
import net.corda.core.contracts.BelongsToContract
|
||||
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.SecureHash
|
||||
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.CordaX500Name
|
||||
import net.corda.core.internal.canBeTransitionedFrom
|
||||
import net.corda.core.internal.inputStream
|
||||
import net.corda.core.internal.read
|
||||
import net.corda.core.internal.requireSupportedHashType
|
||||
import net.corda.core.internal.toPath
|
||||
import net.corda.core.node.NotaryInfo
|
||||
import net.corda.core.node.services.IdentityService
|
||||
import net.corda.core.transactions.LedgerTransaction
|
||||
import net.corda.core.transactions.SignedTransaction
|
||||
import net.corda.core.transactions.WireTransaction
|
||||
import net.corda.finance.POUNDS
|
||||
import net.corda.finance.`issued by`
|
||||
import net.corda.finance.contracts.asset.Cash
|
||||
import net.corda.finance.`issued by`
|
||||
import net.corda.testing.common.internal.testNetworkParameters
|
||||
import net.corda.testing.core.ALICE_NAME
|
||||
import net.corda.testing.core.DUMMY_NOTARY_NAME
|
||||
@ -36,7 +43,15 @@ import net.corda.testing.core.internal.SelfCleaningDir
|
||||
import net.corda.testing.internal.MockCordappProvider
|
||||
import net.corda.testing.node.MockServices
|
||||
import net.corda.testing.node.ledger
|
||||
import org.junit.*
|
||||
import org.junit.AfterClass
|
||||
import org.junit.Before
|
||||
import org.junit.BeforeClass
|
||||
import org.junit.Ignore
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.mockito.kotlin.doReturn
|
||||
import org.mockito.kotlin.mock
|
||||
import org.mockito.kotlin.whenever
|
||||
import java.security.PublicKey
|
||||
import java.util.jar.Attributes
|
||||
import kotlin.test.assertFailsWith
|
||||
@ -91,8 +106,8 @@ class ConstraintsPropagationTests {
|
||||
},
|
||||
networkParameters = testNetworkParameters(minimumPlatformVersion = 4)
|
||||
.copy(whitelistedContractImplementations = mapOf(
|
||||
Cash.PROGRAM_ID to listOf(SecureHash.zeroHash, SecureHash.allOnesHash),
|
||||
noPropagationContractClassName to listOf(SecureHash.zeroHash)),
|
||||
Cash.PROGRAM_ID to listOf(zeroHash, allOnesHash),
|
||||
noPropagationContractClassName to listOf(zeroHash)),
|
||||
notaries = listOf(NotaryInfo(DUMMY_NOTARY, true)))
|
||||
) {
|
||||
override fun loadContractAttachment(stateRef: StateRef) = servicesForResolution.loadContractAttachment(stateRef)
|
||||
@ -103,13 +118,13 @@ class ConstraintsPropagationTests {
|
||||
fun `Happy path with the HashConstraint`() {
|
||||
ledgerServices.ledger(DUMMY_NOTARY) {
|
||||
ledgerServices.recordTransaction(transaction {
|
||||
attachment(Cash.PROGRAM_ID, SecureHash.allOnesHash)
|
||||
attachment(Cash.PROGRAM_ID, allOnesHash)
|
||||
output(Cash.PROGRAM_ID, "c1", DUMMY_NOTARY, null, HashAttachmentConstraint(allOnesHash), Cash.State(1000.POUNDS `issued by` ALICE_PARTY.ref(1), ALICE_PARTY))
|
||||
command(ALICE_PUBKEY, Cash.Commands.Issue())
|
||||
verifies()
|
||||
})
|
||||
transaction {
|
||||
attachment(Cash.PROGRAM_ID, SecureHash.allOnesHash)
|
||||
attachment(Cash.PROGRAM_ID, allOnesHash)
|
||||
input("c1")
|
||||
output(Cash.PROGRAM_ID, "c2", DUMMY_NOTARY, null, HashAttachmentConstraint(allOnesHash), Cash.State(1000.POUNDS `issued by` ALICE_PARTY.ref(1), BOB_PARTY))
|
||||
command(ALICE_PUBKEY, Cash.Commands.Move())
|
||||
@ -125,13 +140,13 @@ class ConstraintsPropagationTests {
|
||||
val cordappAttachmentIds =
|
||||
cordapps.map { cordapp ->
|
||||
val unsignedAttId =
|
||||
cordapp.jarPath.toPath().inputStream().use { unsignedJarStream ->
|
||||
cordapp.jarPath.openStream().use { unsignedJarStream ->
|
||||
ledgerServices.attachments.importContractAttachment(cordapp.contractClassNames, "rpc", unsignedJarStream, null)
|
||||
}
|
||||
val jarAndSigner = ContractJarTestUtils.signContractJar(cordapp.jarPath, copyFirst = true, keyStoreDir = keyStoreDir.path)
|
||||
val signedJar = jarAndSigner.first
|
||||
val signedAttId =
|
||||
signedJar.inputStream().use { signedJarStream ->
|
||||
signedJar.read { signedJarStream ->
|
||||
ledgerServices.attachments.importContractAttachment(cordapp.contractClassNames, "rpc", signedJarStream, null, listOf(jarAndSigner.second))
|
||||
}
|
||||
Pair(unsignedAttId, signedAttId)
|
||||
@ -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`() {
|
||||
ledgerServices.ledger(DUMMY_NOTARY) {
|
||||
transaction {
|
||||
attachment(Cash.PROGRAM_ID, SecureHash.zeroHash)
|
||||
attachment(Cash.PROGRAM_ID, zeroHash)
|
||||
output(Cash.PROGRAM_ID, "c1", DUMMY_NOTARY, null, HashAttachmentConstraint(zeroHash), Cash.State(1000.POUNDS `issued by` ALICE_PARTY.ref(1), ALICE_PARTY))
|
||||
command(ALICE_PUBKEY, Cash.Commands.Issue())
|
||||
verifies()
|
||||
}
|
||||
assertFailsWith<IllegalArgumentException> {
|
||||
transaction {
|
||||
attachment(Cash.PROGRAM_ID, SecureHash.allOnesHash)
|
||||
attachment(Cash.PROGRAM_ID, allOnesHash)
|
||||
input("c1")
|
||||
output(Cash.PROGRAM_ID, "c2", DUMMY_NOTARY, null, HashAttachmentConstraint(allOnesHash), Cash.State(1000.POUNDS `issued by` ALICE_PARTY.ref(1), BOB_PARTY))
|
||||
command(ALICE_PUBKEY, Cash.Commands.Move())
|
||||
@ -184,27 +199,27 @@ class ConstraintsPropagationTests {
|
||||
fun `Transaction validation fails, when constraints do not propagate correctly`() {
|
||||
ledgerServices.ledger(DUMMY_NOTARY) {
|
||||
ledgerServices.recordTransaction(transaction {
|
||||
attachment(Cash.PROGRAM_ID, SecureHash.zeroHash)
|
||||
attachment(Cash.PROGRAM_ID, zeroHash)
|
||||
output(Cash.PROGRAM_ID, "c1", DUMMY_NOTARY, null, HashAttachmentConstraint(zeroHash), Cash.State(1000.POUNDS `issued by` ALICE_PARTY.ref(1), ALICE_PARTY))
|
||||
command(ALICE_PUBKEY, Cash.Commands.Issue())
|
||||
verifies()
|
||||
})
|
||||
ledgerServices.recordTransaction(transaction {
|
||||
attachment(Cash.PROGRAM_ID, SecureHash.zeroHash)
|
||||
attachment(Cash.PROGRAM_ID, zeroHash)
|
||||
input("c1")
|
||||
output(Cash.PROGRAM_ID, "c2", DUMMY_NOTARY, null, WhitelistedByZoneAttachmentConstraint, Cash.State(1000.POUNDS `issued by` ALICE_PARTY.ref(1), BOB_PARTY))
|
||||
command(ALICE_PUBKEY, Cash.Commands.Move())
|
||||
failsWith("are not propagated correctly")
|
||||
})
|
||||
ledgerServices.recordTransaction(transaction {
|
||||
attachment(Cash.PROGRAM_ID, SecureHash.zeroHash)
|
||||
attachment(Cash.PROGRAM_ID, zeroHash)
|
||||
input("c1")
|
||||
output(Cash.PROGRAM_ID, "c3", DUMMY_NOTARY, null, SignatureAttachmentConstraint(ALICE_PUBKEY), Cash.State(1000.POUNDS `issued by` ALICE_PARTY.ref(1), BOB_PARTY))
|
||||
command(ALICE_PUBKEY, Cash.Commands.Move())
|
||||
fails()
|
||||
})
|
||||
transaction {
|
||||
attachment(Cash.PROGRAM_ID, SecureHash.zeroHash)
|
||||
attachment(Cash.PROGRAM_ID, zeroHash)
|
||||
input("c1")
|
||||
output(Cash.PROGRAM_ID, "c4", DUMMY_NOTARY, null, AlwaysAcceptAttachmentConstraint, Cash.State(1000.POUNDS `issued by` ALICE_PARTY.ref(1), BOB_PARTY))
|
||||
command(ALICE_PUBKEY, Cash.Commands.Move())
|
||||
@ -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`() {
|
||||
ledgerServices.ledger(DUMMY_NOTARY) {
|
||||
ledgerServices.recordTransaction(transaction {
|
||||
attachment(Cash.PROGRAM_ID, SecureHash.zeroHash)
|
||||
attachment(Cash.PROGRAM_ID, zeroHash)
|
||||
output(Cash.PROGRAM_ID, "c1", DUMMY_NOTARY, null, WhitelistedByZoneAttachmentConstraint, Cash.State(1000.POUNDS `issued by` ALICE_PARTY.ref(1), ALICE_PARTY))
|
||||
command(ALICE_PUBKEY, Cash.Commands.Issue())
|
||||
verifies()
|
||||
})
|
||||
transaction {
|
||||
attachment(Cash.PROGRAM_ID, SecureHash.zeroHash)
|
||||
attachment(Cash.PROGRAM_ID, zeroHash)
|
||||
input("c1")
|
||||
output(Cash.PROGRAM_ID, "c2", DUMMY_NOTARY, null, HashAttachmentConstraint(zeroHash), Cash.State(1000.POUNDS `issued by` ALICE_PARTY.ref(1), BOB_PARTY))
|
||||
command(ALICE_PUBKEY, Cash.Commands.Move())
|
||||
@ -237,7 +252,7 @@ class ConstraintsPropagationTests {
|
||||
|
||||
ledgerServices.ledger(DUMMY_NOTARY) {
|
||||
ledgerServices.recordTransaction(transaction {
|
||||
attachment(Cash.PROGRAM_ID, SecureHash.zeroHash)
|
||||
attachment(Cash.PROGRAM_ID, zeroHash)
|
||||
output(Cash.PROGRAM_ID, "w1", DUMMY_NOTARY, null, WhitelistedByZoneAttachmentConstraint, Cash.State(1000.POUNDS `issued by` ALICE_PARTY.ref(1), ALICE_PARTY))
|
||||
command(ALICE_PUBKEY, Cash.Commands.Issue())
|
||||
verifies()
|
||||
@ -245,7 +260,7 @@ class ConstraintsPropagationTests {
|
||||
|
||||
// the attachment is signed
|
||||
transaction {
|
||||
attachment(Cash.PROGRAM_ID, SecureHash.allOnesHash, listOf(hashToSignatureConstraintsKey))
|
||||
attachment(Cash.PROGRAM_ID, allOnesHash, listOf(hashToSignatureConstraintsKey))
|
||||
input("w1")
|
||||
output(Cash.PROGRAM_ID, "w2", DUMMY_NOTARY, null, SignatureAttachmentConstraint(hashToSignatureConstraintsKey), Cash.State(1000.POUNDS `issued by` ALICE_PARTY.ref(1), BOB_PARTY))
|
||||
command(ALICE_PUBKEY, Cash.Commands.Move())
|
||||
@ -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`() {
|
||||
ledgerServices.ledger(DUMMY_NOTARY) {
|
||||
ledgerServices.recordTransaction(transaction {
|
||||
attachment(Cash.PROGRAM_ID, SecureHash.zeroHash)
|
||||
attachment(Cash.PROGRAM_ID, zeroHash)
|
||||
output(Cash.PROGRAM_ID, "w1", DUMMY_NOTARY, null, WhitelistedByZoneAttachmentConstraint, Cash.State(1000.POUNDS `issued by` ALICE_PARTY.ref(1), ALICE_PARTY))
|
||||
command(ALICE_PUBKEY, Cash.Commands.Issue())
|
||||
verifies()
|
||||
})
|
||||
// the attachment is not signed
|
||||
transaction {
|
||||
attachment(Cash.PROGRAM_ID, SecureHash.zeroHash)
|
||||
attachment(Cash.PROGRAM_ID, zeroHash)
|
||||
input("w1")
|
||||
output(Cash.PROGRAM_ID, "w2", DUMMY_NOTARY, null, SignatureAttachmentConstraint(ALICE_PUBKEY), Cash.State(1000.POUNDS `issued by` ALICE_PARTY.ref(1), BOB_PARTY))
|
||||
command(ALICE_PUBKEY, Cash.Commands.Move())
|
||||
@ -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`() {
|
||||
ledgerServices.ledger(DUMMY_NOTARY) {
|
||||
ledgerServices.recordTransaction(transaction {
|
||||
attachment(noPropagationContractClassName, SecureHash.zeroHash)
|
||||
attachment(noPropagationContractClassName, zeroHash)
|
||||
output(noPropagationContractClassName, "c1", DUMMY_NOTARY, null, HashAttachmentConstraint(zeroHash), NoPropagationContractState())
|
||||
command(ALICE_PUBKEY, NoPropagationContract.Create())
|
||||
verifies()
|
||||
})
|
||||
ledgerServices.recordTransaction(transaction {
|
||||
attachment(noPropagationContractClassName, SecureHash.zeroHash)
|
||||
attachment(noPropagationContractClassName, zeroHash)
|
||||
input("c1")
|
||||
output(noPropagationContractClassName, "c2", DUMMY_NOTARY, null, WhitelistedByZoneAttachmentConstraint, NoPropagationContractState())
|
||||
command(ALICE_PUBKEY, NoPropagationContract.Create())
|
||||
@ -293,7 +308,7 @@ class ConstraintsPropagationTests {
|
||||
})
|
||||
assertFailsWith<IllegalArgumentException> {
|
||||
transaction {
|
||||
attachment(noPropagationContractClassName, SecureHash.zeroHash)
|
||||
attachment(noPropagationContractClassName, zeroHash)
|
||||
input("c1")
|
||||
output(noPropagationContractClassName, "c3", DUMMY_NOTARY, null, AutomaticPlaceholderConstraint, NoPropagationContractState())
|
||||
command(ALICE_PUBKEY, NoPropagationContract.Create())
|
||||
@ -387,13 +402,13 @@ class ConstraintsPropagationTests {
|
||||
fun `Input state contract version may be incompatible with lower version`() {
|
||||
ledgerServices.ledger(DUMMY_NOTARY) {
|
||||
ledgerServices.recordTransaction(transaction {
|
||||
attachment(Cash.PROGRAM_ID, SecureHash.allOnesHash, listOf(hashToSignatureConstraintsKey), mapOf(Attributes.Name.IMPLEMENTATION_VERSION.toString() to "2"))
|
||||
attachment(Cash.PROGRAM_ID, allOnesHash, listOf(hashToSignatureConstraintsKey), mapOf(Attributes.Name.IMPLEMENTATION_VERSION.toString() to "2"))
|
||||
output(Cash.PROGRAM_ID, "c1", DUMMY_NOTARY, null, SignatureAttachmentConstraint(hashToSignatureConstraintsKey), Cash.State(1000.POUNDS `issued by` ALICE_PARTY.ref(1), ALICE_PARTY))
|
||||
command(ALICE_PUBKEY, Cash.Commands.Issue())
|
||||
verifies()
|
||||
})
|
||||
transaction {
|
||||
attachment(Cash.PROGRAM_ID, SecureHash.zeroHash, listOf(hashToSignatureConstraintsKey), mapOf(Attributes.Name.IMPLEMENTATION_VERSION.toString() to "1"))
|
||||
attachment(Cash.PROGRAM_ID, zeroHash, listOf(hashToSignatureConstraintsKey), mapOf(Attributes.Name.IMPLEMENTATION_VERSION.toString() to "1"))
|
||||
input("c1")
|
||||
output(Cash.PROGRAM_ID, "c2", DUMMY_NOTARY, null, SignatureAttachmentConstraint(hashToSignatureConstraintsKey), Cash.State(1000.POUNDS `issued by` ALICE_PARTY.ref(1), BOB_PARTY))
|
||||
command(ALICE_PUBKEY, Cash.Commands.Move())
|
||||
@ -406,13 +421,13 @@ class ConstraintsPropagationTests {
|
||||
fun `Input state contract version is compatible with the same version`() {
|
||||
ledgerServices.ledger(DUMMY_NOTARY) {
|
||||
ledgerServices.recordTransaction(transaction {
|
||||
attachment(Cash.PROGRAM_ID, SecureHash.allOnesHash, listOf(hashToSignatureConstraintsKey), mapOf(Attributes.Name.IMPLEMENTATION_VERSION.toString() to "3"))
|
||||
attachment(Cash.PROGRAM_ID, allOnesHash, listOf(hashToSignatureConstraintsKey), mapOf(Attributes.Name.IMPLEMENTATION_VERSION.toString() to "3"))
|
||||
output(Cash.PROGRAM_ID, "c1", DUMMY_NOTARY, null, SignatureAttachmentConstraint(hashToSignatureConstraintsKey), Cash.State(1000.POUNDS `issued by` ALICE_PARTY.ref(1), ALICE_PARTY))
|
||||
command(ALICE_PUBKEY, Cash.Commands.Issue())
|
||||
verifies()
|
||||
})
|
||||
transaction {
|
||||
attachment(Cash.PROGRAM_ID, SecureHash.zeroHash, listOf(hashToSignatureConstraintsKey), mapOf(Attributes.Name.IMPLEMENTATION_VERSION.toString() to "3"))
|
||||
attachment(Cash.PROGRAM_ID, zeroHash, listOf(hashToSignatureConstraintsKey), mapOf(Attributes.Name.IMPLEMENTATION_VERSION.toString() to "3"))
|
||||
input("c1")
|
||||
output(Cash.PROGRAM_ID, "c2", DUMMY_NOTARY, null, SignatureAttachmentConstraint(hashToSignatureConstraintsKey), Cash.State(1000.POUNDS `issued by` ALICE_PARTY.ref(1), BOB_PARTY))
|
||||
command(ALICE_PUBKEY, Cash.Commands.Move())
|
||||
@ -425,13 +440,13 @@ class ConstraintsPropagationTests {
|
||||
fun `Input state contract version is compatible with higher version`() {
|
||||
ledgerServices.ledger(DUMMY_NOTARY) {
|
||||
ledgerServices.recordTransaction(transaction {
|
||||
attachment(Cash.PROGRAM_ID, SecureHash.allOnesHash, listOf(hashToSignatureConstraintsKey), mapOf(Attributes.Name.IMPLEMENTATION_VERSION.toString() to "1"))
|
||||
attachment(Cash.PROGRAM_ID, allOnesHash, listOf(hashToSignatureConstraintsKey), mapOf(Attributes.Name.IMPLEMENTATION_VERSION.toString() to "1"))
|
||||
output(Cash.PROGRAM_ID, "c1", DUMMY_NOTARY, null, SignatureAttachmentConstraint(hashToSignatureConstraintsKey), Cash.State(1000.POUNDS `issued by` ALICE_PARTY.ref(1), ALICE_PARTY))
|
||||
command(ALICE_PUBKEY, Cash.Commands.Issue())
|
||||
verifies()
|
||||
})
|
||||
transaction {
|
||||
attachment(Cash.PROGRAM_ID, SecureHash.zeroHash, listOf(hashToSignatureConstraintsKey), mapOf(Attributes.Name.IMPLEMENTATION_VERSION.toString() to "2"))
|
||||
attachment(Cash.PROGRAM_ID, zeroHash, listOf(hashToSignatureConstraintsKey), mapOf(Attributes.Name.IMPLEMENTATION_VERSION.toString() to "2"))
|
||||
input("c1")
|
||||
output(Cash.PROGRAM_ID, "c2", DUMMY_NOTARY, null, SignatureAttachmentConstraint(hashToSignatureConstraintsKey), Cash.State(1000.POUNDS `issued by` ALICE_PARTY.ref(1), BOB_PARTY))
|
||||
command(ALICE_PUBKEY, Cash.Commands.Move())
|
||||
@ -444,13 +459,13 @@ class ConstraintsPropagationTests {
|
||||
fun `Input states contract version may be lower that current contract version`() {
|
||||
ledgerServices.ledger(DUMMY_NOTARY) {
|
||||
ledgerServices.recordTransaction(transaction {
|
||||
attachment(Cash.PROGRAM_ID, SecureHash.allOnesHash, listOf(hashToSignatureConstraintsKey), mapOf(Attributes.Name.IMPLEMENTATION_VERSION.toString() to "1"))
|
||||
attachment(Cash.PROGRAM_ID, allOnesHash, listOf(hashToSignatureConstraintsKey), mapOf(Attributes.Name.IMPLEMENTATION_VERSION.toString() to "1"))
|
||||
output(Cash.PROGRAM_ID, "c1", DUMMY_NOTARY, null, SignatureAttachmentConstraint(hashToSignatureConstraintsKey), Cash.State(1000.POUNDS `issued by` ALICE_PARTY.ref(1), ALICE_PARTY))
|
||||
command(ALICE_PUBKEY, Cash.Commands.Issue())
|
||||
verifies()
|
||||
})
|
||||
ledgerServices.recordTransaction(transaction {
|
||||
attachment(Cash.PROGRAM_ID, SecureHash.zeroHash, listOf(hashToSignatureConstraintsKey), mapOf(Attributes.Name.IMPLEMENTATION_VERSION.toString() to "2"))
|
||||
attachment(Cash.PROGRAM_ID, zeroHash, listOf(hashToSignatureConstraintsKey), mapOf(Attributes.Name.IMPLEMENTATION_VERSION.toString() to "2"))
|
||||
output(Cash.PROGRAM_ID, "c2", DUMMY_NOTARY, null, SignatureAttachmentConstraint(hashToSignatureConstraintsKey), Cash.State(1000.POUNDS `issued by` ALICE_PARTY.ref(1), ALICE_PARTY))
|
||||
command(ALICE_PUBKEY, Cash.Commands.Issue())
|
||||
verifies()
|
||||
@ -469,13 +484,13 @@ class ConstraintsPropagationTests {
|
||||
fun `Input state with contract version can be downgraded to no version`() {
|
||||
ledgerServices.ledger(DUMMY_NOTARY) {
|
||||
ledgerServices.recordTransaction(transaction {
|
||||
attachment(Cash.PROGRAM_ID, SecureHash.allOnesHash, listOf(hashToSignatureConstraintsKey), mapOf(Attributes.Name.IMPLEMENTATION_VERSION.toString() to "2"))
|
||||
attachment(Cash.PROGRAM_ID, allOnesHash, listOf(hashToSignatureConstraintsKey), mapOf(Attributes.Name.IMPLEMENTATION_VERSION.toString() to "2"))
|
||||
output(Cash.PROGRAM_ID, "c1", DUMMY_NOTARY, null, SignatureAttachmentConstraint(hashToSignatureConstraintsKey), Cash.State(1000.POUNDS `issued by` ALICE_PARTY.ref(1), ALICE_PARTY))
|
||||
command(ALICE_PUBKEY, Cash.Commands.Issue())
|
||||
verifies()
|
||||
})
|
||||
transaction {
|
||||
attachment(Cash.PROGRAM_ID, SecureHash.zeroHash, listOf(hashToSignatureConstraintsKey), emptyMap())
|
||||
attachment(Cash.PROGRAM_ID, zeroHash, listOf(hashToSignatureConstraintsKey), emptyMap())
|
||||
input("c1")
|
||||
output(Cash.PROGRAM_ID, "c2", DUMMY_NOTARY, null, SignatureAttachmentConstraint(hashToSignatureConstraintsKey), Cash.State(1000.POUNDS `issued by` ALICE_PARTY.ref(1), BOB_PARTY))
|
||||
command(ALICE_PUBKEY, Cash.Commands.Move())
|
||||
@ -488,13 +503,13 @@ class ConstraintsPropagationTests {
|
||||
fun `Input state without contract version is compatible with any version`() {
|
||||
ledgerServices.ledger(DUMMY_NOTARY) {
|
||||
ledgerServices.recordTransaction(transaction {
|
||||
attachment(Cash.PROGRAM_ID, SecureHash.allOnesHash, listOf(hashToSignatureConstraintsKey), emptyMap())
|
||||
attachment(Cash.PROGRAM_ID, allOnesHash, listOf(hashToSignatureConstraintsKey), emptyMap())
|
||||
output(Cash.PROGRAM_ID, "c1", DUMMY_NOTARY, null, SignatureAttachmentConstraint(hashToSignatureConstraintsKey), Cash.State(1000.POUNDS `issued by` ALICE_PARTY.ref(1), ALICE_PARTY))
|
||||
command(ALICE_PUBKEY, Cash.Commands.Issue())
|
||||
verifies()
|
||||
})
|
||||
transaction {
|
||||
attachment(Cash.PROGRAM_ID, SecureHash.zeroHash, listOf(hashToSignatureConstraintsKey), mapOf(Attributes.Name.IMPLEMENTATION_VERSION.toString() to "2"))
|
||||
attachment(Cash.PROGRAM_ID, zeroHash, listOf(hashToSignatureConstraintsKey), mapOf(Attributes.Name.IMPLEMENTATION_VERSION.toString() to "2"))
|
||||
input("c1")
|
||||
output(Cash.PROGRAM_ID, "c2", DUMMY_NOTARY, null, SignatureAttachmentConstraint(hashToSignatureConstraintsKey), Cash.State(1000.POUNDS `issued by` ALICE_PARTY.ref(1), BOB_PARTY))
|
||||
command(ALICE_PUBKEY, Cash.Commands.Move())
|
||||
|
@ -1,11 +1,16 @@
|
||||
package net.corda.coretests.contracts
|
||||
|
||||
import net.corda.core.contracts.*
|
||||
import net.corda.core.contracts.CommandData
|
||||
import net.corda.core.contracts.CommandWithParties
|
||||
import net.corda.core.contracts.TypeOnlyCommandData
|
||||
import net.corda.core.contracts.requireSingleCommand
|
||||
import net.corda.core.contracts.select
|
||||
import net.corda.core.identity.AbstractParty
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.testing.core.TestIdentity
|
||||
import org.assertj.core.api.Assertions
|
||||
import org.assertj.core.api.Assertions.assertThatIllegalArgumentException
|
||||
import org.assertj.core.api.Assertions.assertThatThrownBy
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.junit.runners.Parameterized
|
||||
@ -35,8 +40,8 @@ class RequireSingleCommandTests(private val testFunction: (Collection<CommandWit
|
||||
@JvmStatic
|
||||
@Parameterized.Parameters(name = "{1}")
|
||||
fun data(): Collection<Array<Any>> = listOf(
|
||||
arrayOf<Any>({ commands: Collection<CommandWithParties<CommandData>> -> commands.requireSingleCommand<TestCommands>() }, "Inline version"),
|
||||
arrayOf<Any>({ commands: Collection<CommandWithParties<CommandData>> -> commands.requireSingleCommand(TestCommands::class.java) }, "Interop version")
|
||||
arrayOf({ commands: Collection<CommandWithParties<CommandData>> -> commands.requireSingleCommand<TestCommands>() }, "Inline version"),
|
||||
arrayOf({ commands: Collection<CommandWithParties<CommandData>> -> commands.requireSingleCommand(TestCommands::class.java) }, "Interop version")
|
||||
)
|
||||
}
|
||||
|
||||
@ -47,16 +52,18 @@ class RequireSingleCommandTests(private val testFunction: (Collection<CommandWit
|
||||
assertEquals(returnedCommand, validCommandOne, "they should be the same")
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException::class, timeout=300_000)
|
||||
@Test(timeout=300_000)
|
||||
fun `check error is thrown if more than one valid command`() {
|
||||
val commands = listOf(validCommandOne, validCommandTwo)
|
||||
assertThatIllegalArgumentException().isThrownBy {
|
||||
testFunction(commands)
|
||||
}
|
||||
}
|
||||
|
||||
@Test(timeout=300_000)
|
||||
fun `check error is thrown when command is of wrong type`() {
|
||||
val commands = listOf(invalidCommand)
|
||||
Assertions.assertThatThrownBy { testFunction(commands) }
|
||||
assertThatThrownBy { testFunction(commands) }
|
||||
.isInstanceOf(IllegalStateException::class.java)
|
||||
.hasMessage("Required net.corda.coretests.contracts.TestCommands command")
|
||||
}
|
||||
@ -69,8 +76,8 @@ class SelectWithSingleInputsTests(private val testFunction: (Collection<CommandW
|
||||
@JvmStatic
|
||||
@Parameterized.Parameters(name = "{1}")
|
||||
fun data(): Collection<Array<Any>> = listOf(
|
||||
arrayOf<Any>({ commands: Collection<CommandWithParties<CommandData>>, signer: PublicKey?, party: AbstractParty? -> commands.select<TestCommands>(signer, party) }, "Inline version"),
|
||||
arrayOf<Any>({ commands: Collection<CommandWithParties<CommandData>>, signer: PublicKey?, party: AbstractParty? -> commands.select(TestCommands::class.java, signer, party) }, "Interop version")
|
||||
arrayOf({ commands: Collection<CommandWithParties<CommandData>>, signer: PublicKey?, party: AbstractParty? -> commands.select<TestCommands>(signer, party) }, "Inline version"),
|
||||
arrayOf({ commands: Collection<CommandWithParties<CommandData>>, signer: PublicKey?, party: AbstractParty? -> commands.select(TestCommands::class.java, signer, party) }, "Interop version")
|
||||
)
|
||||
}
|
||||
|
||||
@ -118,8 +125,8 @@ class SelectWithMultipleInputsTests(private val testFunction: (Collection<Comman
|
||||
@JvmStatic
|
||||
@Parameterized.Parameters(name = "{1}")
|
||||
fun data(): Collection<Array<Any>> = listOf(
|
||||
arrayOf<Any>({ commands: Collection<CommandWithParties<CommandData>>, signers: Collection<PublicKey>?, party: Collection<Party>? -> commands.select<TestCommands>(signers, party) }, "Inline version"),
|
||||
arrayOf<Any>({ commands: Collection<CommandWithParties<CommandData>>, signers: Collection<PublicKey>?, party: Collection<Party>? -> commands.select(TestCommands::class.java, signers, party) }, "Interop version")
|
||||
arrayOf({ commands: Collection<CommandWithParties<CommandData>>, signers: Collection<PublicKey>?, party: Collection<Party>? -> commands.select<TestCommands>(signers, party) }, "Inline version"),
|
||||
arrayOf({ commands: Collection<CommandWithParties<CommandData>>, signers: Collection<PublicKey>?, party: Collection<Party>? -> commands.select(TestCommands::class.java, signers, party) }, "Interop version")
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -1,8 +1,8 @@
|
||||
package net.corda.coretests.contracts
|
||||
|
||||
import com.nhaarman.mockito_kotlin.doReturn
|
||||
import com.nhaarman.mockito_kotlin.mock
|
||||
import com.nhaarman.mockito_kotlin.whenever
|
||||
import org.mockito.kotlin.doReturn
|
||||
import org.mockito.kotlin.mock
|
||||
import org.mockito.kotlin.whenever
|
||||
import net.corda.core.contracts.*
|
||||
import net.corda.core.crypto.Crypto
|
||||
import net.corda.core.crypto.SecureHash
|
||||
|
@ -3,7 +3,6 @@ package net.corda.coretests.crypto
|
||||
import net.corda.core.crypto.*
|
||||
import net.corda.core.crypto.CompositeKey.NodeAndWeight
|
||||
import net.corda.core.internal.declaredField
|
||||
import net.corda.core.internal.div
|
||||
import net.corda.core.serialization.serialize
|
||||
import net.corda.core.utilities.OpaqueBytes
|
||||
import net.corda.core.utilities.toBase58String
|
||||
@ -19,6 +18,7 @@ import org.junit.Test
|
||||
import org.junit.rules.TemporaryFolder
|
||||
import java.security.PublicKey
|
||||
import javax.security.auth.x500.X500Principal
|
||||
import kotlin.io.path.div
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertFailsWith
|
||||
import kotlin.test.assertFalse
|
||||
|
@ -1,11 +1,19 @@
|
||||
package net.corda.coretests.crypto
|
||||
|
||||
import com.nhaarman.mockito_kotlin.doReturn
|
||||
import com.nhaarman.mockito_kotlin.mock
|
||||
import com.nhaarman.mockito_kotlin.whenever
|
||||
import net.corda.core.contracts.*
|
||||
import net.corda.core.crypto.*
|
||||
import net.corda.core.contracts.Command
|
||||
import net.corda.core.contracts.PrivacySalt
|
||||
import net.corda.core.contracts.StateRef
|
||||
import net.corda.core.contracts.TimeWindow
|
||||
import net.corda.core.contracts.TransactionState
|
||||
import net.corda.core.crypto.DigestService
|
||||
import net.corda.core.crypto.MerkleTree
|
||||
import net.corda.core.crypto.MerkleTreeException
|
||||
import net.corda.core.crypto.PartialMerkleTree
|
||||
import net.corda.core.crypto.SecureHash
|
||||
import net.corda.core.crypto.internal.DigestAlgorithmFactory
|
||||
import net.corda.core.crypto.keys
|
||||
import net.corda.core.crypto.randomHash
|
||||
import net.corda.core.crypto.sha256
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.core.internal.BLAKE2s256DigestAlgorithm
|
||||
@ -16,9 +24,10 @@ import net.corda.core.serialization.deserialize
|
||||
import net.corda.core.serialization.serialize
|
||||
import net.corda.core.transactions.ReferenceStateRef
|
||||
import net.corda.core.transactions.WireTransaction
|
||||
import net.corda.coretesting.internal.TEST_TX_TIME
|
||||
import net.corda.finance.DOLLARS
|
||||
import net.corda.finance.`issued by`
|
||||
import net.corda.finance.contracts.asset.Cash
|
||||
import net.corda.finance.`issued by`
|
||||
import net.corda.testing.common.internal.testNetworkParameters
|
||||
import net.corda.testing.core.DUMMY_NOTARY_NAME
|
||||
import net.corda.testing.core.SerializationEnvironmentRule
|
||||
@ -26,20 +35,29 @@ import net.corda.testing.core.TestIdentity
|
||||
import net.corda.testing.dsl.LedgerDSL
|
||||
import net.corda.testing.dsl.TestLedgerDSLInterpreter
|
||||
import net.corda.testing.dsl.TestTransactionDSLInterpreter
|
||||
import net.corda.coretesting.internal.TEST_TX_TIME
|
||||
import net.corda.testing.internal.createWireTransaction
|
||||
import net.corda.testing.node.MockServices
|
||||
import net.corda.testing.node.ledger
|
||||
import org.assertj.core.api.Assertions.assertThatIllegalArgumentException
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.junit.runners.Parameterized
|
||||
import org.mockito.kotlin.doReturn
|
||||
import org.mockito.kotlin.mock
|
||||
import org.mockito.kotlin.whenever
|
||||
import java.security.PublicKey
|
||||
import java.util.function.Predicate
|
||||
import java.util.stream.IntStream
|
||||
import kotlin.streams.toList
|
||||
import kotlin.test.*
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertFailsWith
|
||||
import kotlin.test.assertFalse
|
||||
import kotlin.test.assertNotEquals
|
||||
import kotlin.test.assertNotNull
|
||||
import kotlin.test.assertNull
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
@RunWith(Parameterized::class)
|
||||
class PartialMerkleTreeTest(private var digestService: DigestService) {
|
||||
@ -204,7 +222,7 @@ class PartialMerkleTreeTest(private var digestService: DigestService) {
|
||||
|
||||
@Test(timeout=300_000)
|
||||
fun `nothing filtered`() {
|
||||
val ftxNothing = testTx.buildFilteredTransaction(Predicate { false })
|
||||
val ftxNothing = testTx.buildFilteredTransaction { false }
|
||||
assertTrue(ftxNothing.componentGroups.isEmpty())
|
||||
assertTrue(ftxNothing.attachments.isEmpty())
|
||||
assertTrue(ftxNothing.commands.isEmpty())
|
||||
@ -291,11 +309,13 @@ class PartialMerkleTreeTest(private var digestService: DigestService) {
|
||||
assertFalse(pmt.verify(wrongRoot, inclHashes))
|
||||
}
|
||||
|
||||
@Test(expected = Exception::class, timeout=300_000)
|
||||
@Test(timeout=300_000)
|
||||
fun `hash map serialization not allowed`() {
|
||||
val hm1 = hashMapOf("a" to 1, "b" to 2, "c" to 3, "e" to 4)
|
||||
assertThatIllegalArgumentException().isThrownBy {
|
||||
hm1.serialize()
|
||||
}
|
||||
}
|
||||
|
||||
private fun makeSimpleCashWtx(
|
||||
notary: Party,
|
||||
@ -322,11 +342,11 @@ class PartialMerkleTreeTest(private var digestService: DigestService) {
|
||||
val merkleTree = MerkleTree.getMerkleTree(sampleLeaves, digestService)
|
||||
|
||||
// Provided hashes are not in the tree.
|
||||
assertFailsWith<MerkleTreeException> { PartialMerkleTree.build(merkleTree, listOf<SecureHash>(digestService.hash("20"))) }
|
||||
assertFailsWith<MerkleTreeException> { PartialMerkleTree.build(merkleTree, listOf(digestService.hash("20"))) }
|
||||
// One of the provided hashes is not in the tree.
|
||||
assertFailsWith<MerkleTreeException> { PartialMerkleTree.build(merkleTree, listOf<SecureHash>(digestService.hash("20"), digestService.hash("1"), digestService.hash("5"))) }
|
||||
assertFailsWith<MerkleTreeException> { PartialMerkleTree.build(merkleTree, listOf(digestService.hash("20"), digestService.hash("1"), digestService.hash("5"))) }
|
||||
|
||||
val pmt = PartialMerkleTree.build(merkleTree, listOf<SecureHash>(digestService.hash("1"), digestService.hash("5"), digestService.hash("0"), digestService.hash("19")))
|
||||
val pmt = PartialMerkleTree.build(merkleTree, listOf(digestService.hash("1"), digestService.hash("5"), digestService.hash("0"), digestService.hash("19")))
|
||||
// First leaf.
|
||||
assertEquals(0, pmt.leafIndex(digestService.hash("0")))
|
||||
// Second leaf.
|
||||
@ -340,17 +360,17 @@ class PartialMerkleTreeTest(private var digestService: DigestService) {
|
||||
// The provided hash is not in the tree (using a leaf that didn't exist in the original Merkle tree).
|
||||
assertFailsWith<MerkleTreeException> { pmt.leafIndex(digestService.hash("30")) }
|
||||
|
||||
val pmtFirstElementOnly = PartialMerkleTree.build(merkleTree, listOf<SecureHash>(digestService.hash("0")))
|
||||
val pmtFirstElementOnly = PartialMerkleTree.build(merkleTree, listOf(digestService.hash("0")))
|
||||
assertEquals(0, pmtFirstElementOnly.leafIndex(digestService.hash("0")))
|
||||
// The provided hash is not in the tree.
|
||||
assertFailsWith<MerkleTreeException> { pmtFirstElementOnly.leafIndex(digestService.hash("10")) }
|
||||
|
||||
val pmtLastElementOnly = PartialMerkleTree.build(merkleTree, listOf<SecureHash>(digestService.hash("19")))
|
||||
val pmtLastElementOnly = PartialMerkleTree.build(merkleTree, listOf(digestService.hash("19")))
|
||||
assertEquals(19, pmtLastElementOnly.leafIndex(digestService.hash("19")))
|
||||
// The provided hash is not in the tree.
|
||||
assertFailsWith<MerkleTreeException> { pmtLastElementOnly.leafIndex(digestService.hash("10")) }
|
||||
|
||||
val pmtOneElement = PartialMerkleTree.build(merkleTree, listOf<SecureHash>(digestService.hash("5")))
|
||||
val pmtOneElement = PartialMerkleTree.build(merkleTree, listOf(digestService.hash("5")))
|
||||
assertEquals(5, pmtOneElement.leafIndex(digestService.hash("5")))
|
||||
// The provided hash is not in the tree.
|
||||
assertFailsWith<MerkleTreeException> { pmtOneElement.leafIndex(digestService.hash("10")) }
|
||||
|
@ -1,17 +1,14 @@
|
||||
package net.corda.coretests.crypto
|
||||
|
||||
import com.nhaarman.mockito_kotlin.doReturn
|
||||
import com.nhaarman.mockito_kotlin.mock
|
||||
import com.nhaarman.mockito_kotlin.whenever
|
||||
import net.corda.core.contracts.Command
|
||||
import net.corda.core.contracts.PrivacySalt
|
||||
import net.corda.core.contracts.StateRef
|
||||
import net.corda.core.contracts.TimeWindow
|
||||
import net.corda.core.contracts.TransactionState
|
||||
import net.corda.core.crypto.DigestService
|
||||
import net.corda.core.crypto.MerkleTree
|
||||
import net.corda.core.crypto.MerkleTreeException
|
||||
import net.corda.core.crypto.PartialMerkleTree
|
||||
import net.corda.core.crypto.DigestService
|
||||
import net.corda.core.crypto.SecureHash
|
||||
import net.corda.core.crypto.SecureHash.Companion.SHA2_384
|
||||
import net.corda.core.crypto.SecureHash.Companion.hashAs
|
||||
@ -26,9 +23,10 @@ import net.corda.core.serialization.serialize
|
||||
import net.corda.core.transactions.ReferenceStateRef
|
||||
import net.corda.core.transactions.WireTransaction
|
||||
import net.corda.core.utilities.OpaqueBytes
|
||||
import net.corda.coretesting.internal.TEST_TX_TIME
|
||||
import net.corda.finance.DOLLARS
|
||||
import net.corda.finance.`issued by`
|
||||
import net.corda.finance.contracts.asset.Cash
|
||||
import net.corda.finance.`issued by`
|
||||
import net.corda.testing.common.internal.testNetworkParameters
|
||||
import net.corda.testing.core.DUMMY_NOTARY_NAME
|
||||
import net.corda.testing.core.SerializationEnvironmentRule
|
||||
@ -36,10 +34,10 @@ import net.corda.testing.core.TestIdentity
|
||||
import net.corda.testing.dsl.LedgerDSL
|
||||
import net.corda.testing.dsl.TestLedgerDSLInterpreter
|
||||
import net.corda.testing.dsl.TestTransactionDSLInterpreter
|
||||
import net.corda.coretesting.internal.TEST_TX_TIME
|
||||
import net.corda.testing.internal.createWireTransaction
|
||||
import net.corda.testing.node.MockServices
|
||||
import net.corda.testing.node.ledger
|
||||
import org.assertj.core.api.Assertions.assertThatIllegalArgumentException
|
||||
import org.junit.Assert.assertFalse
|
||||
import org.junit.Assert.assertNotEquals
|
||||
import org.junit.Assert.assertNotNull
|
||||
@ -49,6 +47,9 @@ import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.mockito.kotlin.doReturn
|
||||
import org.mockito.kotlin.mock
|
||||
import org.mockito.kotlin.whenever
|
||||
import java.security.PublicKey
|
||||
import java.util.function.Predicate
|
||||
import java.util.stream.IntStream
|
||||
@ -209,7 +210,7 @@ class PartialMerkleTreeWithNamedHashMultiAlgTreeTest {
|
||||
|
||||
@Test(timeout=300_000)
|
||||
fun `nothing filtered`() {
|
||||
val ftxNothing = testTx.buildFilteredTransaction(Predicate { false })
|
||||
val ftxNothing = testTx.buildFilteredTransaction { false }
|
||||
assertTrue(ftxNothing.componentGroups.isEmpty())
|
||||
assertTrue(ftxNothing.attachments.isEmpty())
|
||||
assertTrue(ftxNothing.commands.isEmpty())
|
||||
@ -296,11 +297,13 @@ class PartialMerkleTreeWithNamedHashMultiAlgTreeTest {
|
||||
assertFalse(pmt.verify(wrongRoot, inclHashes))
|
||||
}
|
||||
|
||||
@Test(expected = Exception::class, timeout=300_000)
|
||||
@Test(timeout=300_000)
|
||||
fun `hash map serialization not allowed`() {
|
||||
val hm1 = hashMapOf("a" to 1, "b" to 2, "c" to 3, "e" to 4)
|
||||
assertThatIllegalArgumentException().isThrownBy {
|
||||
hm1.serialize()
|
||||
}
|
||||
}
|
||||
|
||||
private fun makeSimpleCashWtx(
|
||||
notary: Party,
|
||||
|
@ -1,17 +1,14 @@
|
||||
package net.corda.coretests.crypto
|
||||
|
||||
import com.nhaarman.mockito_kotlin.doReturn
|
||||
import com.nhaarman.mockito_kotlin.mock
|
||||
import com.nhaarman.mockito_kotlin.whenever
|
||||
import net.corda.core.contracts.Command
|
||||
import net.corda.core.contracts.PrivacySalt
|
||||
import net.corda.core.contracts.StateRef
|
||||
import net.corda.core.contracts.TimeWindow
|
||||
import net.corda.core.contracts.TransactionState
|
||||
import net.corda.core.crypto.DigestService
|
||||
import net.corda.core.crypto.MerkleTree
|
||||
import net.corda.core.crypto.MerkleTreeException
|
||||
import net.corda.core.crypto.PartialMerkleTree
|
||||
import net.corda.core.crypto.DigestService
|
||||
import net.corda.core.crypto.SecureHash
|
||||
import net.corda.core.crypto.SecureHash.Companion.SHA2_384
|
||||
import net.corda.core.crypto.SecureHash.Companion.hashAs
|
||||
@ -26,9 +23,10 @@ import net.corda.core.serialization.serialize
|
||||
import net.corda.core.transactions.ReferenceStateRef
|
||||
import net.corda.core.transactions.WireTransaction
|
||||
import net.corda.core.utilities.OpaqueBytes
|
||||
import net.corda.coretesting.internal.TEST_TX_TIME
|
||||
import net.corda.finance.DOLLARS
|
||||
import net.corda.finance.`issued by`
|
||||
import net.corda.finance.contracts.asset.Cash
|
||||
import net.corda.finance.`issued by`
|
||||
import net.corda.testing.common.internal.testNetworkParameters
|
||||
import net.corda.testing.core.DUMMY_NOTARY_NAME
|
||||
import net.corda.testing.core.SerializationEnvironmentRule
|
||||
@ -36,10 +34,10 @@ import net.corda.testing.core.TestIdentity
|
||||
import net.corda.testing.dsl.LedgerDSL
|
||||
import net.corda.testing.dsl.TestLedgerDSLInterpreter
|
||||
import net.corda.testing.dsl.TestTransactionDSLInterpreter
|
||||
import net.corda.coretesting.internal.TEST_TX_TIME
|
||||
import net.corda.testing.internal.createWireTransaction
|
||||
import net.corda.testing.node.MockServices
|
||||
import net.corda.testing.node.ledger
|
||||
import org.assertj.core.api.Assertions.assertThatIllegalArgumentException
|
||||
import org.junit.Assert.assertFalse
|
||||
import org.junit.Assert.assertNotEquals
|
||||
import org.junit.Assert.assertNotNull
|
||||
@ -49,6 +47,9 @@ import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.mockito.kotlin.doReturn
|
||||
import org.mockito.kotlin.mock
|
||||
import org.mockito.kotlin.whenever
|
||||
import java.security.PublicKey
|
||||
import java.util.function.Predicate
|
||||
import java.util.stream.IntStream
|
||||
@ -209,7 +210,7 @@ class PartialMerkleTreeWithNamedHashTest {
|
||||
|
||||
@Test(timeout=300_000)
|
||||
fun `nothing filtered`() {
|
||||
val ftxNothing = testTx.buildFilteredTransaction(Predicate { false })
|
||||
val ftxNothing = testTx.buildFilteredTransaction { false }
|
||||
assertTrue(ftxNothing.componentGroups.isEmpty())
|
||||
assertTrue(ftxNothing.attachments.isEmpty())
|
||||
assertTrue(ftxNothing.commands.isEmpty())
|
||||
@ -296,11 +297,13 @@ class PartialMerkleTreeWithNamedHashTest {
|
||||
assertFalse(pmt.verify(wrongRoot, inclHashes))
|
||||
}
|
||||
|
||||
@Test(expected = Exception::class, timeout=300_000)
|
||||
@Test(timeout=300_000)
|
||||
fun `hash map serialization not allowed`() {
|
||||
val hm1 = hashMapOf("a" to 1, "b" to 2, "c" to 3, "e" to 4)
|
||||
assertThatIllegalArgumentException().isThrownBy {
|
||||
hm1.serialize()
|
||||
}
|
||||
}
|
||||
|
||||
private fun makeSimpleCashWtx(
|
||||
notary: Party,
|
||||
|
@ -6,6 +6,7 @@ import net.corda.core.crypto.sign
|
||||
import net.corda.core.serialization.SerializedBytes
|
||||
import net.corda.core.serialization.serialize
|
||||
import net.corda.testing.core.SerializationEnvironmentRule
|
||||
import org.assertj.core.api.Assertions.assertThatExceptionOfType
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
@ -35,12 +36,14 @@ class SignedDataTest {
|
||||
assertEquals(data, unwrappedData)
|
||||
}
|
||||
|
||||
@Test(expected = SignatureException::class, timeout=300_000)
|
||||
@Test(timeout=300_000)
|
||||
fun `make sure incorrectly signed data raises an exception`() {
|
||||
val keyPairA = generateKeyPair()
|
||||
val keyPairB = generateKeyPair()
|
||||
val sig = keyPairA.private.sign(serialized.bytes, keyPairB.public)
|
||||
val wrappedData = SignedData(serialized, sig)
|
||||
assertThatExceptionOfType(SignatureException::class.java).isThrownBy {
|
||||
wrappedData.verified()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,17 @@
|
||||
package net.corda.coretests.crypto
|
||||
|
||||
import net.corda.core.crypto.*
|
||||
import net.corda.core.crypto.Crypto
|
||||
import net.corda.core.crypto.MerkleTree
|
||||
import net.corda.core.crypto.MerkleTreeException
|
||||
import net.corda.core.crypto.PartialMerkleTree
|
||||
import net.corda.core.crypto.SecureHash
|
||||
import net.corda.core.crypto.SignableData
|
||||
import net.corda.core.crypto.SignatureMetadata
|
||||
import net.corda.core.crypto.TransactionSignature
|
||||
import net.corda.core.crypto.sha256
|
||||
import net.corda.core.crypto.sign
|
||||
import net.corda.testing.core.SerializationEnvironmentRule
|
||||
import org.assertj.core.api.Assertions.assertThatExceptionOfType
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import java.math.BigInteger
|
||||
@ -39,13 +49,15 @@ class TransactionSignatureTest {
|
||||
}
|
||||
|
||||
/** Verification should fail; corrupted metadata - clearData (Merkle root) has changed. */
|
||||
@Test(expected = SignatureException::class,timeout=300_000)
|
||||
@Test(timeout=300_000)
|
||||
fun `Signature metadata full failure clearData has changed`() {
|
||||
val keyPair = Crypto.generateKeyPair("ECDSA_SECP256K1_SHA256")
|
||||
val signableData = SignableData(testBytes.sha256(), SignatureMetadata(1, Crypto.findSignatureScheme(keyPair.public).schemeNumberID))
|
||||
val transactionSignature = keyPair.sign(signableData)
|
||||
assertThatExceptionOfType(SignatureException::class.java).isThrownBy {
|
||||
Crypto.doVerify((testBytes + testBytes).sha256(), transactionSignature)
|
||||
}
|
||||
}
|
||||
|
||||
@Test(timeout=300_000)
|
||||
fun `Verify multi-tx signature`() {
|
||||
|
@ -1,6 +1,7 @@
|
||||
package net.corda.coretests.flows
|
||||
|
||||
import co.paralleluniverse.fibers.Suspendable
|
||||
import co.paralleluniverse.strands.Strand
|
||||
import net.corda.core.CordaException
|
||||
import net.corda.core.flows.FlowExternalAsyncOperation
|
||||
import net.corda.core.flows.FlowExternalOperation
|
||||
@ -133,7 +134,7 @@ abstract class AbstractFlowExternalOperationTest {
|
||||
fun createFuture(): CompletableFuture<Any> {
|
||||
return CompletableFuture.supplyAsync(Supplier<Any> {
|
||||
log.info("Starting sleep inside of future")
|
||||
Thread.sleep(1000)
|
||||
Strand.sleep(1000)
|
||||
log.info("Finished sleep inside of future")
|
||||
"Here is your return value"
|
||||
}, executorService)
|
||||
|
@ -23,22 +23,22 @@ import net.corda.core.identity.groupAbstractPartyByWellKnownParty
|
||||
import net.corda.core.transactions.SignedTransaction
|
||||
import net.corda.core.transactions.TransactionBuilder
|
||||
import net.corda.core.utilities.getOrThrow
|
||||
import net.corda.coretesting.internal.matchers.flow.willReturn
|
||||
import net.corda.coretesting.internal.matchers.flow.willThrow
|
||||
import net.corda.testing.contracts.DummyContract
|
||||
import net.corda.testing.core.ALICE_NAME
|
||||
import net.corda.testing.core.BOB_NAME
|
||||
import net.corda.testing.core.CHARLIE_NAME
|
||||
import net.corda.testing.core.TestIdentity
|
||||
import net.corda.testing.core.singleIdentity
|
||||
import net.corda.coretesting.internal.matchers.flow.willReturn
|
||||
import net.corda.coretesting.internal.matchers.flow.willThrow
|
||||
import net.corda.testing.node.MockServices
|
||||
import net.corda.testing.node.internal.DUMMY_CONTRACTS_CORDAPP
|
||||
import net.corda.testing.node.internal.InternalMockNetwork
|
||||
import net.corda.testing.node.internal.TestStartedNode
|
||||
import net.corda.testing.node.internal.enclosedCordapp
|
||||
import org.hamcrest.CoreMatchers.`is`
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.assertj.core.api.Assertions.assertThatIllegalArgumentException
|
||||
import org.junit.AfterClass
|
||||
import org.junit.Assert
|
||||
import org.junit.Test
|
||||
import java.security.PublicKey
|
||||
|
||||
@ -92,7 +92,7 @@ class CollectSignaturesFlowTests : WithContracts {
|
||||
mockNet.runNetwork()
|
||||
val stx = future.get()
|
||||
val missingSigners = stx.getMissingSigners()
|
||||
Assert.assertThat(missingSigners, `is`(emptySet()))
|
||||
assertThat(missingSigners).isEmpty()
|
||||
}
|
||||
|
||||
@Test(timeout=300_000)
|
||||
@ -122,10 +122,10 @@ class CollectSignaturesFlowTests : WithContracts {
|
||||
mockNet.runNetwork()
|
||||
val stx = future.get()
|
||||
val missingSigners = stx.getMissingSigners()
|
||||
Assert.assertThat(missingSigners, `is`(emptySet()))
|
||||
assertThat(missingSigners).isEmpty()
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException::class, timeout=300_000)
|
||||
@Test(timeout=300_000)
|
||||
fun `throws exception when extra sessions are initiated`() {
|
||||
bobNode.registerInitiatedFlow(ExtraSessionsFlowResponder::class.java)
|
||||
charlieNode.registerInitiatedFlow(ExtraSessionsFlowResponder::class.java)
|
||||
@ -137,8 +137,10 @@ class CollectSignaturesFlowTests : WithContracts {
|
||||
listOf(bobNode.info.singleIdentity(), alice)))
|
||||
.resultFuture
|
||||
mockNet.runNetwork()
|
||||
assertThatIllegalArgumentException().isThrownBy {
|
||||
future.getOrThrow()
|
||||
}
|
||||
}
|
||||
|
||||
@Test(timeout=300_000)
|
||||
fun `it is possible to collect from multiple well known sessions`() {
|
||||
@ -152,7 +154,7 @@ class CollectSignaturesFlowTests : WithContracts {
|
||||
listOf(bobNode.info.singleIdentity(), alice))).resultFuture
|
||||
mockNet.runNetwork()
|
||||
val signedTx = future.getOrThrow()
|
||||
Assert.assertThat(signedTx.getMissingSigners(), `is`(emptySet()))
|
||||
assertThat(signedTx.getMissingSigners()).isEmpty()
|
||||
}
|
||||
|
||||
@Test(timeout=300_000)
|
||||
@ -216,7 +218,7 @@ class CollectSignaturesFlowTests : WithContracts {
|
||||
}
|
||||
}
|
||||
|
||||
@InitiatedBy(TestFlow.Initiator::class)
|
||||
@InitiatedBy(Initiator::class)
|
||||
class Responder(private val otherSideSession: FlowSession) : FlowLogic<Unit>() {
|
||||
@Suspendable
|
||||
override fun call() {
|
||||
@ -251,7 +253,7 @@ class AnonymousSessionTestFlow(private val cis: List<PartyAndCertificate>) : Flo
|
||||
}
|
||||
}
|
||||
val state = DummyContract.MultiOwnerState(owners = cis.map { AnonymousParty(it.owningKey) })
|
||||
val create = net.corda.testing.contracts.DummyContract.Commands.Create()
|
||||
val create = DummyContract.Commands.Create()
|
||||
val txBuilder = TransactionBuilder(notary = serviceHub.networkMapCache.notaryIdentities.first())
|
||||
.addOutputState(state)
|
||||
.addCommand(create, cis.map { it.owningKey })
|
||||
@ -289,7 +291,7 @@ class MixAndMatchAnonymousSessionTestFlow(private val cis: List<PartyAndCertific
|
||||
}
|
||||
}
|
||||
val state = DummyContract.MultiOwnerState(owners = cis.map { AnonymousParty(it.owningKey) })
|
||||
val create = net.corda.testing.contracts.DummyContract.Commands.Create()
|
||||
val create = DummyContract.Commands.Create()
|
||||
val txBuilder = TransactionBuilder(notary = serviceHub.networkMapCache.notaryIdentities.first())
|
||||
.addOutputState(state)
|
||||
.addCommand(create, cis.map { it.owningKey })
|
||||
@ -324,7 +326,7 @@ class ExtraSessionsFlow(private val openFor: List<Party>, private val involve: L
|
||||
|
||||
val sessions = openFor.map { initiateFlow(it) }
|
||||
val state = DummyContract.MultiOwnerState(owners = involve.map { AnonymousParty(it.owningKey) })
|
||||
val create = net.corda.testing.contracts.DummyContract.Commands.Create()
|
||||
val create = DummyContract.Commands.Create()
|
||||
val txBuilder = TransactionBuilder(notary = serviceHub.networkMapCache.notaryIdentities.first())
|
||||
.addOutputState(state)
|
||||
.addCommand(create, involve.map { it.owningKey })
|
||||
|
@ -9,6 +9,7 @@ import net.corda.core.CordaRuntimeException
|
||||
import net.corda.core.contracts.ContractState
|
||||
import net.corda.core.contracts.StateAndRef
|
||||
import net.corda.core.flows.ContractUpgradeFlow
|
||||
import net.corda.core.internal.getRequiredTransaction
|
||||
import net.corda.core.messaging.CordaRPCOps
|
||||
import net.corda.core.transactions.ContractUpgradeLedgerTransaction
|
||||
import net.corda.core.transactions.SignedTransaction
|
||||
@ -120,8 +121,7 @@ class ContractUpgradeFlowRPCTest : WithContracts, WithFinality {
|
||||
isUpgrade<FROM, TO>())
|
||||
|
||||
private fun TestStartedNode.getContractUpgradeTransaction(state: StateAndRef<ContractState>) =
|
||||
services.validatedTransactions.getTransaction(state.ref.txhash)!!
|
||||
.resolveContractUpgradeTransaction(services)
|
||||
services.getRequiredTransaction(state.ref.txhash).resolveContractUpgradeTransaction(services)
|
||||
|
||||
private inline fun <reified FROM : Any, reified TO : Any> isUpgrade() =
|
||||
isUpgradeFrom<FROM>() and isUpgradeTo<TO>()
|
||||
|
@ -1,31 +1,54 @@
|
||||
package net.corda.coretests.flows
|
||||
|
||||
import com.natpryce.hamkrest.*
|
||||
import com.natpryce.hamkrest.Matcher
|
||||
import com.natpryce.hamkrest.and
|
||||
import com.natpryce.hamkrest.anything
|
||||
import com.natpryce.hamkrest.assertion.assertThat
|
||||
import net.corda.core.contracts.*
|
||||
import com.natpryce.hamkrest.equalTo
|
||||
import com.natpryce.hamkrest.has
|
||||
import com.natpryce.hamkrest.isA
|
||||
import net.corda.core.contracts.AlwaysAcceptAttachmentConstraint
|
||||
import net.corda.core.contracts.Amount
|
||||
import net.corda.core.contracts.AttachmentConstraint
|
||||
import net.corda.core.contracts.BelongsToContract
|
||||
import net.corda.core.contracts.CommandAndState
|
||||
import net.corda.core.contracts.ContractState
|
||||
import net.corda.core.contracts.FungibleAsset
|
||||
import net.corda.core.contracts.Issued
|
||||
import net.corda.core.contracts.StateAndRef
|
||||
import net.corda.core.contracts.TypeOnlyCommandData
|
||||
import net.corda.core.contracts.UpgradedContractWithLegacyConstraint
|
||||
import net.corda.core.flows.UnexpectedFlowEndException
|
||||
import net.corda.core.identity.AbstractParty
|
||||
import net.corda.core.internal.Emoji
|
||||
import net.corda.core.internal.getRequiredTransaction
|
||||
import net.corda.core.internal.mapToSet
|
||||
import net.corda.core.transactions.ContractUpgradeLedgerTransaction
|
||||
import net.corda.core.transactions.LedgerTransaction
|
||||
import net.corda.core.utilities.OpaqueBytes
|
||||
import net.corda.core.utilities.getOrThrow
|
||||
import net.corda.coretesting.internal.matchers.flow.willReturn
|
||||
import net.corda.coretesting.internal.matchers.flow.willThrow
|
||||
import net.corda.finance.USD
|
||||
import net.corda.finance.`issued by`
|
||||
import net.corda.finance.contracts.asset.Cash
|
||||
import net.corda.finance.flows.CashIssueFlow
|
||||
import net.corda.finance.`issued by`
|
||||
import net.corda.testing.contracts.DummyContract
|
||||
import net.corda.testing.contracts.DummyContractV2
|
||||
import net.corda.testing.contracts.DummyContractV3
|
||||
import net.corda.testing.core.ALICE_NAME
|
||||
import net.corda.testing.core.BOB_NAME
|
||||
import net.corda.testing.core.singleIdentity
|
||||
import net.corda.coretesting.internal.matchers.flow.willReturn
|
||||
import net.corda.coretesting.internal.matchers.flow.willThrow
|
||||
import net.corda.testing.node.internal.*
|
||||
import net.corda.testing.node.internal.DUMMY_CONTRACTS_CORDAPP
|
||||
import net.corda.testing.node.internal.FINANCE_CONTRACTS_CORDAPP
|
||||
import net.corda.testing.node.internal.FINANCE_WORKFLOWS_CORDAPP
|
||||
import net.corda.testing.node.internal.InternalMockNetwork
|
||||
import net.corda.testing.node.internal.TestStartedNode
|
||||
import net.corda.testing.node.internal.enclosedCordapp
|
||||
import net.corda.testing.node.internal.startFlow
|
||||
import org.junit.AfterClass
|
||||
import org.junit.Test
|
||||
import java.util.*
|
||||
import java.util.Currency
|
||||
|
||||
class ContractUpgradeFlowTest : WithContracts, WithFinality {
|
||||
|
||||
@ -159,7 +182,7 @@ class ContractUpgradeFlowTest : WithContracts, WithFinality {
|
||||
@BelongsToContract(CashV2::class)
|
||||
data class State(override val amount: Amount<Issued<Currency>>, val owners: List<AbstractParty>) : FungibleAsset<Currency> {
|
||||
override val owner: AbstractParty = owners.first()
|
||||
override val exitKeys = (owners + amount.token.issuer.party).map { it.owningKey }.toSet()
|
||||
override val exitKeys = (owners + amount.token.issuer.party).mapToSet { it.owningKey }
|
||||
override val participants = owners
|
||||
|
||||
override fun withNewOwnerAndAmount(newAmount: Amount<Issued<Currency>>, newOwner: AbstractParty) = copy(amount = amount.copy(newAmount.quantity), owners = listOf(newOwner))
|
||||
@ -180,8 +203,7 @@ class ContractUpgradeFlowTest : WithContracts, WithFinality {
|
||||
isUpgrade<FROM, TO>())
|
||||
|
||||
private fun TestStartedNode.getContractUpgradeTransaction(state: StateAndRef<ContractState>) =
|
||||
services.validatedTransactions.getTransaction(state.ref.txhash)!!
|
||||
.resolveContractUpgradeTransaction(services)
|
||||
services.getRequiredTransaction(state.ref.txhash).resolveContractUpgradeTransaction(services)
|
||||
|
||||
private inline fun <reified FROM : Any, reified TO : Any> isUpgrade() =
|
||||
isUpgradeFrom<FROM>() and isUpgradeTo<TO>()
|
||||
|
@ -15,6 +15,7 @@ import net.corda.testing.core.singleIdentity
|
||||
import net.corda.testing.driver.DriverParameters
|
||||
import net.corda.testing.driver.driver
|
||||
import org.junit.Test
|
||||
import java.io.Serializable
|
||||
import java.sql.SQLTransientConnectionException
|
||||
import java.util.concurrent.CompletableFuture
|
||||
import kotlin.test.assertFailsWith
|
||||
@ -22,6 +23,8 @@ import kotlin.test.assertTrue
|
||||
|
||||
class FlowExternalAsyncOperationTest : AbstractFlowExternalOperationTest() {
|
||||
|
||||
private fun interface SerializableLambda2<S, T, R> : (S, T) -> R, Serializable
|
||||
|
||||
@Test(timeout = 300_000)
|
||||
fun `external async operation`() {
|
||||
driver(DriverParameters(notarySpecs = emptyList(), startNodesInProcess = true)) {
|
||||
@ -196,15 +199,15 @@ class FlowExternalAsyncOperationTest : AbstractFlowExternalOperationTest() {
|
||||
@StartableByRPC
|
||||
class FlowWithExternalAsyncOperationPropagatesException<T>(party: Party, private val exceptionType: Class<T>) :
|
||||
FlowWithExternalProcess(party) {
|
||||
|
||||
@Suspendable
|
||||
override fun testCode(): Any {
|
||||
val e = createException()
|
||||
return await(ExternalAsyncOperation(serviceHub) { _, _ ->
|
||||
|
||||
return await(ExternalAsyncOperation(serviceHub, (SerializableLambda2 { _, _ ->
|
||||
CompletableFuture<Any>().apply {
|
||||
completeExceptionally(e)
|
||||
}
|
||||
})
|
||||
})))
|
||||
}
|
||||
|
||||
private fun createException() = when (exceptionType) {
|
||||
@ -252,7 +255,6 @@ class FlowExternalAsyncOperationTest : AbstractFlowExternalOperationTest() {
|
||||
@StartableByRPC
|
||||
class FlowWithExternalAsyncOperationThatDirectlyAccessesServiceHubFailsRetry(party: Party) : FlowWithExternalProcess(party) {
|
||||
|
||||
@Suppress("TooGenericExceptionCaught")
|
||||
@Suspendable
|
||||
override fun testCode(): Any {
|
||||
return await(ExternalAsyncOperation(serviceHub) { _, _ ->
|
||||
|
@ -21,12 +21,15 @@ import net.corda.testing.driver.DriverParameters
|
||||
import net.corda.testing.driver.driver
|
||||
import net.corda.testing.node.internal.cordappsForPackages
|
||||
import org.junit.Test
|
||||
import java.io.Serializable
|
||||
import java.sql.SQLTransientConnectionException
|
||||
import kotlin.test.assertFailsWith
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
class FlowExternalOperationTest : AbstractFlowExternalOperationTest() {
|
||||
|
||||
private fun interface SerializableLambda2<S, T, R> : (S, T) -> R, Serializable
|
||||
|
||||
@Test(timeout = 300_000)
|
||||
fun `external operation`() {
|
||||
driver(DriverParameters(notarySpecs = emptyList(), startNodesInProcess = true)) {
|
||||
@ -254,7 +257,7 @@ class FlowExternalOperationTest : AbstractFlowExternalOperationTest() {
|
||||
@Suspendable
|
||||
override fun testCode() {
|
||||
val e = createException()
|
||||
await(ExternalOperation(serviceHub) { _, _ -> throw e })
|
||||
await(ExternalOperation(serviceHub, (SerializableLambda2 { _, _ -> throw e })))
|
||||
}
|
||||
|
||||
private fun createException() = when (exceptionType) {
|
||||
@ -292,7 +295,6 @@ class FlowExternalOperationTest : AbstractFlowExternalOperationTest() {
|
||||
@StartableByRPC
|
||||
class FlowWithExternalOperationThatDirectlyAccessesServiceHubFailsRetry(party: Party) : FlowWithExternalProcess(party) {
|
||||
|
||||
@Suppress("TooGenericExceptionCaught")
|
||||
@Suspendable
|
||||
override fun testCode(): Any {
|
||||
try {
|
||||
|
@ -20,11 +20,13 @@ import net.corda.testing.driver.DriverParameters
|
||||
import net.corda.testing.driver.driver
|
||||
import net.corda.testing.internal.IS_S390X
|
||||
import org.junit.Assume
|
||||
import org.junit.Ignore
|
||||
import org.junit.Test
|
||||
import java.time.Duration
|
||||
import java.time.Instant
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
@Ignore("TODO JDK17: Fixme - flaky test")
|
||||
class FlowSleepTest {
|
||||
|
||||
@Test(timeout = 300_000)
|
||||
|
@ -13,6 +13,7 @@ import net.corda.core.flows.ReceiveFinalityFlow
|
||||
import net.corda.core.flows.StartableByRPC
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.core.internal.FlowStateMachineHandle
|
||||
import net.corda.core.internal.getRequiredTransaction
|
||||
import net.corda.core.messaging.CordaRPCOps
|
||||
import net.corda.core.messaging.FlowHandle
|
||||
import net.corda.core.messaging.startFlow
|
||||
@ -26,9 +27,7 @@ interface WithFinality : WithMockNet {
|
||||
return startFlowAndRunNetwork(FinalityInvoker(stx, recipients.toSet(), emptySet()))
|
||||
}
|
||||
|
||||
fun TestStartedNode.getValidatedTransaction(stx: SignedTransaction): SignedTransaction {
|
||||
return services.validatedTransactions.getTransaction(stx.id)!!
|
||||
}
|
||||
fun TestStartedNode.getValidatedTransaction(stx: SignedTransaction): SignedTransaction = services.getRequiredTransaction(stx.id)
|
||||
|
||||
fun CordaRPCOps.finalise(stx: SignedTransaction, vararg recipients: Party): FlowHandle<SignedTransaction> {
|
||||
return startFlow(WithFinality::FinalityInvoker, stx, recipients.toSet(), emptySet()).andRunNetwork()
|
||||
|
@ -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
|
||||
|
||||
import com.nhaarman.mockito_kotlin.doReturn
|
||||
import com.nhaarman.mockito_kotlin.whenever
|
||||
import org.mockito.kotlin.doReturn
|
||||
import org.mockito.kotlin.whenever
|
||||
import net.corda.core.crypto.generateKeyPair
|
||||
import net.corda.core.internal.getPackageOwnerOf
|
||||
import net.corda.core.node.NetworkParameters
|
||||
|
@ -15,7 +15,7 @@ class VaultUpdateTests {
|
||||
private companion object {
|
||||
const val DUMMY_PROGRAM_ID = "net.corda.coretests.node.VaultUpdateTests\$DummyContract"
|
||||
val DUMMY_NOTARY = TestIdentity(DUMMY_NOTARY_NAME, 20).party
|
||||
val emptyUpdate = Vault.Update(emptySet(), emptySet(), type = Vault.UpdateType.GENERAL, references = emptySet())
|
||||
val emptyUpdate = Vault.Update(emptySet<StateAndRef<*>>(), emptySet(), type = Vault.UpdateType.GENERAL, references = emptySet())
|
||||
}
|
||||
|
||||
object DummyContract : Contract {
|
||||
|
@ -1,6 +1,6 @@
|
||||
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.crypto.Crypto
|
||||
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.declaredField
|
||||
import net.corda.core.internal.hash
|
||||
import net.corda.core.internal.inputStream
|
||||
import net.corda.core.node.NetworkParameters
|
||||
import net.corda.core.node.services.AttachmentId
|
||||
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.junit.Assert.assertArrayEquals
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertNull
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
@ -55,14 +53,16 @@ import java.io.InputStream
|
||||
import java.net.URL
|
||||
import java.nio.file.Path
|
||||
import java.security.PublicKey
|
||||
import kotlin.io.path.inputStream
|
||||
import kotlin.io.path.readBytes
|
||||
import kotlin.test.assertFailsWith
|
||||
import kotlin.test.fail
|
||||
|
||||
class AttachmentsClassLoaderTests {
|
||||
companion object {
|
||||
// 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_V4: URL = AttachmentsClassLoaderTests::class.java.getResource("isolated-4.0.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")!!
|
||||
private const val ISOLATED_CONTRACT_CLASS_NAME = "net.corda.finance.contracts.isolated.AnotherDummyContract"
|
||||
|
||||
private fun readAttachment(attachment: Attachment, filepath: String): ByteArray {
|
||||
@ -128,8 +128,6 @@ class AttachmentsClassLoaderTests {
|
||||
@Test(timeout=300_000)
|
||||
fun `test contracts have no permissions for protection domain`() {
|
||||
val isolatedId = importAttachment(ISOLATED_CONTRACTS_JAR_PATH.openStream(), "app", "isolated.jar")
|
||||
assertNull(System.getSecurityManager())
|
||||
|
||||
createClassloader(isolatedId).use { classLoader ->
|
||||
val contractClass = Class.forName(ISOLATED_CONTRACT_CLASS_NAME, true, classLoader)
|
||||
val protectionDomain = contractClass.protectionDomain ?: fail("Protection Domain missing")
|
||||
@ -614,7 +612,7 @@ class AttachmentsClassLoaderTests {
|
||||
|
||||
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")
|
||||
@Deprecated("Use signerKeys. There is no requirement that attachment signers are Corda parties.")
|
||||
override val signers: List<Party> = emptyList()
|
||||
|
@ -1,8 +1,8 @@
|
||||
package net.corda.coretests.transactions
|
||||
|
||||
import com.nhaarman.mockito_kotlin.doReturn
|
||||
import com.nhaarman.mockito_kotlin.mock
|
||||
import com.nhaarman.mockito_kotlin.whenever
|
||||
import org.mockito.kotlin.doReturn
|
||||
import org.mockito.kotlin.mock
|
||||
import org.mockito.kotlin.whenever
|
||||
import net.corda.core.contracts.*
|
||||
import net.corda.core.crypto.generateKeyPair
|
||||
import net.corda.core.identity.AbstractParty
|
||||
|
@ -1,8 +1,8 @@
|
||||
package net.corda.coretests.transactions
|
||||
|
||||
import com.nhaarman.mockito_kotlin.doReturn
|
||||
import com.nhaarman.mockito_kotlin.mock
|
||||
import com.nhaarman.mockito_kotlin.whenever
|
||||
import org.mockito.kotlin.doReturn
|
||||
import org.mockito.kotlin.mock
|
||||
import org.mockito.kotlin.whenever
|
||||
import net.corda.core.contracts.*
|
||||
import net.corda.core.contracts.Requirements.using
|
||||
import net.corda.core.crypto.SecureHash
|
||||
|
@ -1,10 +1,6 @@
|
||||
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.ContractAttachment
|
||||
import net.corda.core.contracts.HashAttachmentConstraint
|
||||
import net.corda.core.contracts.PrivacySalt
|
||||
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.TimeWindow
|
||||
import net.corda.core.contracts.TransactionState
|
||||
import net.corda.core.contracts.TransactionVerificationException
|
||||
import net.corda.core.cordapp.CordappProvider
|
||||
import net.corda.core.crypto.CompositeKey
|
||||
import net.corda.core.contracts.TransactionVerificationException.UnsupportedHashTypeException
|
||||
import net.corda.core.crypto.DigestService
|
||||
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.PLATFORM_VERSION
|
||||
import net.corda.core.internal.digestService
|
||||
import net.corda.core.node.ServicesForResolution
|
||||
import net.corda.core.node.ZoneVersionTooLowException
|
||||
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.serialization.serialize
|
||||
import net.corda.core.serialization.internal._driverSerializationEnv
|
||||
import net.corda.core.transactions.TransactionBuilder
|
||||
import net.corda.coretesting.internal.rigorousMock
|
||||
import net.corda.testing.common.internal.testNetworkParameters
|
||||
import net.corda.testing.contracts.DummyContract
|
||||
import net.corda.testing.contracts.DummyState
|
||||
import net.corda.testing.core.ALICE_NAME
|
||||
import net.corda.testing.core.BOB_NAME
|
||||
import net.corda.testing.core.DUMMY_NOTARY_NAME
|
||||
import net.corda.testing.core.DummyCommandData
|
||||
import net.corda.testing.core.SerializationEnvironmentRule
|
||||
import net.corda.testing.core.TestIdentity
|
||||
import net.corda.testing.node.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.assertThatExceptionOfType
|
||||
import org.assertj.core.api.Assertions.assertThatThrownBy
|
||||
import org.junit.Assert.assertFalse
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Before
|
||||
import org.junit.Ignore
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import java.security.PublicKey
|
||||
import java.time.Instant
|
||||
import kotlin.test.assertFailsWith
|
||||
|
||||
@ -57,33 +45,12 @@ class TransactionBuilderTest {
|
||||
val testSerialization = SerializationEnvironmentRule()
|
||||
|
||||
private val notary = TestIdentity(DUMMY_NOTARY_NAME).party
|
||||
private val services = rigorousMock<ServicesForResolution>()
|
||||
private val contractAttachmentId = SecureHash.randomSHA256()
|
||||
private val attachments = rigorousMock<AttachmentStorage>()
|
||||
private val networkParametersService = mock<NetworkParametersService>()
|
||||
|
||||
@Before
|
||||
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")
|
||||
}
|
||||
private val services = MockServices(
|
||||
listOf("net.corda.testing.contracts"),
|
||||
TestIdentity(ALICE_NAME),
|
||||
testNetworkParameters(minimumPlatformVersion = PLATFORM_VERSION)
|
||||
)
|
||||
private val contractAttachmentId = services.attachments.getLatestContractAttachments(DummyContract.PROGRAM_ID)[0]
|
||||
|
||||
@Test(timeout=300_000)
|
||||
fun `bare minimum issuance tx`() {
|
||||
@ -99,13 +66,11 @@ class TransactionBuilderTest {
|
||||
val wtx = builder.toWireTransaction(services)
|
||||
assertThat(wtx.outputs).containsOnly(outputState)
|
||||
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)
|
||||
fun `automatic hash constraint`() {
|
||||
doReturn(unsignedAttachment).whenever(attachments).openAttachment(contractAttachmentId)
|
||||
|
||||
val outputState = TransactionState(data = DummyState(), contract = DummyContract.PROGRAM_ID, notary = notary)
|
||||
val builder = TransactionBuilder()
|
||||
.addOutputState(outputState)
|
||||
@ -116,8 +81,6 @@ class TransactionBuilderTest {
|
||||
|
||||
@Test(timeout=300_000)
|
||||
fun `reference states`() {
|
||||
doReturn(unsignedAttachment).whenever(attachments).openAttachment(contractAttachmentId)
|
||||
|
||||
val referenceState = TransactionState(DummyState(), DummyContract.PROGRAM_ID, notary)
|
||||
val referenceStateRef = StateRef(SecureHash.randomSHA256(), 1)
|
||||
val builder = TransactionBuilder(notary)
|
||||
@ -125,32 +88,41 @@ class TransactionBuilderTest {
|
||||
.addOutputState(TransactionState(DummyState(), DummyContract.PROGRAM_ID, notary))
|
||||
.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) }
|
||||
.isInstanceOf(ZoneVersionTooLowException::class.java)
|
||||
.hasMessageContaining("Reference states")
|
||||
}
|
||||
|
||||
doReturn(testNetworkParameters(minimumPlatformVersion = 4)).whenever(services).networkParameters
|
||||
doReturn(referenceState).whenever(services).loadState(referenceStateRef)
|
||||
with(testNetworkParameters(minimumPlatformVersion = 4)) {
|
||||
val services = MockServices(listOf("net.corda.testing.contracts"), TestIdentity(ALICE_NAME), this)
|
||||
val wtx = builder.toWireTransaction(services)
|
||||
assertThat(wtx.references).containsOnly(referenceStateRef)
|
||||
}
|
||||
}
|
||||
|
||||
@Test(timeout=300_000)
|
||||
fun `automatic signature constraint`() {
|
||||
val aliceParty = TestIdentity(ALICE_NAME).party
|
||||
val bobParty = TestIdentity(BOB_NAME).party
|
||||
val compositeKey = CompositeKey.Builder().addKeys(aliceParty.owningKey, bobParty.owningKey).build()
|
||||
val expectedConstraint = SignatureAttachmentConstraint(compositeKey)
|
||||
val signedAttachment = signedAttachment(aliceParty, bobParty)
|
||||
// We need to use a MockNetwork so that we can create a signed attachment. However, SerializationEnvironmentRule and MockNetwork
|
||||
// don't work well together, so we temporarily clear out the driverSerializationEnv for this test.
|
||||
val driverSerializationEnv = _driverSerializationEnv.get()
|
||||
_driverSerializationEnv.set(null)
|
||||
val mockNetwork = MockNetwork(
|
||||
MockNetworkParameters(
|
||||
networkParameters = testNetworkParameters(minimumPlatformVersion = PLATFORM_VERSION),
|
||||
cordappsForAllNodes = listOf(cordappWithPackages("net.corda.testing.contracts").signed())
|
||||
)
|
||||
)
|
||||
|
||||
assertTrue(expectedConstraint.isSatisfiedBy(signedAttachment))
|
||||
assertFalse(expectedConstraint.isSatisfiedBy(unsignedAttachment))
|
||||
try {
|
||||
val services = mockNetwork.notaryNodes[0].services
|
||||
|
||||
doReturn(attachments).whenever(services).attachments
|
||||
doReturn(signedAttachment).whenever(attachments).openAttachment(contractAttachmentId)
|
||||
doReturn(listOf(contractAttachmentId)).whenever(attachments)
|
||||
.getLatestContractAttachments("net.corda.testing.contracts.DummyContract")
|
||||
val attachment = services.attachments.openAttachment(services.attachments.getLatestContractAttachments(DummyContract.PROGRAM_ID)[0])
|
||||
val attachmentSigner = attachment!!.signerKeys.single()
|
||||
|
||||
val expectedConstraint = SignatureAttachmentConstraint(attachmentSigner)
|
||||
assertTrue(expectedConstraint.isSatisfiedBy(attachment))
|
||||
|
||||
val outputState = TransactionState(data = DummyState(), contract = DummyContract.PROGRAM_ID, notary = notary)
|
||||
val builder = TransactionBuilder()
|
||||
@ -159,19 +131,11 @@ class TransactionBuilderTest {
|
||||
val wtx = builder.toWireTransaction(services)
|
||||
|
||||
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)
|
||||
fun `list accessors are mutable copies`() {
|
||||
@ -270,7 +234,7 @@ class TransactionBuilderTest {
|
||||
}
|
||||
|
||||
@Ignore
|
||||
@Test(timeout=300_000, expected = TransactionVerificationException.UnsupportedHashTypeException::class)
|
||||
@Test(timeout=300_000)
|
||||
fun `throws with non-default hash algorithm`() {
|
||||
HashAgility.init()
|
||||
try {
|
||||
@ -286,13 +250,15 @@ class TransactionBuilderTest {
|
||||
.addOutputState(outputState)
|
||||
.addCommand(DummyCommandData, notary.owningKey)
|
||||
|
||||
assertThatExceptionOfType(UnsupportedHashTypeException::class.java).isThrownBy {
|
||||
builder.toWireTransaction(services)
|
||||
}
|
||||
} finally {
|
||||
HashAgility.init()
|
||||
}
|
||||
}
|
||||
|
||||
@Test(timeout=300_000, expected = Test.None::class)
|
||||
@Test(timeout=300_000)
|
||||
fun `allows non-default hash algorithm`() {
|
||||
HashAgility.init(txHashAlgoName = DigestService.sha2_384.hashAlgorithm)
|
||||
assertThat(services.digestService).isEqualTo(DigestService.sha2_384)
|
||||
|
@ -1,8 +1,8 @@
|
||||
package net.corda.coretests.transactions
|
||||
|
||||
import com.nhaarman.mockito_kotlin.doReturn
|
||||
import com.nhaarman.mockito_kotlin.mock
|
||||
import com.nhaarman.mockito_kotlin.whenever
|
||||
import org.mockito.kotlin.doReturn
|
||||
import org.mockito.kotlin.mock
|
||||
import org.mockito.kotlin.whenever
|
||||
import net.corda.core.contracts.*
|
||||
import net.corda.core.identity.AbstractParty
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
|
@ -1,7 +1,7 @@
|
||||
package net.corda.coretests.transactions
|
||||
|
||||
import com.nhaarman.mockito_kotlin.doReturn
|
||||
import com.nhaarman.mockito_kotlin.whenever
|
||||
import org.mockito.kotlin.doReturn
|
||||
import org.mockito.kotlin.whenever
|
||||
import net.corda.core.contracts.*
|
||||
import net.corda.core.crypto.*
|
||||
import net.corda.core.crypto.CompositeKey
|
||||
|
@ -1,22 +1,17 @@
|
||||
import static org.gradle.api.JavaVersion.VERSION_1_8
|
||||
|
||||
apply plugin: 'kotlin'
|
||||
apply plugin: 'kotlin-jpa'
|
||||
apply plugin: 'org.jetbrains.kotlin.jvm'
|
||||
apply plugin: 'org.jetbrains.kotlin.plugin.jpa'
|
||||
apply plugin: 'net.corda.plugins.quasar-utils'
|
||||
apply plugin: 'net.corda.plugins.publish-utils'
|
||||
apply plugin: 'net.corda.plugins.api-scanner'
|
||||
apply plugin: 'com.jfrog.artifactory'
|
||||
apply plugin: 'corda.common-publishing'
|
||||
|
||||
description 'Corda core'
|
||||
|
||||
targetCompatibility = VERSION_1_8
|
||||
|
||||
sourceSets {
|
||||
obfuscator
|
||||
}
|
||||
|
||||
configurations {
|
||||
integrationTestCompile.extendsFrom testCompile
|
||||
integrationTestImplementation.extendsFrom testImplementation
|
||||
integrationTestRuntimeOnly.extendsFrom testRuntimeOnly
|
||||
|
||||
smokeTestCompile.extendsFrom compile
|
||||
@ -24,10 +19,9 @@ configurations {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
||||
obfuscatorImplementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
|
||||
compileOnly "io.opentelemetry:opentelemetry-api:${open_telemetry_version}"
|
||||
implementation "io.opentelemetry:opentelemetry-api:${open_telemetry_version}"
|
||||
compileOnly project(':opentelemetry')
|
||||
|
||||
testImplementation sourceSets.obfuscator.output
|
||||
testImplementation "org.junit.jupiter:junit-jupiter-api:${junit_jupiter_version}"
|
||||
testImplementation "junit:junit:$junit_version"
|
||||
@ -35,72 +29,65 @@ dependencies {
|
||||
testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:${junit_jupiter_version}"
|
||||
testRuntimeOnly "org.junit.platform:junit-platform-launcher:${junit_platform_version}"
|
||||
|
||||
testCompile "commons-fileupload:commons-fileupload:$fileupload_version"
|
||||
testImplementation "commons-fileupload:commons-fileupload:$fileupload_version"
|
||||
|
||||
// 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"
|
||||
compile "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
|
||||
testCompile "org.jetbrains.kotlin:kotlin-test:$kotlin_version"
|
||||
implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
|
||||
testImplementation "org.jetbrains.kotlin:kotlin-test:$kotlin_version"
|
||||
|
||||
// Hamkrest, for fluent, composable matchers
|
||||
testCompile "com.natpryce:hamkrest:$hamkrest_version"
|
||||
|
||||
// Thread safety annotations
|
||||
compile "com.google.code.findbugs:jsr305:$jsr305_version"
|
||||
testImplementation "com.natpryce:hamkrest:$hamkrest_version"
|
||||
|
||||
// SLF4J: commons-logging bindings for a SLF4J back end
|
||||
compile "org.slf4j:jcl-over-slf4j:$slf4j_version"
|
||||
compile "org.slf4j:slf4j-api:$slf4j_version"
|
||||
implementation "org.slf4j:jcl-over-slf4j:$slf4j_version"
|
||||
implementation "org.slf4j:slf4j-api:$slf4j_version"
|
||||
|
||||
// AssertJ: for fluent assertions for testing
|
||||
testCompile "org.assertj:assertj-core:${assertj_version}"
|
||||
testImplementation "org.assertj:assertj-core:${assertj_version}"
|
||||
|
||||
// Guava: Google utilities library.
|
||||
compile "com.google.guava:guava:$guava_version"
|
||||
implementation "com.google.guava:guava:$guava_version"
|
||||
|
||||
// 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.
|
||||
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/
|
||||
compile "net.i2p.crypto:eddsa:$eddsa_version"
|
||||
implementation "net.i2p.crypto:eddsa:$eddsa_version"
|
||||
|
||||
// Bouncy castle support needed for X509 certificate manipulation
|
||||
compile "org.bouncycastle:bcprov-jdk18on:${bouncycastle_version}"
|
||||
compile "org.bouncycastle:bcpkix-jdk18on:${bouncycastle_version}"
|
||||
|
||||
// JPA 2.2 annotations.
|
||||
compile "javax.persistence:javax.persistence-api:2.2"
|
||||
implementation "org.bouncycastle:bcprov-jdk18on:${bouncycastle_version}"
|
||||
testImplementation "org.bouncycastle:bcpkix-jdk18on:${bouncycastle_version}"
|
||||
|
||||
// required to use @Type annotation
|
||||
compile "org.hibernate:hibernate-core:$hibernate_version"
|
||||
implementation "org.hibernate:hibernate-core:$hibernate_version"
|
||||
|
||||
// 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
|
||||
testRuntimeOnly "com.esotericsoftware:kryo:$kryo_version"
|
||||
testRuntimeOnly "org.slf4j:slf4j-simple:$slf4j_version"
|
||||
|
||||
testCompile "com.nhaarman:mockito-kotlin:$mockito_kotlin_version"
|
||||
testCompile "org.mockito:mockito-core:$mockito_version"
|
||||
testCompile "org.assertj:assertj-core:$assertj_version"
|
||||
testCompile "com.natpryce:hamkrest:$hamkrest_version"
|
||||
testCompile 'org.hamcrest:hamcrest-library:2.1'
|
||||
|
||||
testImplementation "org.mockito.kotlin:mockito-kotlin:$mockito_kotlin_version"
|
||||
testImplementation "org.mockito:mockito-core:$mockito_version"
|
||||
testImplementation "org.assertj:assertj-core:$assertj_version"
|
||||
testImplementation "com.natpryce:hamkrest:$hamkrest_version"
|
||||
testImplementation 'org.hamcrest:hamcrest-library:2.1'
|
||||
}
|
||||
|
||||
// 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
|
||||
into "$project.rootProject.projectDir/lib"
|
||||
rename { filename -> "quasar.jar" }
|
||||
@ -110,13 +97,16 @@ jar {
|
||||
finalizedBy(copyQuasarJar)
|
||||
archiveBaseName = 'corda-core'
|
||||
archiveClassifier = ''
|
||||
|
||||
manifest {
|
||||
attributes('Add-Opens': 'java.base/java.net java.base/java.nio')
|
||||
}
|
||||
}
|
||||
|
||||
configurations {
|
||||
testArtifacts.extendsFrom testRuntimeClasspath
|
||||
}
|
||||
|
||||
|
||||
processTestResources {
|
||||
inputs.files(jar)
|
||||
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 {
|
||||
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()
|
||||
}
|
||||
|
||||
task testJar(type: Jar) {
|
||||
tasks.register('testJar', Jar) {
|
||||
dependsOn testClasses
|
||||
classifier "tests"
|
||||
from sourceSets.test.output
|
||||
}
|
||||
@ -165,11 +167,6 @@ quasar {
|
||||
"io.opentelemetry.**")
|
||||
}
|
||||
|
||||
artifacts {
|
||||
testArtifacts testJar
|
||||
publish testJar
|
||||
}
|
||||
|
||||
scanApi {
|
||||
excludeClasses = [
|
||||
// Kotlin should probably have declared this class as "synthetic".
|
||||
@ -177,13 +174,23 @@ scanApi {
|
||||
]
|
||||
}
|
||||
|
||||
publish {
|
||||
name jar.baseName
|
||||
}
|
||||
|
||||
tasks.register("writeTestResources", JavaExec) {
|
||||
classpath sourceSets.obfuscator.output
|
||||
classpath sourceSets.obfuscator.runtimeClasspath
|
||||
main 'net.corda.core.internal.utilities.TestResourceWriter'
|
||||
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")
|
||||
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.utilities.getOrThrow
|
||||
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)
|
||||
|
||||
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> {
|
||||
val resultFuture = openFuture<W>()
|
||||
val winnerChosen = AtomicBoolean()
|
||||
@ -40,7 +39,7 @@ internal fun <V, W> firstOf(futures: Array<out CordaFuture<out V>>, log: Logger,
|
||||
it.isCancelled -> {
|
||||
// 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 {
|
||||
fun open(): InputStream
|
||||
|
||||
@JvmDefault
|
||||
fun openAsJAR(): JarInputStream {
|
||||
val stream = open()
|
||||
return try {
|
||||
@ -49,7 +48,6 @@ interface Attachment : NamedByHash {
|
||||
* 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.
|
||||
*/
|
||||
@JvmDefault
|
||||
fun extractFile(path: String, outputTo: OutputStream) = openAsJAR().use { it.extractFile(path, outputTo) }
|
||||
|
||||
/**
|
||||
|
@ -27,6 +27,7 @@ class ContractAttachment private constructor(
|
||||
|
||||
companion object {
|
||||
@CordaInternal
|
||||
@JvmSynthetic
|
||||
fun create(attachment: Attachment,
|
||||
contract: ContractClassName,
|
||||
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.OID.$COMPOSITE_SIGNATURE", CompositeSignature.SIGNATURE_ALGORITHM)
|
||||
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() {
|
||||
|
@ -1,10 +1,10 @@
|
||||
package net.corda.core.crypto.internal
|
||||
|
||||
import net.corda.core.crypto.DigestAlgorithm
|
||||
import java.lang.reflect.Constructor
|
||||
import net.corda.core.internal.loadClassOfType
|
||||
import java.security.MessageDigest
|
||||
import java.security.NoSuchAlgorithmException
|
||||
import java.util.*
|
||||
import java.util.Collections
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
|
||||
sealed class DigestAlgorithmFactory {
|
||||
@ -28,9 +28,8 @@ sealed class DigestAlgorithmFactory {
|
||||
}
|
||||
|
||||
private class CustomAlgorithmFactory(className: String) : DigestAlgorithmFactory() {
|
||||
val constructor: Constructor<out DigestAlgorithm> = Class.forName(className, false, javaClass.classLoader)
|
||||
.asSubclass(DigestAlgorithm::class.java)
|
||||
.getConstructor()
|
||||
private val constructor = loadClassOfType<DigestAlgorithm>(className, false, javaClass.classLoader).getConstructor()
|
||||
|
||||
override val algorithm: String = constructor.newInstance().algorithm
|
||||
|
||||
override fun create(): DigestAlgorithm {
|
||||
|
@ -14,12 +14,13 @@ import java.io.InputStream
|
||||
import java.security.Provider
|
||||
import java.security.SecureRandom
|
||||
import java.security.SecureRandomSpi
|
||||
import kotlin.system.exitProcess
|
||||
|
||||
/**
|
||||
* This has been migrated into a separate class so that it
|
||||
* is easier to delete from the core-deterministic module.
|
||||
*/
|
||||
internal val platformSecureRandom: () -> SecureRandom = when {
|
||||
val platformSecureRandom: () -> SecureRandom = when {
|
||||
SgxSupport.isInsideEnclave -> {
|
||||
{ DummySecureRandom }
|
||||
}
|
||||
@ -29,23 +30,22 @@ internal val platformSecureRandom: () -> SecureRandom = when {
|
||||
}
|
||||
|
||||
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 {
|
||||
const val algorithm = "CordaPRNG"
|
||||
const val ALGORITHM = "CordaPRNG"
|
||||
|
||||
private val logger = loggerFor<PlatformSecureRandomService>()
|
||||
}
|
||||
|
||||
private val instance: SecureRandomSpi = if (SystemUtils.IS_OS_LINUX) tryAndUseLinuxSecureRandomSpi() else PlatformSecureRandomSpi()
|
||||
|
||||
@Suppress("TooGenericExceptionCaught", "TooGenericExceptionThrown")
|
||||
private fun tryAndUseLinuxSecureRandomSpi(): SecureRandomSpi = try {
|
||||
LinuxSecureRandomSpi()
|
||||
} catch (e: Exception) {
|
||||
logger.error("Unable to initialise LinuxSecureRandomSpi. The exception logged with this message might assist with diagnosis." +
|
||||
" The process will now exit.", e)
|
||||
System.exit(1)
|
||||
throw RuntimeException("Never reached, but calms the compiler.")
|
||||
exitProcess(1)
|
||||
}
|
||||
|
||||
override fun newInstance(constructorParameter: Any?) = instance
|
||||
@ -63,7 +63,7 @@ private class PlatformSecureRandomSpi : SecureRandomSpi() {
|
||||
override fun engineGenerateSeed(numBytes: Int): ByteArray = secureRandom.generateSeed(numBytes)
|
||||
}
|
||||
|
||||
@Suppress("TooGenericExceptionCaught", "TooGenericExceptionThrown")
|
||||
@Suppress("TooGenericExceptionThrown")
|
||||
private class LinuxSecureRandomSpi : SecureRandomSpi() {
|
||||
private fun openURandom(): InputStream {
|
||||
try {
|
||||
@ -91,5 +91,5 @@ private class LinuxSecureRandomSpi : SecureRandomSpi() {
|
||||
|
||||
// This is safe to share because of the underlying implementation of SecureRandomSpi
|
||||
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.
|
||||
Security.insertProviderAt(it, 1) // The position is 1-based.
|
||||
}
|
||||
|
||||
// OID taken from https://tools.ietf.org/html/draft-ietf-curdle-pkix-00
|
||||
val `id-Curve25519ph` = ASN1ObjectIdentifier("1.3.101.112")
|
||||
val cordaBouncyCastleProvider = BouncyCastleProvider().apply {
|
||||
@ -45,6 +46,23 @@ val cordaBouncyCastleProvider = BouncyCastleProvider().apply {
|
||||
// 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.
|
||||
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 {
|
||||
|
@ -573,7 +573,7 @@ abstract class FlowLogic<out T> {
|
||||
}
|
||||
|
||||
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>> {
|
||||
|
@ -11,6 +11,8 @@ import net.corda.core.internal.NetworkParametersStorage
|
||||
import net.corda.core.internal.PlatformVersionSwitches
|
||||
import net.corda.core.internal.RetrieveAnyTransactionPayload
|
||||
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.node.ServicesForResolution
|
||||
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 RetrieveAnyTransactionPayload -> TransactionAuthorisationFilter(acceptAll = true)
|
||||
is List<*> -> TransactionAuthorisationFilter().addAuthorised(payload.flatMap { someObject ->
|
||||
if (someObject is StateAndRef<*>) {
|
||||
getInputTransactions(serviceHub.validatedTransactions.getTransaction(someObject.ref.txhash)!!) + someObject.ref.txhash
|
||||
}
|
||||
else if (someObject is NamedByHash) {
|
||||
setOf(someObject.id)
|
||||
} else {
|
||||
throw Exception("Unknown payload type: ${someObject!!::class.java} ?")
|
||||
when (someObject) {
|
||||
is StateAndRef<*> -> getInputTransactions(serviceHub.getRequiredTransaction(someObject.ref.txhash)) + someObject.ref.txhash
|
||||
is NamedByHash -> setOf(someObject.id)
|
||||
else -> throw Exception("Unknown payload type: ${someObject!!::class.java} ?")
|
||||
}
|
||||
}.toSet())
|
||||
else -> throw Exception("Unknown payload type: ${payload::class.java} ?")
|
||||
@ -308,7 +307,7 @@ open class DataVendingFlow(val otherSessions: Set<FlowSession>, val payload: Any
|
||||
|
||||
@Suspendable
|
||||
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) {
|
||||
|
@ -1,7 +1,6 @@
|
||||
package net.corda.core.identity
|
||||
|
||||
import net.corda.core.internal.CertRole
|
||||
import net.corda.core.internal.uncheckedCast
|
||||
import net.corda.core.internal.validate
|
||||
import net.corda.core.serialization.CordaSerializable
|
||||
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
|
||||
// an all-null chain is in theory valid.
|
||||
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()) {
|
||||
val certificate = certChain[certIdx]
|
||||
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>,
|
||||
classVersionRange: IntRange? = null): Set<T> {
|
||||
return getNamesOfClassesImplementing(classloader, clazz, classVersionRange)
|
||||
.map { Class.forName(it, false, classloader).asSubclass(clazz) }
|
||||
.mapTo(LinkedHashSet()) { it.kotlin.objectOrNewInstance() }
|
||||
.mapToSet { loadClassOfType(clazz, it, false, classloader).kotlin.objectOrNewInstance() }
|
||||
}
|
||||
|
||||
/**
|
||||
@ -36,7 +35,13 @@ fun <T: Any> createInstancesOfClassesImplementing(classloader: ClassLoader, claz
|
||||
*/
|
||||
fun <T: Any> getNamesOfClassesImplementing(classloader: ClassLoader, clazz: Class<T>,
|
||||
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)
|
||||
.ignoreParentClassLoaders()
|
||||
.enableClassInfo()
|
||||
@ -50,10 +55,23 @@ fun <T: Any> getNamesOfClassesImplementing(classloader: ClassLoader, clazz: Clas
|
||||
}
|
||||
result.getClassesImplementing(clazz.name)
|
||||
.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 {
|
||||
val threadClassLoader = Thread.currentThread().contextClassLoader
|
||||
try {
|
||||
@ -62,5 +80,4 @@ fun <T: Any?> executeWithThreadContextClassLoader(classloader: ClassLoader, fn:
|
||||
} finally {
|
||||
Thread.currentThread().contextClassLoader = threadClassLoader
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,35 +1,29 @@
|
||||
@file:Suppress("TooManyFunctions")
|
||||
package net.corda.core.internal
|
||||
|
||||
import net.corda.core.contracts.Attachment
|
||||
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.FlowLogic
|
||||
import net.corda.core.node.NetworkParameters
|
||||
import net.corda.core.node.ServiceHub
|
||||
import net.corda.core.node.ServicesForResolution
|
||||
import net.corda.core.node.ZoneVersionTooLowException
|
||||
import net.corda.core.node.services.AttachmentId
|
||||
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.node.services.TransactionStorage
|
||||
import net.corda.core.serialization.CordaSerializable
|
||||
import net.corda.core.serialization.SerializationContext
|
||||
import net.corda.core.transactions.LedgerTransaction
|
||||
import net.corda.core.transactions.SignedTransaction
|
||||
import net.corda.core.transactions.TransactionBuilder
|
||||
import net.corda.core.transactions.WireTransaction
|
||||
import org.slf4j.MDC
|
||||
import java.security.PublicKey
|
||||
import java.util.jar.JarInputStream
|
||||
|
||||
// *Internal* Corda-specific utilities.
|
||||
|
||||
|
||||
// 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) {
|
||||
checkMinimumPlatformVersion(networkParameters.minimumPlatformVersion, requiredMinPlatformVersion, feature)
|
||||
@ -68,11 +62,6 @@ fun TransactionBuilder.toWireTransaction(services: ServicesForResolution, serial
|
||||
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. */
|
||||
fun Class<out FlowLogic<*>>.isIdempotentFlow(): Boolean {
|
||||
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 The set of [AttachmentId]s after the node's fix-up rules have been applied to [attachmentIds].
|
||||
*/
|
||||
fun CordappProvider.internalFixupAttachmentIds(attachmentIds: Collection<AttachmentId>): Set<AttachmentId> {
|
||||
return (this as CordappFixupInternal).fixupAttachmentIds(attachmentIds)
|
||||
fun TransactionStorage.getRequiredTransaction(txhash: SecureHash): SignedTransaction {
|
||||
return getTransaction(txhash) ?: throw TransactionResolutionException(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
|
||||
}
|
||||
}
|
||||
}
|
||||
fun ServiceHub.getRequiredTransaction(txhash: SecureHash): SignedTransaction = validatedTransactions.getRequiredTransaction(txhash)
|
||||
|
@ -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
|
||||
|
||||
import net.corda.core.crypto.Crypto
|
||||
@ -23,22 +22,18 @@ import rx.subjects.UnicastSubject
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.io.IOException
|
||||
import java.io.InputStream
|
||||
import java.io.OutputStream
|
||||
import java.lang.reflect.Field
|
||||
import java.lang.reflect.Member
|
||||
import java.lang.reflect.Modifier
|
||||
import java.math.BigDecimal
|
||||
import java.net.HttpURLConnection
|
||||
import java.net.HttpURLConnection.HTTP_MOVED_PERM
|
||||
import java.net.HttpURLConnection.HTTP_OK
|
||||
import java.net.Proxy
|
||||
import java.net.URI
|
||||
import java.net.URL
|
||||
import java.nio.ByteBuffer
|
||||
import java.nio.file.CopyOption
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Path
|
||||
import java.nio.file.Paths
|
||||
import java.security.KeyPair
|
||||
import java.security.MessageDigest
|
||||
import java.security.PrivateKey
|
||||
@ -73,6 +68,7 @@ import java.util.stream.StreamSupport
|
||||
import java.util.zip.Deflater
|
||||
import java.util.zip.ZipEntry
|
||||
import java.util.zip.ZipOutputStream
|
||||
import kotlin.io.path.toPath
|
||||
import kotlin.math.roundToLong
|
||||
import kotlin.reflect.KClass
|
||||
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.times(multiplicand: Long): Duration = multipliedBy(multiplicand)
|
||||
operator fun Duration.times(multiplicand: Double): Duration = Duration.ofNanos((toNanos() * multiplicand).roundToLong())
|
||||
fun min(d1: Duration, d2: Duration): Duration = if (d1 <= d2) d1 else d2
|
||||
|
||||
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
|
||||
@ -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. */
|
||||
fun <T> List<T>.indexOfOrThrow(item: T): Int {
|
||||
val i = indexOf(item)
|
||||
@ -142,6 +129,30 @@ fun <T> List<T>.indexOfOrThrow(item: T): Int {
|
||||
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)
|
||||
|
||||
/** 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()
|
||||
|
||||
object NullOutputStream : OutputStream() {
|
||||
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 }
|
||||
fun String.abbreviate(maxWidth: Int): String = if (length <= maxWidth) this else "${take(maxWidth - 1)}…"
|
||||
|
||||
/**
|
||||
* 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)
|
||||
|
||||
// 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> {
|
||||
return flatMap {
|
||||
@ -335,7 +337,10 @@ val <T : Any> Class<T>.kotlinObjectInstance: T? get() {
|
||||
field?.let {
|
||||
if (it.type == this && it.isPublic && it.isStatic && it.isFinal) {
|
||||
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 {
|
||||
null
|
||||
}
|
||||
@ -431,8 +436,6 @@ inline val Member.isStatic: Boolean get() = Modifier.isStatic(modifiers)
|
||||
|
||||
inline val Member.isFinal: Boolean get() = Modifier.isFinal(modifiers)
|
||||
|
||||
fun URI.toPath(): Path = Paths.get(this)
|
||||
|
||||
fun URL.toPath(): Path = toURI().toPath()
|
||||
|
||||
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()
|
||||
|
||||
/**
|
||||
* 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> {
|
||||
val payloadData: T = try {
|
||||
val serializer = SerializationDefaults.SERIALIZATION_FACTORY
|
||||
@ -560,6 +558,10 @@ fun <K, V> MutableMap<K, V>.toSynchronised(): MutableMap<K, V> = Collections.syn
|
||||
/** @see Collections.synchronizedSet */
|
||||
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.
|
||||
* 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 JDK8_CLASS_FILE_FORMAT_MAJOR_VERSION = 52
|
||||
const val JAVA_1_2_CLASS_FILE_FORMAT_MAJOR_VERSION = 46
|
||||
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.
|
||||
*/
|
||||
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
|
||||
* the name can be used to create a file name or a metric name.
|
||||
*/
|
||||
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" }
|
||||
}
|
||||
|
||||
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(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>
|
||||
}
|
||||
|
||||
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.serialization.deserialize
|
||||
import java.io.*
|
||||
import java.nio.charset.Charset
|
||||
import java.nio.charset.StandardCharsets.UTF_8
|
||||
import java.nio.file.*
|
||||
import java.io.IOException
|
||||
import java.io.InputStream
|
||||
import java.io.OutputStream
|
||||
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.FileAttribute
|
||||
import java.nio.file.attribute.FileTime
|
||||
import java.util.stream.Stream
|
||||
import kotlin.streams.toList
|
||||
|
||||
/**
|
||||
* Allows you to write code like: Paths.get("someDir") / "subdir" / "filename" but using the Paths API to avoid platform
|
||||
* separator problems.
|
||||
* @see Path.resolve
|
||||
*/
|
||||
operator fun Path.div(other: String): Path = resolve(other)
|
||||
|
||||
/**
|
||||
* 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)
|
||||
import kotlin.io.path.createDirectories
|
||||
import kotlin.io.path.deleteIfExists
|
||||
import kotlin.io.path.div
|
||||
import kotlin.io.path.exists
|
||||
import kotlin.io.path.inputStream
|
||||
import kotlin.io.path.isDirectory
|
||||
import kotlin.io.path.isSymbolicLink
|
||||
import kotlin.io.path.name
|
||||
import kotlin.io.path.outputStream
|
||||
import kotlin.io.path.readBytes
|
||||
import kotlin.io.path.readSymbolicLink
|
||||
|
||||
/** Copy the file into the target directory using [Files.copy]. */
|
||||
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
|
||||
* not include any path separator characters.
|
||||
*/
|
||||
val targetFile = targetDir.resolve(fileName.toString())
|
||||
val targetFile = targetDir / name
|
||||
Files.copy(this, targetFile, *options)
|
||||
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]. */
|
||||
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 */
|
||||
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. */
|
||||
fun Path.deleteRecursively() {
|
||||
if (!exists()) return
|
||||
Files.walkFileTree(this, object : SimpleFileVisitor<Path>() {
|
||||
override fun visitFile(file: Path, attrs: BasicFileAttributes): FileVisitResult {
|
||||
file.delete()
|
||||
file.deleteIfExists()
|
||||
return FileVisitResult.CONTINUE
|
||||
}
|
||||
override fun postVisitDirectory(dir: Path, exception: IOException?): FileVisitResult {
|
||||
dir.delete()
|
||||
dir.deleteIfExists()
|
||||
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].
|
||||
* @return the output of [block]
|
||||
@ -169,35 +81,14 @@ inline fun Path.write(createDirs: Boolean = false, vararg options: OpenOption =
|
||||
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].
|
||||
* @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. */
|
||||
inline val Path.hash: SecureHash get() = read { it.hash() }
|
||||
|
||||
/* Check if the Path is symbolic link */
|
||||
fun Path.safeSymbolicRead(): Path {
|
||||
if (Files.isSymbolicLink(this)) {
|
||||
return (Files.readSymbolicLink(this))
|
||||
} else {
|
||||
return (this)
|
||||
}
|
||||
}
|
||||
fun Path.safeSymbolicRead(): Path = if (isSymbolicLink()) readSymbolicLink() else this
|
||||
|
@ -6,19 +6,15 @@ import net.corda.core.crypto.TransactionSignature
|
||||
import net.corda.core.flows.TransactionMetadata
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
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.serialization.internal.AttachmentsClassLoaderCache
|
||||
import net.corda.core.transactions.SignedTransaction
|
||||
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.
|
||||
interface ServiceHubCoreInternal : ServiceHub {
|
||||
|
||||
interface ServiceHubCoreInternal : VerifyingServiceHub {
|
||||
val externalOperationExecutor: ExecutorService
|
||||
|
||||
val attachmentTrustCalculator: AttachmentTrustCalculator
|
||||
|
||||
/**
|
||||
* Optional `NotaryService` which will be `null` for all non-Notary nodes.
|
||||
*/
|
||||
@ -26,8 +22,6 @@ interface ServiceHubCoreInternal : ServiceHub {
|
||||
|
||||
fun createTransactionsResolver(flow: ResolveTransactionsFlow): TransactionsResolver
|
||||
|
||||
val attachmentsClassLoaderCache: AttachmentsClassLoaderCache
|
||||
|
||||
/**
|
||||
* Stores [SignedTransaction] and participant signatures without the notary signature in the local transaction storage,
|
||||
* inclusive of flow recovery metadata.
|
||||
|
@ -172,11 +172,13 @@ fun createComponentGroups(inputs: List<StateRef>,
|
||||
return componentGroupMap
|
||||
}
|
||||
|
||||
typealias SerializedTransactionState = SerializedBytes<TransactionState<ContractState>>
|
||||
|
||||
/**
|
||||
* A SerializedStateAndRef is a pair (BinaryStateRepresentation, StateRef).
|
||||
* 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(): StateAndRef<ContractState> {
|
||||
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.
|
||||
* 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 ->
|
||||
thenMatch(success@ {
|
||||
result.captureLater(try {
|
||||
@ -146,7 +145,6 @@ interface ValueOrException<in V> {
|
||||
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. */
|
||||
@Suppress("TooGenericExceptionCaught")
|
||||
fun capture(block: () -> V): Boolean {
|
||||
return set(try {
|
||||
block()
|
||||
@ -164,18 +162,17 @@ interface OpenFuture<V> : ValueOrException<V>, CordaFuture<V>
|
||||
|
||||
/** Unless you really want this particular implementation, use [openFuture] to make one. */
|
||||
@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 {
|
||||
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 setException(t: Throwable) = impl.completeExceptionally(t)
|
||||
override fun <W> then(callback: (CordaFuture<V>) -> W) = thenImpl(defaultLog, callback)
|
||||
/** For testing only. */
|
||||
@Suppress("TooGenericExceptionCaught")
|
||||
internal fun <W> thenImpl(log: Logger, callback: (CordaFuture<V>) -> W) {
|
||||
fun <W> thenImpl(log: Logger, callback: (CordaFuture<V>) -> W) {
|
||||
impl.whenComplete { _, _ ->
|
||||
try {
|
||||
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 java.net.URL
|
||||
import java.nio.file.Paths
|
||||
import kotlin.io.path.name
|
||||
|
||||
data class CordappImpl(
|
||||
override val contractClassNames: List<String>,
|
||||
@ -49,7 +50,7 @@ data class CordappImpl(
|
||||
}
|
||||
|
||||
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 */
|
||||
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
|
||||
|
||||
import co.paralleluniverse.fibers.instrument.DontInstrument
|
||||
import io.opentelemetry.api.GlobalOpenTelemetry
|
||||
import io.opentelemetry.api.OpenTelemetry
|
||||
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) {
|
||||
private var openTelemetryDriver: Any? = null
|
||||
val openTelemetry: OpenTelemetry by lazy {
|
||||
val openTelemetry: OpenTelemetry by lazy @DontInstrument {
|
||||
try {
|
||||
openTelemetryDriver = OpenTelemetryDriver(serviceName)
|
||||
(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 {
|
||||
val telemetryId = startSpan(name, attributes, flowLogic)
|
||||
try {
|
||||
@ -195,7 +194,7 @@ class TelemetryServiceImpl : SingletonSerializeAsToken(), TelemetryService {
|
||||
}
|
||||
|
||||
@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 {
|
||||
val telemetryId = startSpanForFlow(name, attributes, flowLogic, remoteSerializedTelemetry)
|
||||
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.ContractAttachment
|
||||
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.crypto.CompositeKey
|
||||
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.warnContractWithoutConstraintPropagation
|
||||
import net.corda.core.internal.warnOnce
|
||||
import net.corda.core.transactions.LedgerTransaction
|
||||
import net.corda.core.utilities.loggerFor
|
||||
import java.util.function.Function
|
||||
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 {
|
||||
|
||||
/**
|
||||
@ -142,10 +145,12 @@ private class Validator(private val ltx: LedgerTransaction, private val transact
|
||||
*/
|
||||
@Suppress("ThrowsCount")
|
||||
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.
|
||||
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.
|
||||
// And then maps them to the attachment.
|
||||
@ -393,7 +398,7 @@ private class Validator(private val ltx: LedgerTransaction, private val transact
|
||||
@Suppress("NestedBlockDepth", "MagicNumber")
|
||||
private fun verifyConstraints(contractAttachmentsByContract: Map<ContractClassName, ContractAttachment>) {
|
||||
// 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) {
|
||||
/**
|
||||
* 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
|
||||
* its contents, as well as executing all of its smart contracts.
|
||||
*/
|
||||
@Suppress("TooGenericExceptionCaught")
|
||||
class TransactionVerifier(private val transactionClassLoader: ClassLoader) : Function<Supplier<LedgerTransaction>, Unit> {
|
||||
// Loads the contract class from the transactionClassLoader.
|
||||
private fun createContractClass(id: SecureHash, contractClassName: ContractClassName): Class<out Contract> {
|
||||
return try {
|
||||
Class.forName(contractClassName, false, transactionClassLoader).asSubclass(Contract::class.java)
|
||||
loadClassOfType<Contract>(contractClassName, false, transactionClassLoader)
|
||||
} catch (e: Exception) {
|
||||
throw ContractCreationError(id, contractClassName, e)
|
||||
}
|
||||
@ -448,7 +452,7 @@ class TransactionVerifier(private val transactionClassLoader: ClassLoader) : Fun
|
||||
|
||||
private fun generateContracts(ltx: LedgerTransaction): List<Contract> {
|
||||
return (ltx.inputs.map(StateAndRef<ContractState>::state) + ltx.outputs)
|
||||
.mapTo(LinkedHashSet(), TransactionState<*>::contract)
|
||||
.mapToSet { it.contract }
|
||||
.map { contractClassName ->
|
||||
createContractClass(ltx.id, contractClassName)
|
||||
}.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.internal.PlatformVersionSwitches.TWO_PHASE_FINALITY
|
||||
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.diagnostics.DiagnosticsService
|
||||
import net.corda.core.serialization.CordaSerializable
|
||||
@ -81,7 +82,6 @@ interface ServicesForResolution {
|
||||
/**
|
||||
* Provides a callback for the Node to customise the [LedgerTransaction].
|
||||
*/
|
||||
@JvmDefault
|
||||
fun specialise(ltx: LedgerTransaction): LedgerTransaction = ltx
|
||||
}
|
||||
|
||||
@ -171,12 +171,6 @@ interface ServiceHub : ServicesForResolution {
|
||||
*/
|
||||
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
|
||||
* clock so the current time can be controlled during unit testing.
|
||||
@ -284,8 +278,7 @@ interface ServiceHub : ServicesForResolution {
|
||||
*/
|
||||
@Throws(TransactionResolutionException::class)
|
||||
fun <T : ContractState> toStateAndRef(stateRef: StateRef): StateAndRef<T> {
|
||||
val stx = validatedTransactions.getTransaction(stateRef.txhash) ?: throw TransactionResolutionException(stateRef.txhash)
|
||||
return stx.resolveBaseTransaction(this).outRef(stateRef.index)
|
||||
return StateAndRef(uncheckedCast(loadState(stateRef)), stateRef)
|
||||
}
|
||||
|
||||
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,
|
||||
* 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.
|
||||
* The following methods are blocked:
|
||||
* We want to make sure users have a restricted access to administrative functions, this function will return a [Connection] instance
|
||||
* with the following methods blocked:
|
||||
* - abort(executor: Executor?)
|
||||
* - clearWarnings()
|
||||
* - 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 {
|
||||
@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")
|
||||
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
|
||||
* a leaf in an Subscribers' tree structure.
|
||||
*/
|
||||
@Suppress("TooGenericExceptionCaught")
|
||||
override fun onNext(t: T) {
|
||||
try {
|
||||
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].
|
||||
*/
|
||||
@Suppress("TooGenericExceptionCaught")
|
||||
override fun _onError(e: Throwable) {
|
||||
@Suppress("DEPRECATION")
|
||||
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.PackageOwnershipException
|
||||
import net.corda.core.crypto.SecureHash
|
||||
import net.corda.core.internal.JDK1_2_CLASS_FILE_FORMAT_MAJOR_VERSION
|
||||
import net.corda.core.internal.JDK8_CLASS_FILE_FORMAT_MAJOR_VERSION
|
||||
import net.corda.core.internal.JAVA_17_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.NamedCacheFactory
|
||||
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
|
||||
factoryField.set(null, null)
|
||||
// Set our custom factory and wrap the current one into it
|
||||
URL.setURLStreamHandlerFactory(
|
||||
// Set the factory to a decorator
|
||||
object : URLStreamHandlerFactory {
|
||||
URL.setURLStreamHandlerFactory { protocol ->
|
||||
// route between our own and the pre-existing factory
|
||||
override fun createURLStreamHandler(protocol: String): URLStreamHandler? {
|
||||
return AttachmentURLStreamHandlerFactory.createURLStreamHandler(protocol)
|
||||
AttachmentURLStreamHandlerFactory.createURLStreamHandler(protocol)
|
||||
?: existingFactory.createURLStreamHandler(protocol)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -158,9 +153,7 @@ class AttachmentsClassLoader(attachments: List<Attachment>,
|
||||
checkAttachments(attachments)
|
||||
}
|
||||
|
||||
private class AttachmentHashContext(
|
||||
val txId: SecureHash,
|
||||
val buffer: ByteArray = ByteArray(DEFAULT_BUFFER_SIZE))
|
||||
private class AttachmentHashContext(val buffer: ByteArray = ByteArray(DEFAULT_BUFFER_SIZE))
|
||||
|
||||
private fun hash(inputStream : InputStream, ctx : AttachmentHashContext) : SecureHash.SHA256 {
|
||||
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.
|
||||
// TODO - investigate potential exploits.
|
||||
private fun shouldCheckForNoOverlap(path: String, targetPlatformVersion: Int): Boolean {
|
||||
require(path.toLowerCase() == path)
|
||||
require(path.lowercase() == path)
|
||||
require(!path.contains('\\'))
|
||||
|
||||
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.
|
||||
|
||||
val classLoaderEntries = mutableMapOf<String, SecureHash>()
|
||||
val ctx = AttachmentHashContext(sampleTxId)
|
||||
val ctx = AttachmentHashContext()
|
||||
for (attachment in attachments) {
|
||||
// We may have been given an attachment loaded from the database in which case, important info like
|
||||
// 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.
|
||||
//
|
||||
// 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.
|
||||
if (path.endsWith(".class")) {
|
||||
@ -285,7 +278,7 @@ class AttachmentsClassLoader(attachments: List<Attachment>,
|
||||
for ((namespace, pubkey) in params.packageOwnership) {
|
||||
// Note that due to the toLowerCase() call above, we'll be comparing against a lowercased
|
||||
// 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
|
||||
if (pkgName == ns || pkgName.startsWith("$ns.")) {
|
||||
if (pubkey !in signers)
|
||||
@ -358,12 +351,12 @@ object AttachmentsClassLoaderBuilder {
|
||||
val attachmentIds = attachments.mapTo(LinkedHashSet(), Attachment::id)
|
||||
|
||||
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
|
||||
val transactionClassLoader = AttachmentsClassLoader(attachments, key.params, txId, isAttachmentTrusted, parent)
|
||||
val serializers = try {
|
||||
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) {
|
||||
throw TransactionVerificationException.UnsupportedClassVersionError(txId, ex.message!!, ex)
|
||||
}
|
||||
@ -380,9 +373,9 @@ object AttachmentsClassLoaderBuilder {
|
||||
.withWhitelist(whitelistedClasses)
|
||||
.withCustomSerializers(serializers)
|
||||
.withoutCarpenter()
|
||||
})
|
||||
}
|
||||
|
||||
val serializationContext = cachedSerializationContext.withProperties(mapOf<Any, Any>(
|
||||
val serializationContext = cachedSerializationContext.withProperties(mapOf(
|
||||
// Duplicate the SerializationContext from the cache and give
|
||||
// it these extra properties, just for this transaction.
|
||||
// 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 expiryQueue = ReferenceQueue<SerializationContext>()
|
||||
|
||||
@Suppress("TooGenericExceptionCaught")
|
||||
private fun purgeExpiryQueue() {
|
||||
// Close the AttachmentsClassLoader for every SerializationContext
|
||||
// that has already been garbage-collected.
|
||||
|
@ -1,28 +1,44 @@
|
||||
package net.corda.core.transactions
|
||||
|
||||
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.SecureHash
|
||||
import net.corda.core.crypto.TransactionSignature
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.core.internal.AttachmentWithContext
|
||||
import net.corda.core.internal.ServiceHubCoreInternal
|
||||
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.ServicesForResolution
|
||||
import net.corda.core.serialization.CordaSerializable
|
||||
import net.corda.core.serialization.DeprecatedConstructorForDeserialization
|
||||
import net.corda.core.serialization.SerializedBytes
|
||||
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.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.Component.*
|
||||
import net.corda.core.transactions.WireTransaction.Companion.resolveStateRefBinaryComponent
|
||||
import net.corda.core.transactions.ContractUpgradeWireTransaction.Component.INPUTS
|
||||
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.toBase58String
|
||||
import java.security.PublicKey
|
||||
@ -52,7 +68,10 @@ data class ContractUpgradeWireTransaction(
|
||||
* Runs the explicit upgrade logic.
|
||||
*/
|
||||
@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
|
||||
val upgradedState: S = upgradedContract.upgrade(state.data)
|
||||
val inputConstraint = state.constraint
|
||||
@ -121,60 +140,12 @@ data class ContractUpgradeWireTransaction(
|
||||
|
||||
/** Resolves input states and contract attachments, and builds a ContractUpgradeLedgerTransaction. */
|
||||
fun resolve(services: ServicesForResolution, sigs: List<TransactionSignature>): ContractUpgradeLedgerTransaction {
|
||||
val resolvedInputs = services.loadStates(inputs.toSet()).toList()
|
||||
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()
|
||||
}
|
||||
return ContractUpgradeLedgerTransaction.resolve(services.toVerifyingServiceHub(), this, sigs)
|
||||
}
|
||||
|
||||
/** Constructs a filtered transaction: the inputs, the notary party and network parameters hash are always visible, while the rest are hidden. */
|
||||
fun buildFilteredTransaction(): ContractUpgradeFilteredTransaction {
|
||||
val totalComponents = (0 until serializedComponents.size).toSet()
|
||||
val totalComponents = serializedComponents.indices.toSet()
|
||||
val visibleComponents = mapOf(
|
||||
INPUTS.ordinal to FilteredComponent(serializedComponents[INPUTS.ordinal], nonces[INPUTS.ordinal]),
|
||||
NOTARY.ordinal to FilteredComponent(serializedComponents[NOTARY.ordinal], nonces[NOTARY.ordinal]),
|
||||
@ -287,39 +258,46 @@ private constructor(
|
||||
get() = upgradedContract::class.java.name
|
||||
|
||||
companion object {
|
||||
|
||||
@CordaInternal
|
||||
internal fun create(
|
||||
inputs: List<StateAndRef<ContractState>>,
|
||||
notary: Party,
|
||||
legacyContractAttachment: Attachment,
|
||||
upgradedContractAttachment: Attachment,
|
||||
id: SecureHash,
|
||||
privacySalt: PrivacySalt,
|
||||
sigs: List<TransactionSignature>,
|
||||
networkParameters: NetworkParameters,
|
||||
upgradedContract: UpgradedContract<ContractState, *>
|
||||
): ContractUpgradeLedgerTransaction {
|
||||
return ContractUpgradeLedgerTransaction(inputs, notary, legacyContractAttachment, upgradedContractAttachment, id, privacySalt, sigs, networkParameters, upgradedContract)
|
||||
@JvmSynthetic
|
||||
@Suppress("ThrowsCount")
|
||||
fun resolve(verificationSupport: VerificationSupport,
|
||||
wtx: ContractUpgradeWireTransaction,
|
||||
sigs: List<TransactionSignature>): ContractUpgradeLedgerTransaction {
|
||||
val inputs = wtx.inputs.map(verificationSupport::getStateAndRef)
|
||||
val (legacyContractAttachment, upgradedContractAttachment) = verificationSupport.getAttachments(listOf(
|
||||
wtx.legacyContractAttachmentId,
|
||||
wtx.upgradedContractAttachmentId
|
||||
))
|
||||
val networkParameters = verificationSupport.getNetworkParameters(wtx.networkParametersHash)
|
||||
?: 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
|
||||
internal fun loadUpgradedContract(upgradedContractClassName: ContractClassName, classLoader: ClassLoader): UpgradedContract<ContractState, *> {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
return Class.forName(upgradedContractClassName, false, classLoader)
|
||||
.asSubclass(Contract::class.java)
|
||||
.getConstructor()
|
||||
.newInstance() as UpgradedContract<ContractState, *>
|
||||
@JvmSynthetic
|
||||
internal fun loadUpgradedContract(className: ContractClassName, id: SecureHash, classLoader: ClassLoader): UpgradedContract<ContractState, *> {
|
||||
return try {
|
||||
loadClassOfType<UpgradedContract<ContractState, *>>(className, false, classLoader)
|
||||
.getDeclaredConstructor()
|
||||
.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. */
|
||||
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> {
|
||||
return keys.map { it.toBase58String() }
|
||||
@ -401,7 +379,7 @@ private constructor(
|
||||
privacySalt: PrivacySalt,
|
||||
sigs: List<TransactionSignature>,
|
||||
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.")
|
||||
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