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:
Jose Coll 2023-12-19 08:46:52 +00:00
commit b47d5ec5c7
544 changed files with 9602 additions and 8232 deletions

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

@ -45,6 +45,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 {

View File

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

View File

@ -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 {

View File

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

View File

@ -70,6 +70,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
View File

@ -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') {

View File

@ -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"
}
}

View 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')
}
}
}

View File

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

View File

@ -1,25 +1,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
}
}
}

View File

@ -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))
}
}

View File

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

View File

@ -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
}
}
}

View File

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

View File

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

View File

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

View File

@ -1,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
}
}
}

View File

@ -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
}

View File

@ -1,12 +1,12 @@
package net.corda.client.rpc
import com.nhaarman.mockito_kotlin.any
import com.nhaarman.mockito_kotlin.atLeastOnce
import com.nhaarman.mockito_kotlin.mock
import com.nhaarman.mockito_kotlin.never
import com.nhaarman.mockito_kotlin.times
import com.nhaarman.mockito_kotlin.verify
import com.nhaarman.mockito_kotlin.whenever
import org.mockito.kotlin.any
import org.mockito.kotlin.atLeastOnce
import org.mockito.kotlin.mock
import org.mockito.kotlin.never
import org.mockito.kotlin.times
import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever
import net.corda.client.rpc.internal.RPCClient
import net.corda.client.rpc.ext.RPCConnectionListener
import net.corda.core.messaging.RPCOps

View File

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

View File

@ -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 {

View File

@ -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()

View File

@ -150,7 +150,6 @@ internal class RPCClientProxyHandler(
}
}
@Suppress("TooGenericExceptionCaught")
private fun closeObservable(observable: UnicastSubject<Notification<*>>) {
// Notify listeners of the observables that the connection is being terminated.
try {
@ -300,7 +299,7 @@ internal class RPCClientProxyHandler(
class FailoverHandler(private val detected: () -> Unit = {},
private val completed: () -> Unit = {},
private val failed: () -> Unit = {}): FailoverEventListener {
override fun failoverEvent(eventType: FailoverEventType?) {
override fun failoverEvent(eventType: FailoverEventType) {
when (eventType) {
FailoverEventType.FAILURE_DETECTED -> { detected() }
FailoverEventType.FAILOVER_COMPLETED -> { completed() }
@ -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) {

View File

@ -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

View File

@ -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
}
}
}

View File

@ -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
}
}
}

View File

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

View File

@ -1,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
}
}
}

View File

@ -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
}

View File

@ -2,64 +2,26 @@
<Configuration status="info" shutdownHook="disable">
<Properties>
<Property name="log-path">${sys:log-path:-logs}</Property>
<Property name="log-name">node-${hostName}</Property>
<Property name="diagnostic-log-name">diagnostic-${hostName}</Property>
<Property name="archive">${log-path}/archive</Property>
<Property name="defaultLogLevel">${sys:defaultLogLevel:-info}</Property>
<Property name="consoleLogLevel">${sys:consoleLogLevel:-error}</Property>
<Property name="log_path">${sys:log-path:-logs}</Property>
<Property name="log_name">node-${hostName}</Property>
<Property name="diagnostic_log_name">diagnostic-${hostName}</Property>
<Property name="archive">${log_path}/archive</Property>
<Property name="default_log_level">${sys:defaultLogLevel:-info}</Property>
<Property name="console_log_level">${sys:consoleLogLevel:-error}</Property>
</Properties>
<Appenders>
<ScriptAppenderSelector name="Console-Selector">
<Script language="nashorn"><![CDATA[
var System = Java.type('java.lang.System');
var level = System.getProperty("consoleLogLevel");
var enabled = System.getProperty("consoleLoggingEnabled");
enabled == "true" && (level == "debug" || level == "trace") ? "Console-Debug-Appender" : "Console-Appender";
]]></Script>
<AppenderSet>
<!-- The default console appender - prints no exception information -->
<Console name="Console-Appender" target="SYSTEM_OUT">
<PatternLayout>
<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">

View File

@ -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

View File

@ -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

View File

@ -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 ->

View File

@ -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 {

View File

@ -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())

View File

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

View File

@ -1,8 +1,8 @@
package net.corda.coretests.contracts
import com.nhaarman.mockito_kotlin.doReturn
import com.nhaarman.mockito_kotlin.mock
import com.nhaarman.mockito_kotlin.whenever
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.mock
import org.mockito.kotlin.whenever
import net.corda.core.contracts.*
import net.corda.core.crypto.Crypto
import net.corda.core.crypto.SecureHash

View File

@ -3,7 +3,6 @@ package net.corda.coretests.crypto
import net.corda.core.crypto.*
import net.corda.core.crypto.CompositeKey.NodeAndWeight
import net.corda.core.internal.declaredField
import net.corda.core.internal.div
import net.corda.core.serialization.serialize
import net.corda.core.utilities.OpaqueBytes
import net.corda.core.utilities.toBase58String
@ -19,6 +18,7 @@ import org.junit.Test
import org.junit.rules.TemporaryFolder
import java.security.PublicKey
import javax.security.auth.x500.X500Principal
import kotlin.io.path.div
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith
import kotlin.test.assertFalse

View File

@ -1,11 +1,19 @@
package net.corda.coretests.crypto
import com.nhaarman.mockito_kotlin.doReturn
import com.nhaarman.mockito_kotlin.mock
import com.nhaarman.mockito_kotlin.whenever
import net.corda.core.contracts.*
import net.corda.core.crypto.*
import net.corda.core.contracts.Command
import net.corda.core.contracts.PrivacySalt
import net.corda.core.contracts.StateRef
import net.corda.core.contracts.TimeWindow
import net.corda.core.contracts.TransactionState
import net.corda.core.crypto.DigestService
import net.corda.core.crypto.MerkleTree
import net.corda.core.crypto.MerkleTreeException
import net.corda.core.crypto.PartialMerkleTree
import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.internal.DigestAlgorithmFactory
import net.corda.core.crypto.keys
import net.corda.core.crypto.randomHash
import net.corda.core.crypto.sha256
import net.corda.core.identity.CordaX500Name
import net.corda.core.identity.Party
import net.corda.core.internal.BLAKE2s256DigestAlgorithm
@ -16,9 +24,10 @@ import net.corda.core.serialization.deserialize
import net.corda.core.serialization.serialize
import net.corda.core.transactions.ReferenceStateRef
import net.corda.core.transactions.WireTransaction
import net.corda.coretesting.internal.TEST_TX_TIME
import net.corda.finance.DOLLARS
import net.corda.finance.`issued by`
import net.corda.finance.contracts.asset.Cash
import net.corda.finance.`issued by`
import net.corda.testing.common.internal.testNetworkParameters
import net.corda.testing.core.DUMMY_NOTARY_NAME
import net.corda.testing.core.SerializationEnvironmentRule
@ -26,20 +35,29 @@ import net.corda.testing.core.TestIdentity
import net.corda.testing.dsl.LedgerDSL
import net.corda.testing.dsl.TestLedgerDSLInterpreter
import net.corda.testing.dsl.TestTransactionDSLInterpreter
import net.corda.coretesting.internal.TEST_TX_TIME
import net.corda.testing.internal.createWireTransaction
import net.corda.testing.node.MockServices
import net.corda.testing.node.ledger
import org.assertj.core.api.Assertions.assertThatIllegalArgumentException
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.Parameterized
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.mock
import org.mockito.kotlin.whenever
import java.security.PublicKey
import java.util.function.Predicate
import java.util.stream.IntStream
import kotlin.streams.toList
import kotlin.test.*
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith
import kotlin.test.assertFalse
import kotlin.test.assertNotEquals
import kotlin.test.assertNotNull
import kotlin.test.assertNull
import kotlin.test.assertTrue
@RunWith(Parameterized::class)
class PartialMerkleTreeTest(private var digestService: DigestService) {
@ -204,7 +222,7 @@ class PartialMerkleTreeTest(private var digestService: DigestService) {
@Test(timeout=300_000)
fun `nothing filtered`() {
val ftxNothing = testTx.buildFilteredTransaction(Predicate { false })
val ftxNothing = testTx.buildFilteredTransaction { false }
assertTrue(ftxNothing.componentGroups.isEmpty())
assertTrue(ftxNothing.attachments.isEmpty())
assertTrue(ftxNothing.commands.isEmpty())
@ -291,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")) }

View File

@ -1,17 +1,14 @@
package net.corda.coretests.crypto
import com.nhaarman.mockito_kotlin.doReturn
import com.nhaarman.mockito_kotlin.mock
import com.nhaarman.mockito_kotlin.whenever
import net.corda.core.contracts.Command
import net.corda.core.contracts.PrivacySalt
import net.corda.core.contracts.StateRef
import net.corda.core.contracts.TimeWindow
import net.corda.core.contracts.TransactionState
import net.corda.core.crypto.DigestService
import net.corda.core.crypto.MerkleTree
import net.corda.core.crypto.MerkleTreeException
import net.corda.core.crypto.PartialMerkleTree
import net.corda.core.crypto.DigestService
import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.SecureHash.Companion.SHA2_384
import net.corda.core.crypto.SecureHash.Companion.hashAs
@ -26,9 +23,10 @@ import net.corda.core.serialization.serialize
import net.corda.core.transactions.ReferenceStateRef
import net.corda.core.transactions.WireTransaction
import net.corda.core.utilities.OpaqueBytes
import net.corda.coretesting.internal.TEST_TX_TIME
import net.corda.finance.DOLLARS
import net.corda.finance.`issued by`
import net.corda.finance.contracts.asset.Cash
import net.corda.finance.`issued by`
import net.corda.testing.common.internal.testNetworkParameters
import net.corda.testing.core.DUMMY_NOTARY_NAME
import net.corda.testing.core.SerializationEnvironmentRule
@ -36,10 +34,10 @@ import net.corda.testing.core.TestIdentity
import net.corda.testing.dsl.LedgerDSL
import net.corda.testing.dsl.TestLedgerDSLInterpreter
import net.corda.testing.dsl.TestTransactionDSLInterpreter
import net.corda.coretesting.internal.TEST_TX_TIME
import net.corda.testing.internal.createWireTransaction
import net.corda.testing.node.MockServices
import net.corda.testing.node.ledger
import org.assertj.core.api.Assertions.assertThatIllegalArgumentException
import org.junit.Assert.assertFalse
import org.junit.Assert.assertNotEquals
import org.junit.Assert.assertNotNull
@ -49,6 +47,9 @@ import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.jupiter.api.Assertions.assertEquals
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.mock
import org.mockito.kotlin.whenever
import java.security.PublicKey
import java.util.function.Predicate
import java.util.stream.IntStream
@ -209,7 +210,7 @@ class PartialMerkleTreeWithNamedHashMultiAlgTreeTest {
@Test(timeout=300_000)
fun `nothing filtered`() {
val ftxNothing = testTx.buildFilteredTransaction(Predicate { false })
val ftxNothing = testTx.buildFilteredTransaction { false }
assertTrue(ftxNothing.componentGroups.isEmpty())
assertTrue(ftxNothing.attachments.isEmpty())
assertTrue(ftxNothing.commands.isEmpty())
@ -296,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,

View File

@ -1,17 +1,14 @@
package net.corda.coretests.crypto
import com.nhaarman.mockito_kotlin.doReturn
import com.nhaarman.mockito_kotlin.mock
import com.nhaarman.mockito_kotlin.whenever
import net.corda.core.contracts.Command
import net.corda.core.contracts.PrivacySalt
import net.corda.core.contracts.StateRef
import net.corda.core.contracts.TimeWindow
import net.corda.core.contracts.TransactionState
import net.corda.core.crypto.DigestService
import net.corda.core.crypto.MerkleTree
import net.corda.core.crypto.MerkleTreeException
import net.corda.core.crypto.PartialMerkleTree
import net.corda.core.crypto.DigestService
import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.SecureHash.Companion.SHA2_384
import net.corda.core.crypto.SecureHash.Companion.hashAs
@ -26,9 +23,10 @@ import net.corda.core.serialization.serialize
import net.corda.core.transactions.ReferenceStateRef
import net.corda.core.transactions.WireTransaction
import net.corda.core.utilities.OpaqueBytes
import net.corda.coretesting.internal.TEST_TX_TIME
import net.corda.finance.DOLLARS
import net.corda.finance.`issued by`
import net.corda.finance.contracts.asset.Cash
import net.corda.finance.`issued by`
import net.corda.testing.common.internal.testNetworkParameters
import net.corda.testing.core.DUMMY_NOTARY_NAME
import net.corda.testing.core.SerializationEnvironmentRule
@ -36,10 +34,10 @@ import net.corda.testing.core.TestIdentity
import net.corda.testing.dsl.LedgerDSL
import net.corda.testing.dsl.TestLedgerDSLInterpreter
import net.corda.testing.dsl.TestTransactionDSLInterpreter
import net.corda.coretesting.internal.TEST_TX_TIME
import net.corda.testing.internal.createWireTransaction
import net.corda.testing.node.MockServices
import net.corda.testing.node.ledger
import org.assertj.core.api.Assertions.assertThatIllegalArgumentException
import org.junit.Assert.assertFalse
import org.junit.Assert.assertNotEquals
import org.junit.Assert.assertNotNull
@ -49,6 +47,9 @@ import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.jupiter.api.Assertions.assertEquals
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.mock
import org.mockito.kotlin.whenever
import java.security.PublicKey
import java.util.function.Predicate
import java.util.stream.IntStream
@ -209,7 +210,7 @@ class PartialMerkleTreeWithNamedHashTest {
@Test(timeout=300_000)
fun `nothing filtered`() {
val ftxNothing = testTx.buildFilteredTransaction(Predicate { false })
val ftxNothing = testTx.buildFilteredTransaction { false }
assertTrue(ftxNothing.componentGroups.isEmpty())
assertTrue(ftxNothing.attachments.isEmpty())
assertTrue(ftxNothing.commands.isEmpty())
@ -296,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,

View File

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

View File

@ -1,7 +1,17 @@
package net.corda.coretests.crypto
import net.corda.core.crypto.*
import net.corda.core.crypto.Crypto
import net.corda.core.crypto.MerkleTree
import net.corda.core.crypto.MerkleTreeException
import net.corda.core.crypto.PartialMerkleTree
import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.SignableData
import net.corda.core.crypto.SignatureMetadata
import net.corda.core.crypto.TransactionSignature
import net.corda.core.crypto.sha256
import net.corda.core.crypto.sign
import net.corda.testing.core.SerializationEnvironmentRule
import org.assertj.core.api.Assertions.assertThatExceptionOfType
import org.junit.Rule
import org.junit.Test
import java.math.BigInteger
@ -39,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`() {

View File

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

View File

@ -23,22 +23,22 @@ import net.corda.core.identity.groupAbstractPartyByWellKnownParty
import net.corda.core.transactions.SignedTransaction
import net.corda.core.transactions.TransactionBuilder
import net.corda.core.utilities.getOrThrow
import net.corda.coretesting.internal.matchers.flow.willReturn
import net.corda.coretesting.internal.matchers.flow.willThrow
import net.corda.testing.contracts.DummyContract
import net.corda.testing.core.ALICE_NAME
import net.corda.testing.core.BOB_NAME
import net.corda.testing.core.CHARLIE_NAME
import net.corda.testing.core.TestIdentity
import net.corda.testing.core.singleIdentity
import net.corda.coretesting.internal.matchers.flow.willReturn
import net.corda.coretesting.internal.matchers.flow.willThrow
import net.corda.testing.node.MockServices
import net.corda.testing.node.internal.DUMMY_CONTRACTS_CORDAPP
import net.corda.testing.node.internal.InternalMockNetwork
import net.corda.testing.node.internal.TestStartedNode
import net.corda.testing.node.internal.enclosedCordapp
import org.hamcrest.CoreMatchers.`is`
import org.assertj.core.api.Assertions.assertThat
import org.assertj.core.api.Assertions.assertThatIllegalArgumentException
import org.junit.AfterClass
import org.junit.Assert
import org.junit.Test
import java.security.PublicKey
@ -92,7 +92,7 @@ class CollectSignaturesFlowTests : WithContracts {
mockNet.runNetwork()
val stx = future.get()
val missingSigners = stx.getMissingSigners()
Assert.assertThat(missingSigners, `is`(emptySet()))
assertThat(missingSigners).isEmpty()
}
@Test(timeout=300_000)
@ -122,10 +122,10 @@ class CollectSignaturesFlowTests : WithContracts {
mockNet.runNetwork()
val stx = future.get()
val missingSigners = stx.getMissingSigners()
Assert.assertThat(missingSigners, `is`(emptySet()))
assertThat(missingSigners).isEmpty()
}
@Test(expected = IllegalArgumentException::class, timeout=300_000)
@Test(timeout=300_000)
fun `throws exception when extra sessions are initiated`() {
bobNode.registerInitiatedFlow(ExtraSessionsFlowResponder::class.java)
charlieNode.registerInitiatedFlow(ExtraSessionsFlowResponder::class.java)
@ -137,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 })

View File

@ -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>()

View File

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

View File

@ -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) { _, _ ->

View File

@ -21,12 +21,15 @@ import net.corda.testing.driver.DriverParameters
import net.corda.testing.driver.driver
import net.corda.testing.node.internal.cordappsForPackages
import org.junit.Test
import java.io.Serializable
import java.sql.SQLTransientConnectionException
import kotlin.test.assertFailsWith
import kotlin.test.assertTrue
class FlowExternalOperationTest : AbstractFlowExternalOperationTest() {
private fun interface SerializableLambda2<S, T, R> : (S, T) -> R, Serializable
@Test(timeout = 300_000)
fun `external operation`() {
driver(DriverParameters(notarySpecs = emptyList(), startNodesInProcess = true)) {
@ -254,7 +257,7 @@ class FlowExternalOperationTest : AbstractFlowExternalOperationTest() {
@Suspendable
override fun testCode() {
val e = createException()
await(ExternalOperation(serviceHub) { _, _ -> throw e })
await(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 {

View File

@ -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)

View File

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

View File

@ -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) }
}
}

View File

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

View File

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

View File

@ -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

View File

@ -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()

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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
}
}
}

View File

@ -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) })
}
}
}

View File

@ -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) }
/**

View File

@ -27,6 +27,7 @@ class ContractAttachment private constructor(
companion object {
@CordaInternal
@JvmSynthetic
fun create(attachment: Attachment,
contract: ContractClassName,
additionalContracts: Set<ContractClassName> = emptySet(),

View File

@ -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() {

View File

@ -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 {

View File

@ -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)
}

View File

@ -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 {

View File

@ -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>> {

View File

@ -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) {

View File

@ -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)

View File

@ -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
}
}

View File

@ -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)

View File

@ -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>
}

View File

@ -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

View File

@ -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_.]*\$")

View File

@ -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

View File

@ -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.

View File

@ -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

View File

@ -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)

View File

@ -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"

View File

@ -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?
}

View File

@ -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

View File

@ -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 {

View File

@ -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
}
}

View File

@ -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)
}
}

View File

@ -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)
}
}

View File

@ -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 ->

View File

@ -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")
}
}

View File

@ -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()

View File

@ -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<*>
}

View File

@ -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())
}
}

View File

@ -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)

View File

@ -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.

View File

@ -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