From 406f7ff292947107237639817ac72555ef32e844 Mon Sep 17 00:00:00 2001 From: Shams Asari Date: Tue, 2 Jan 2024 17:02:20 +0000 Subject: [PATCH] ENT-11056: Compile the external verifier using Kotlin 1.2 (#7622) This requires Kotlin 1.2 versions of core and serialization (core-1.2 and serialization-1.2 respectively), which are just "shell" modules and which compile the existing source code with Kotlin 1.2. The 1.2 plugin does not work with the current version of Gradle and so the 1.2 compiler has to be called directly. Now with two versions of Kotlin in the code base, each module needs to have its version manually specified to ensure a clean separation. Otherwise, the default Kotlin version can override 1.2 when needed. Some of the code was tidied-up or improved to enable it to be cross-compiled. For post-1.2 APIs being used, they have been copied into core-1.2 with the same method signatures. OpenTelemetryComponent was moved to node-api, along with the dependency, to avoid also having a 1.2 version for the opentelemetry module. --- build.gradle | 46 ++++--- buildSrc/build.gradle | 5 +- .../src/main/groovy/corda.kotlin-1.2.gradle | 91 +++++++++++++ client/jackson/build.gradle | 1 - .../client/rpc/internal/RPCClientTelemetry.kt | 11 +- confidential-identities/build.gradle | 1 + constants.properties | 1 + core-1.2/README.md | 4 + core-1.2/build.gradle | 34 +++++ .../collections/KotlinCollections1.2.kt | 37 ++++++ .../kotlin/kotlin/io/path/KotlinIoPath1.2.kt | 37 ++++++ .../main/kotlin/kotlin/text/KotlinText1.2.kt | 10 ++ core/build.gradle | 55 +++----- .../net/corda/core/internal/InternalUtils.kt | 26 ++-- .../core/internal/JarSignatureCollector.kt | 6 +- .../internal/verification/AttachmentFixups.kt | 2 +- .../verification/VerificationService.kt | 13 +- .../verification/VerifyingServiceHub.kt | 3 +- .../net/corda/core/schemas/PersistentTypes.kt | 3 +- .../internal/AttachmentsClassLoader.kt | 32 ++--- .../core/transactions/LedgerTransaction.kt | 24 ++-- .../core/transactions/TransactionBuilder.kt | 3 +- .../corda/core/utilities/ProgressTracker.kt | 49 ++++--- experimental/netparams/build.gradle | 3 +- experimental/nodeinfo/build.gradle | 3 +- node-api/build.gradle | 5 +- .../telemetry/OpenTelemetryComponent.kt | 28 ++-- node/build.gradle | 3 +- .../verification/ExternalVerificationTest.kt | 123 ++++++++++++++---- .../net/corda/node/internal/AbstractNode.kt | 2 +- .../net/corda/node/internal/NodeStartup.kt | 2 +- samples/bank-of-corda-demo/build.gradle | 18 +-- .../contracts-states/build.gradle | 26 ++-- .../simm-valuation-demo/flows/build.gradle | 1 + serialization-1.2/README.md | 5 + serialization-1.2/build.gradle | 35 +++++ serialization/build.gradle | 21 +-- .../verifier/ExternalVerifierTypes.kt | 34 ++--- settings.gradle | 2 + testing/test-cli/build.gradle | 1 - testing/test-common/build.gradle | 1 + testing/testserver/build.gradle | 3 +- tools/blobinspector/build.gradle | 1 - tools/cliutils/build.gradle | 1 - tools/demobench/build.gradle | 11 +- tools/network-builder/build.gradle | 1 - verifier/README.md | 6 + verifier/build.gradle | 8 +- .../net/corda/verifier/ExternalVerifier.kt | 4 +- 49 files changed, 585 insertions(+), 257 deletions(-) create mode 100644 buildSrc/src/main/groovy/corda.kotlin-1.2.gradle create mode 100644 core-1.2/README.md create mode 100644 core-1.2/build.gradle create mode 100644 core-1.2/src/main/kotlin/kotlin/collections/KotlinCollections1.2.kt create mode 100644 core-1.2/src/main/kotlin/kotlin/io/path/KotlinIoPath1.2.kt create mode 100644 core-1.2/src/main/kotlin/kotlin/text/KotlinText1.2.kt rename {core/src/main/kotlin/net/corda/core => node-api/src/main/kotlin/net/corda/nodeapi}/internal/telemetry/OpenTelemetryComponent.kt (95%) create mode 100644 serialization-1.2/README.md create mode 100644 serialization-1.2/build.gradle create mode 100644 verifier/README.md diff --git a/build.gradle b/build.gradle index c615101f0a..cfb95addf7 100644 --- a/build.gradle +++ b/build.gradle @@ -245,7 +245,7 @@ logger.lifecycle("Building Corda version: {}", corda_release_version) logger.lifecycle("User home: {}", System.getProperty('user.home')) allprojects { - apply plugin: 'org.jetbrains.kotlin.jvm' + apply plugin: 'java' apply plugin: 'kotlin-allopen' apply plugin: 'jacoco' apply plugin: 'org.owasp.dependencycheck' @@ -437,10 +437,12 @@ allprojects { } configurations { - all { + configureEach { resolutionStrategy { - // Force dependencies to use the same version of Kotlin as Corda. - force "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version" + if (pluginManager.hasPlugin("org.jetbrains.kotlin.jvm")) { + // Force dependencies to use the same version of Kotlin as Corda. + force "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version" + } // Force dependencies to use the same version of Guava as Corda. force "com.google.guava:guava:$guava_version" @@ -487,24 +489,26 @@ allprojects { // Effectively delete this unused and unwanted transitive dependency of Artemis. substitute module('org.jgroups:jgroups') with module("org.apache.activemq:artemis-commons:$artemis_version") } - } - } - - // Select all of the compileClasspath and runtimeClasspath etc configurations, - // but NOT the "classpath" configuration, as that is used by the Gradle plugins. - matching { it.name.endsWith("Classpath") }.configureEach { cfg -> - cfg.resolutionStrategy { - dependencySubstitution { - // Force dependencies to use the same version of Kotlin as Corda. - substitute module('org.jetbrains.kotlin:kotlin-stdlib-common') with module("org.jetbrains.kotlin:kotlin-stdlib-common:$kotlin_version") - substitute module('org.jetbrains.kotlin:kotlin-stdlib') with module("org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version") - substitute module('org.jetbrains.kotlin:kotlin-reflect') with module("org.jetbrains.kotlin:kotlin-reflect:$kotlin_version") - } // FORCE Gradle to use latest SNAPSHOT dependencies. cacheChangingModulesFor 0, 'seconds' } } + + if (pluginManager.hasPlugin("org.jetbrains.kotlin.jvm")) { + // Select all of the compileClasspath and runtimeClasspath etc configurations, + // but NOT the "classpath" configuration, as that is used by the Gradle plugins. + matching { it.name.endsWith("Classpath") }.configureEach { cfg -> + cfg.resolutionStrategy { + dependencySubstitution { + // Force dependencies to use the same version of Kotlin as Corda. + substitute module('org.jetbrains.kotlin:kotlin-stdlib-common') with module("org.jetbrains.kotlin:kotlin-stdlib-common:$kotlin_version") + substitute module('org.jetbrains.kotlin:kotlin-stdlib') with module("org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version") + substitute module('org.jetbrains.kotlin:kotlin-reflect') with module("org.jetbrains.kotlin:kotlin-reflect:$kotlin_version") + } + } + } + } } } @@ -557,9 +561,9 @@ tasks.register('jacocoRootReport', JacocoReport) { // classDirectories = files(subprojects.sourceSets.main.output) // executionData = files(subprojects.jacocoTestReport.executionData) reports { - html.enabled = true - xml.enabled = true - csv.enabled = false + html.required = true + xml.required = true + csv.required = false } onlyIf = { true @@ -595,7 +599,7 @@ tasks.register('detektBaseline', JavaExec) { } tasks.withType(Test).configureEach { - reports.html.destination = file("${reporting.baseDir}/${name}") + reports.html.outputLocation.set(file("${reporting.baseDir}/${name}")) } tasks.register('testReport', TestReport) { diff --git a/buildSrc/build.gradle b/buildSrc/build.gradle index b1327d1c0b..8ba81c98a4 100644 --- a/buildSrc/build.gradle +++ b/buildSrc/build.gradle @@ -46,8 +46,9 @@ repositories { } dependencies { - 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 + implementation "com.github.docker-java:docker-java:$constants.dockerJavaVersion" + implementation "com.github.docker-java:docker-java-transport-httpclient5:$constants.dockerJavaVersion" + implementation "org.jooq:joor:$constants.joorVersion" if (System.getenv('CORDA_ARTIFACTORY_USERNAME') != null || project.hasProperty('cordaArtifactoryUsername')) { implementation "com.r3.internal.gradle.plugins:publish:$internalPublishVersion" diff --git a/buildSrc/src/main/groovy/corda.kotlin-1.2.gradle b/buildSrc/src/main/groovy/corda.kotlin-1.2.gradle new file mode 100644 index 0000000000..fa45f93245 --- /dev/null +++ b/buildSrc/src/main/groovy/corda.kotlin-1.2.gradle @@ -0,0 +1,91 @@ +import org.gradle.api.internal.file.DefaultSourceDirectorySet + +import static org.joor.Reflect.onClass + +pluginManager.apply(Kotlin12Plugin.class) + +// We cannot use the 1.2 Kotlin plugin as it only works with a very old version of Gradle, which itself will only work on Java 8. So we need +// our own plugin which calls the 1.2 compiler directly. +class Kotlin12Plugin implements Plugin { + private static final KOTLIN_VERSION = "1.2.71" + + @Override + void apply(Project project) { + project.pluginManager.apply(JavaPlugin.class) + + project.extensions.add("kotlin_1_2_version", KOTLIN_VERSION) + project.dependencies.add("implementation", "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$KOTLIN_VERSION") + + def kotlinCompilerConfiguration = project.configurations.create("kotlinCompiler") + project.dependencies.add("kotlinCompiler", "org.jetbrains.kotlin:kotlin-compiler:$KOTLIN_VERSION") + + project.extensions.getByType(JavaPluginExtension.class).sourceSets.configureEach { sourceSet -> + // Create the "src/*/kotlin" SourceDirectorySet, alongside the "java" one + def kotlinSourceDirectorySet = new DefaultSourceDirectorySet(project.objects.sourceDirectorySet("kotlin", "${sourceSet.displayName} Kotlin source")) + sourceSet.extensions.add(SourceDirectorySet.class, "kotlin", kotlinSourceDirectorySet) + kotlinSourceDirectorySet.filter.include("**/*.java", "**/*.kt") + kotlinSourceDirectorySet.srcDir(project.file("src/${sourceSet.name}/kotlin")) + + def allKotlin = project.objects.sourceDirectorySet("allkotlin", "${sourceSet.displayName} Kotlin source") + allKotlin.filter.include("**/*.kt") + allKotlin.source(kotlinSourceDirectorySet) + + sourceSet.allJava.source(kotlinSourceDirectorySet) + sourceSet.allSource.source(kotlinSourceDirectorySet) + + def kotlinBuildDir = project.layout.buildDirectory.dir("classes/kotlin/${sourceSet.name}") + sourceSet.output.dir(kotlinBuildDir) + + def taskSourceSetName = isMain(sourceSet) ? "" : sourceSet.name.capitalize() + + def compileKotlin = project.tasks.register("compile${taskSourceSetName}Kotlin", KotlinCompile.class) { task -> + // The 1.2 compiler needs to be laoded in a separate class loader, as the build classpath already contains its own version + // of Kotlin. + task.compilerClasspath.from(kotlinCompilerConfiguration) + task.source(allKotlin) + // Paradoxically, the Java sources are also required by the Kotlin compiler. This is actually so that it can correctly + // resolve any references the Kotlin code makes to Java code. + task.source(sourceSet.allJava) + task.classpath = sourceSet.compileClasspath + task.destinationDirectory = kotlinBuildDir + } + + // Compiling the Java code needs the compiled Kotlin code first + project.tasks.named("compile${taskSourceSetName}Java", JavaCompile.class) { task -> + task.classpath += project.files(compileKotlin.map { it.destinationDirectory }) + } + } + } +} + +abstract class KotlinCompile extends AbstractCompile { + @Classpath + abstract ConfigurableFileCollection getCompilerClasspath() + + @TaskAction + void compile() { + def args = [ + "-jvm-target", "1.8", + "-language-version", "1.2", + "-api-version", "1.2", + "-java-parameters", + "-Xjvm-default=compatibility", + "-no-stdlib", + "-Xallow-kotlin-package", // We may have copies of stdlib APIs (see `core-1.2`) + "-cp", classpath.asPath, + "-d", destinationDirectory.get().asFile.absolutePath + ] + args.addAll(source.collect { it.absolutePath }) + + logger.info("args: {}", args) + + def compilerClassLoader = new URLClassLoader(compilerClasspath.collect { it.toURI().toURL() } as URL[]) + def exitCode = onClass("org.jetbrains.kotlin.cli.jvm.K2JVMCompiler", compilerClassLoader) + .create() + .call("exec", System.err, args as String[]) + .get() + if (exitCode.toString() != "OK") { + throw new GradleException("Compilation error. See log for more details") + } + } +} diff --git a/client/jackson/build.gradle b/client/jackson/build.gradle index 47b9b51ac3..19a1ff21eb 100644 --- a/client/jackson/build.gradle +++ b/client/jackson/build.gradle @@ -1,4 +1,3 @@ -apply plugin: 'java' apply plugin: 'org.jetbrains.kotlin.jvm' apply plugin: 'net.corda.plugins.api-scanner' apply plugin: 'corda.common-publishing' diff --git a/client/rpc/src/main/kotlin/net/corda/client/rpc/internal/RPCClientTelemetry.kt b/client/rpc/src/main/kotlin/net/corda/client/rpc/internal/RPCClientTelemetry.kt index c2c9457919..cc7693d0f5 100644 --- a/client/rpc/src/main/kotlin/net/corda/client/rpc/internal/RPCClientTelemetry.kt +++ b/client/rpc/src/main/kotlin/net/corda/client/rpc/internal/RPCClientTelemetry.kt @@ -1,14 +1,15 @@ package net.corda.client.rpc.internal -import net.corda.core.internal.telemetry.OpenTelemetryComponent import net.corda.core.internal.telemetry.SimpleLogTelemetryComponent import net.corda.core.internal.telemetry.TelemetryServiceImpl import net.corda.core.utilities.contextLogger +import net.corda.nodeapi.internal.telemetry.OpenTelemetryComponent -class RPCClientTelemetry(val serviceName: String, val openTelemetryEnabled: Boolean, - val simpleLogTelemetryEnabled: Boolean, val spanStartEndEventsEnabled: Boolean, +class RPCClientTelemetry(serviceName: String, + val openTelemetryEnabled: Boolean, + val simpleLogTelemetryEnabled: Boolean, + val spanStartEndEventsEnabled: Boolean, val copyBaggageToTags: Boolean) { - companion object { private val log = contextLogger() } @@ -39,4 +40,4 @@ class RPCClientTelemetry(val serviceName: String, val openTelemetryEnabled: Bool fun getTelemetryHandle(telemetryClass: Class): T? { return telemetryService.getTelemetryHandle(telemetryClass) } -} \ No newline at end of file +} diff --git a/confidential-identities/build.gradle b/confidential-identities/build.gradle index 606c0930d7..4c7cd9e050 100644 --- a/confidential-identities/build.gradle +++ b/confidential-identities/build.gradle @@ -1,5 +1,6 @@ // 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: 'org.jetbrains.kotlin.jvm' apply plugin: 'net.corda.plugins.quasar-utils' apply plugin: 'net.corda.plugins.cordapp' apply plugin: 'corda.common-publishing' diff --git a/constants.properties b/constants.properties index ab94de1edb..a4e352dca9 100644 --- a/constants.properties +++ b/constants.properties @@ -100,3 +100,4 @@ controlsfxVersion=8.40.15 fontawesomefxCommonsVersion=11.0 fontawesomefxFontawesomeVersion=4.7.0-11 javaassistVersion=3.29.2-GA +joorVersion=0.9.15 diff --git a/core-1.2/README.md b/core-1.2/README.md new file mode 100644 index 0000000000..9543551417 --- /dev/null +++ b/core-1.2/README.md @@ -0,0 +1,4 @@ +This is a Kotlin 1.2 version of the `core` module, which is consumed by the `verifier` module, for verifying contracts written in Kotlin +1.2. This is just a "shell" module which uses the existing the code in `core` and compiles it with the 1.2 compiler. + +To allow `core` to benefit from new APIs introduced since 1.2, those APIs much be copied into this module with the same `kotlin` package. diff --git a/core-1.2/build.gradle b/core-1.2/build.gradle new file mode 100644 index 0000000000..26368194bb --- /dev/null +++ b/core-1.2/build.gradle @@ -0,0 +1,34 @@ +apply plugin: "corda.kotlin-1.2" +apply plugin: "corda.common-publishing" + +description 'Corda core built with Kotlin 1.2' + +sourceSets { + main { + java.srcDir("../core/src/main/java") + kotlin.srcDir("../core/src/main/kotlin") + } +} + +dependencies { + // Use the same dependencies as core (minus Kotlin) + implementation(project(path: ":core", configuration: "resolvableImplementation")) { + exclude(group: "org.jetbrains.kotlin") + } + implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_1_2_version" + implementation "co.paralleluniverse:quasar-core:$quasar_version" +} + +jar { + archiveBaseName = 'corda-core-1.2' +} + +// TODO Don't publish publicly as it's only needed by the `verifier` module which consumes this into a fat jar. +publishing { + publications { + maven(MavenPublication) { + artifactId 'corda-core-1.2' + from components.java + } + } +} diff --git a/core-1.2/src/main/kotlin/kotlin/collections/KotlinCollections1.2.kt b/core-1.2/src/main/kotlin/kotlin/collections/KotlinCollections1.2.kt new file mode 100644 index 0000000000..9e9650968f --- /dev/null +++ b/core-1.2/src/main/kotlin/kotlin/collections/KotlinCollections1.2.kt @@ -0,0 +1,37 @@ +// Implement the new post-1.2 APIs which are used by core and serialization +@file:Suppress("unused", "MagicNumber", "INVISIBLE_MEMBER") + +package kotlin.collections + +inline fun Iterable.associateWith(valueSelector: (K) -> V): Map { + val result = LinkedHashMap(mapCapacity(if (this is Collection<*>) size else 10).coerceAtLeast(16)) + return associateWithTo(result, valueSelector) +} + +inline fun > Iterable.associateWithTo(destination: M, valueSelector: (K) -> V): M { + for (element in this) { + destination.put(element, valueSelector(element)) + } + return destination +} + +inline fun Iterable.sumOf(selector: (T) -> Int): Int { + var sum = 0 + for (element in this) { + sum += selector(element) + } + return sum +} + +inline fun > Iterable.maxOf(selector: (T) -> R): R { + val iterator = iterator() + if (!iterator.hasNext()) throw NoSuchElementException() + var maxValue = selector(iterator.next()) + while (iterator.hasNext()) { + val v = selector(iterator.next()) + if (maxValue < v) { + maxValue = v + } + } + return maxValue +} diff --git a/core-1.2/src/main/kotlin/kotlin/io/path/KotlinIoPath1.2.kt b/core-1.2/src/main/kotlin/kotlin/io/path/KotlinIoPath1.2.kt new file mode 100644 index 0000000000..3a11116527 --- /dev/null +++ b/core-1.2/src/main/kotlin/kotlin/io/path/KotlinIoPath1.2.kt @@ -0,0 +1,37 @@ +// Implement the new post-1.2 APIs which are used by core and serialization +@file:Suppress("unused", "SpreadOperator", "NOTHING_TO_INLINE") + +package kotlin.io.path + +import java.io.InputStream +import java.io.OutputStream +import java.nio.file.Files +import java.nio.file.LinkOption +import java.nio.file.OpenOption +import java.nio.file.Path +import java.nio.file.attribute.FileAttribute + +inline operator fun Path.div(other: String): Path = this.resolve(other) + +fun Path.listDirectoryEntries(glob: String = "*"): List = Files.newDirectoryStream(this, glob).use { it.toList() } + +inline fun Path.createDirectories(vararg attributes: FileAttribute<*>): Path = Files.createDirectories(this, *attributes) + +inline fun Path.deleteIfExists(): Boolean = Files.deleteIfExists(this) + +inline fun Path.exists(vararg options: LinkOption): Boolean = Files.exists(this, *options) + +inline fun Path.inputStream(vararg options: OpenOption): InputStream = Files.newInputStream(this, *options) + +inline fun Path.outputStream(vararg options: OpenOption): OutputStream = Files.newOutputStream(this, *options) + +inline fun Path.isDirectory(vararg options: LinkOption): Boolean = Files.isDirectory(this, *options) + +inline fun Path.isSymbolicLink(): Boolean = Files.isSymbolicLink(this) + +inline fun Path.readSymbolicLink(): Path = Files.readSymbolicLink(this) + +val Path.name: String + get() = fileName?.toString().orEmpty() + +inline fun Path.readBytes(): ByteArray = Files.readAllBytes(this) diff --git a/core-1.2/src/main/kotlin/kotlin/text/KotlinText1.2.kt b/core-1.2/src/main/kotlin/kotlin/text/KotlinText1.2.kt new file mode 100644 index 0000000000..b8722316cd --- /dev/null +++ b/core-1.2/src/main/kotlin/kotlin/text/KotlinText1.2.kt @@ -0,0 +1,10 @@ +// Implement the new post-1.2 APIs which are used by core and serialization +@file:Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN", "NOTHING_TO_INLINE", "unused") + +package kotlin.text + +import java.util.Locale + +inline fun String.lowercase(): String = (this as java.lang.String).toLowerCase(Locale.ROOT) + +inline fun String.lowercase(locale: Locale): String = (this as java.lang.String).toLowerCase(locale) diff --git a/core/build.gradle b/core/build.gradle index 0cffc580dc..7267a11336 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -11,68 +11,51 @@ sourceSets { } configurations { + resolvableImplementation.extendsFrom implementation + integrationTestImplementation.extendsFrom testImplementation integrationTestRuntimeOnly.extendsFrom testRuntimeOnly smokeTestCompile.extendsFrom compile smokeTestRuntimeOnly.extendsFrom runtimeOnly + + testArtifacts.extendsFrom(testRuntimeClasspath) } dependencies { - 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" - 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}" - - testImplementation "commons-fileupload:commons-fileupload:$fileupload_version" - - // Guava: Google test library (collections test suite) - testImplementation "com.google.guava:guava-testlib:$guava_version" - implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version" - testImplementation "org.jetbrains.kotlin:kotlin-test:$kotlin_version" - - // Hamkrest, for fluent, composable matchers - testImplementation "com.natpryce:hamkrest:$hamkrest_version" - // SLF4J: commons-logging bindings for a SLF4J back end implementation "org.slf4j:jcl-over-slf4j:$slf4j_version" implementation "org.slf4j:slf4j-api:$slf4j_version" - - // AssertJ: for fluent assertions for testing - testImplementation "org.assertj:assertj-core:${assertj_version}" - // Guava: Google utilities library. implementation "com.google.guava:guava:$guava_version" - // For caches rather than guava implementation "com.github.ben-manes.caffeine:caffeine:$caffeine_version" - // RxJava: observable streams of events. implementation "io.reactivex:rxjava:$rxjava_version" - implementation "org.apache.commons:commons-lang3:$commons_lang3_version" - // Java ed25519 implementation. See https://github.com/str4d/ed25519-java/ implementation "net.i2p.crypto:eddsa:$eddsa_version" - // Bouncy castle support needed for X509 certificate manipulation implementation "org.bouncycastle:bcprov-jdk18on:${bouncycastle_version}" - testImplementation "org.bouncycastle:bcpkix-jdk18on:${bouncycastle_version}" - // required to use @Type annotation implementation "org.hibernate:hibernate-core:$hibernate_version" - // FastThreadLocal implementation "io.netty:netty-common:$netty_version" + implementation "io.github.classgraph:classgraph:$class_graph_version" - implementation group: "io.github.classgraph", name: "classgraph", version: class_graph_version - + testImplementation sourceSets.obfuscator.output + testImplementation "org.junit.jupiter:junit-jupiter-api:$junit_jupiter_version" + testImplementation "junit:junit:$junit_version" + testImplementation "commons-fileupload:commons-fileupload:$fileupload_version" + // Guava: Google test library (collections test suite) + testImplementation "com.google.guava:guava-testlib:$guava_version" + testImplementation "org.jetbrains.kotlin:kotlin-test:$kotlin_version" + // Hamkrest, for fluent, composable matchers + testImplementation "com.natpryce:hamkrest:$hamkrest_version" + // AssertJ: for fluent assertions for testing + testImplementation "org.assertj:assertj-core:$assertj_version" + testImplementation "org.bouncycastle:bcpkix-jdk18on:$bouncycastle_version" testImplementation "org.ow2.asm:asm:$asm_version" // JDK11: required by Quasar at run-time @@ -103,10 +86,6 @@ jar { } } -configurations { - testArtifacts.extendsFrom testRuntimeClasspath -} - processTestResources { inputs.files(jar) into("zip") { diff --git a/core/src/main/kotlin/net/corda/core/internal/InternalUtils.kt b/core/src/main/kotlin/net/corda/core/internal/InternalUtils.kt index b3ef3ef36e..99a60aa8e3 100644 --- a/core/src/main/kotlin/net/corda/core/internal/InternalUtils.kt +++ b/core/src/main/kotlin/net/corda/core/internal/InternalUtils.kt @@ -1,3 +1,5 @@ +@file:Suppress("MagicNumber") + package net.corda.core.internal import net.corda.core.crypto.Crypto @@ -34,6 +36,7 @@ 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 @@ -60,6 +63,8 @@ import java.util.Spliterator.SUBSIZED import java.util.Spliterators import java.util.concurrent.ExecutorService import java.util.concurrent.TimeUnit +import java.util.jar.JarEntry +import java.util.jar.JarInputStream import java.util.stream.Collectors import java.util.stream.Collectors.toCollection import java.util.stream.IntStream @@ -68,7 +73,6 @@ 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 @@ -132,14 +136,17 @@ fun List.indexOfOrThrow(item: T): Int { /** * Similar to [Iterable.map] except it maps to a [Set] which preserves the iteration order. */ +@Suppress("INVISIBLE_MEMBER", "RemoveExplicitTypeArguments") // Because the external verifier uses Kotlin 1.2 inline fun Iterable.mapToSet(transform: (T) -> R): Set { - if (this is Collection) { + return if (this is Collection) { when (size) { 0 -> return emptySet() 1 -> return setOf(transform(first())) + else -> mapTo(LinkedHashSet(mapCapacity(size)), transform) } + } else { + mapTo(LinkedHashSet(), transform) } - return mapTo(LinkedHashSet(), transform) } /** @@ -176,6 +183,8 @@ fun InputStream.hash(): SecureHash { inline fun InputStream.readObject(): T = readFully().deserialize() +fun JarInputStream.entries(): Sequence = generateSequence(nextJarEntry) { nextJarEntry } + fun String.abbreviate(maxWidth: Int): String = if (length <= maxWidth) this else "${take(maxWidth - 1)}…" /** @@ -370,17 +379,10 @@ class DeclaredField(clazz: Class<*>, name: String, private val receiver: Any? val name: String = javaField.name private fun Field.accessible(action: Field.() -> RESULT): RESULT { - @Suppress("DEPRECATION") // JDK11: isAccessible() should be replaced with canAccess() (since 9) - val accessible = isAccessible isAccessible = true - try { - return action(this) - } finally { - isAccessible = accessible - } + return action(this) } - @Throws(NoSuchFieldException::class) private fun findField(fieldName: String, clazz: Class<*>?): Field { if (clazz == null) { throw NoSuchFieldException(fieldName) @@ -436,7 +438,7 @@ inline val Member.isStatic: Boolean get() = Modifier.isStatic(modifiers) inline val Member.isFinal: Boolean get() = Modifier.isFinal(modifiers) -fun URL.toPath(): Path = toURI().toPath() +fun URL.toPath(): Path = Paths.get(toURI()) val DEFAULT_HTTP_CONNECT_TIMEOUT = 30.seconds.toMillis() val DEFAULT_HTTP_READ_TIMEOUT = 30.seconds.toMillis() diff --git a/core/src/main/kotlin/net/corda/core/internal/JarSignatureCollector.kt b/core/src/main/kotlin/net/corda/core/internal/JarSignatureCollector.kt index 6132dec45d..7ff377250d 100644 --- a/core/src/main/kotlin/net/corda/core/internal/JarSignatureCollector.kt +++ b/core/src/main/kotlin/net/corda/core/internal/JarSignatureCollector.kt @@ -57,8 +57,8 @@ object JarSignatureCollector { return firstSignerSet } - private val JarInputStream.fileSignerSets: List>> get() = - entries.thatAreSignable.shreddedFrom(this).toFileSignerSet().toList() + private val JarInputStream.fileSignerSets: List>> + get() = entries().thatAreSignable.shreddedFrom(this).toFileSignerSet().toList() private val Sequence.thatAreSignable: Sequence get() = filterNot { isNotSignable(it) } @@ -85,8 +85,6 @@ object JarSignatureCollector { private fun Set.toCertificates(): List = map { it.signerCertPath.certificates[0] as X509Certificate }.sortedBy { it.toString() } // Sorted for determinism. - - private val JarInputStream.entries get(): Sequence = generateSequence(nextJarEntry) { nextJarEntry } } class InvalidJarSignersException(msg: String) : Exception(msg) \ No newline at end of file diff --git a/core/src/main/kotlin/net/corda/core/internal/verification/AttachmentFixups.kt b/core/src/main/kotlin/net/corda/core/internal/verification/AttachmentFixups.kt index 4e20a46d41..ab01403edb 100644 --- a/core/src/main/kotlin/net/corda/core/internal/verification/AttachmentFixups.kt +++ b/core/src/main/kotlin/net/corda/core/internal/verification/AttachmentFixups.kt @@ -56,7 +56,7 @@ class AttachmentFixups { private fun parseIds(ids: String): Set { return ids.splitToSequence(",") .map(String::trim) - .filterNot(String::isEmpty) + .filter { it.isNotEmpty() } .mapTo(LinkedHashSet(), SecureHash.Companion::create) } diff --git a/core/src/main/kotlin/net/corda/core/internal/verification/VerificationService.kt b/core/src/main/kotlin/net/corda/core/internal/verification/VerificationService.kt index f92dcfa63e..ebf90f3e94 100644 --- a/core/src/main/kotlin/net/corda/core/internal/verification/VerificationService.kt +++ b/core/src/main/kotlin/net/corda/core/internal/verification/VerificationService.kt @@ -9,6 +9,7 @@ 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.entries import net.corda.core.internal.getRequiredTransaction import net.corda.core.node.NetworkParameters import net.corda.core.node.services.AttachmentStorage @@ -31,7 +32,6 @@ 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. @@ -135,19 +135,12 @@ interface VerificationService : VerificationSupport { // 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 + if (attch.hasFile("$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 - } - } - } + private fun Attachment.hasFile(className: String): Boolean = openAsJAR().use { it.entries().any { entry -> entry.name == className } } override fun isAttachmentTrusted(attachment: Attachment): Boolean = attachmentTrustCalculator.calculate(attachment) diff --git a/core/src/main/kotlin/net/corda/core/internal/verification/VerifyingServiceHub.kt b/core/src/main/kotlin/net/corda/core/internal/verification/VerifyingServiceHub.kt index babd3cd0b5..6bcec51d16 100644 --- a/core/src/main/kotlin/net/corda/core/internal/verification/VerifyingServiceHub.kt +++ b/core/src/main/kotlin/net/corda/core/internal/verification/VerifyingServiceHub.kt @@ -39,7 +39,8 @@ interface VerifyingServiceHub : ServiceHub, VerificationService { private fun loadContractAttachment(stateRef: StateRef, forContractClassName: String?): Attachment { val stx = getRequiredTransaction(stateRef.txhash) - return when (val ctx = stx.coreTransaction) { + val ctx = stx.coreTransaction + return when (ctx) { is WireTransaction -> { val contractClassName = forContractClassName ?: ctx.outRef(stateRef.index).state.contract ctx.attachments diff --git a/core/src/main/kotlin/net/corda/core/schemas/PersistentTypes.kt b/core/src/main/kotlin/net/corda/core/schemas/PersistentTypes.kt index 5295b4a46a..79b86d2b96 100644 --- a/core/src/main/kotlin/net/corda/core/schemas/PersistentTypes.kt +++ b/core/src/main/kotlin/net/corda/core/schemas/PersistentTypes.kt @@ -75,9 +75,10 @@ open class MappedSchema(schemaFamily: Class<*>, * A super class for all mapped states exported to a schema that ensures the [StateRef] appears on the database row. The * [StateRef] will be set to the correct value by the framework (there's no need to set during mapping generation by the state itself). */ +@Suppress("RedundantModalityModifier") // Because the external verifier uses Kotlin 1.2 @MappedSuperclass @CordaSerializable -class PersistentState(@EmbeddedId override var stateRef: PersistentStateRef? = null) : DirectStatePersistable +open class PersistentState(@EmbeddedId override var stateRef: PersistentStateRef? = null) : DirectStatePersistable /** * Embedded [StateRef] representation used in state mapping. diff --git a/core/src/main/kotlin/net/corda/core/serialization/internal/AttachmentsClassLoader.kt b/core/src/main/kotlin/net/corda/core/serialization/internal/AttachmentsClassLoader.kt index ad927efdf6..ee1a02a44b 100644 --- a/core/src/main/kotlin/net/corda/core/serialization/internal/AttachmentsClassLoader.kt +++ b/core/src/main/kotlin/net/corda/core/serialization/internal/AttachmentsClassLoader.kt @@ -17,6 +17,7 @@ import net.corda.core.internal.VisibleForTesting import net.corda.core.internal.cordapp.targetPlatformVersion import net.corda.core.internal.createInstancesOfClassesImplementing import net.corda.core.internal.createSimpleCache +import net.corda.core.internal.entries import net.corda.core.internal.toSynchronised import net.corda.core.node.NetworkParameters import net.corda.core.serialization.AMQP_ENVELOPE_CACHE_INITIAL_CAPACITY @@ -48,7 +49,6 @@ import java.util.ServiceLoader import java.util.WeakHashMap import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.atomic.AtomicLong -import java.util.function.Function import kotlin.collections.component1 import kotlin.collections.component2 import kotlin.collections.set @@ -170,13 +170,7 @@ class AttachmentsClassLoader(attachments: List, } private fun containsClasses(attachment: Attachment): Boolean { - attachment.openAsJAR().use { jar -> - while (true) { - val entry = jar.nextJarEntry ?: return false - if (entry.name.endsWith(".class", ignoreCase = true)) return true - } - } - return false + return attachment.openAsJAR().use { it.entries().any { entry -> entry.name.endsWith(".class", ignoreCase = true) } } } // This function attempts to strike a balance between security and usability when it comes to the no-overlap rule. @@ -412,7 +406,7 @@ object AttachmentURLStreamHandlerFactory : URLStreamHandlerFactory { @Synchronized fun toUrl(attachment: Attachment): URL { - val uniqueURL = URL(attachmentScheme, "", -1, attachment.id.toString()+ "?" + uniqueness.getAndIncrement(), AttachmentURLStreamHandler) + val uniqueURL = URL(attachmentScheme, "", -1, "${attachment.id}?${uniqueness.getAndIncrement()}", AttachmentURLStreamHandler) loadedAttachments[uniqueURL] = attachment return uniqueURL } @@ -429,12 +423,12 @@ object AttachmentURLStreamHandlerFactory : URLStreamHandlerFactory { override fun equals(attachmentUrl: URL, otherURL: URL?): Boolean { if (attachmentUrl.protocol != otherURL?.protocol) return false - if (attachmentUrl.protocol != attachmentScheme) throw IllegalArgumentException("Cannot handle protocol: ${attachmentUrl.protocol}") + require(attachmentUrl.protocol == attachmentScheme) { "Cannot handle protocol: ${attachmentUrl.protocol}" } return attachmentUrl.file == otherURL?.file } override fun hashCode(url: URL): Int { - if (url.protocol != attachmentScheme) throw IllegalArgumentException("Cannot handle protocol: ${url.protocol}") + require(url.protocol == attachmentScheme) { "Cannot handle protocol: ${url.protocol}" } return url.file.hashCode() } } @@ -466,7 +460,10 @@ private class AttachmentsHolderImpl : AttachmentsHolder { } interface AttachmentsClassLoaderCache { - fun computeIfAbsent(key: AttachmentsClassLoaderKey, mappingFunction: Function): SerializationContext + fun computeIfAbsent( + key: AttachmentsClassLoaderKey, + mappingFunction: (AttachmentsClassLoaderKey) -> SerializationContext + ): SerializationContext } class AttachmentsClassLoaderCacheImpl(cacheFactory: NamedCacheFactory) : SingletonSerializeAsToken(), AttachmentsClassLoaderCache { @@ -514,18 +511,23 @@ class AttachmentsClassLoaderCacheImpl(cacheFactory: NamedCacheFactory) : Singlet }, "AttachmentsClassLoader_cache" ) - override fun computeIfAbsent(key: AttachmentsClassLoaderKey, mappingFunction: Function): SerializationContext { + override fun computeIfAbsent( + key: AttachmentsClassLoaderKey, + mappingFunction: (AttachmentsClassLoaderKey) -> SerializationContext + ): SerializationContext { purgeExpiryQueue() return cache.get(key, mappingFunction) ?: throw NullPointerException("null returned from cache mapping function") } } class AttachmentsClassLoaderSimpleCacheImpl(cacheSize: Int) : AttachmentsClassLoaderCache { - private val cache: MutableMap = createSimpleCache(cacheSize).toSynchronised() - override fun computeIfAbsent(key: AttachmentsClassLoaderKey, mappingFunction: Function): SerializationContext { + override fun computeIfAbsent( + key: AttachmentsClassLoaderKey, + mappingFunction: (AttachmentsClassLoaderKey) -> SerializationContext + ): SerializationContext { return cache.computeIfAbsent(key, mappingFunction) } } diff --git a/core/src/main/kotlin/net/corda/core/transactions/LedgerTransaction.kt b/core/src/main/kotlin/net/corda/core/transactions/LedgerTransaction.kt index 8037668b68..8845cfca4c 100644 --- a/core/src/main/kotlin/net/corda/core/transactions/LedgerTransaction.kt +++ b/core/src/main/kotlin/net/corda/core/transactions/LedgerTransaction.kt @@ -58,7 +58,7 @@ import java.util.function.Supplier * * [LedgerTransaction]s should never be instantiated directly from client code, but rather via WireTransaction.toLedgerTransaction */ -@Suppress("LongParameterList") +@Suppress("LongParameterList", "RedundantSamConstructor") // Because the external verifier uses Kotlin 1.2 class LedgerTransaction private constructor( // DOCSTART 1 @@ -465,7 +465,7 @@ private constructor( } inline fun filterInputs(crossinline predicate: (T) -> Boolean): List { - return filterInputs(T::class.java) { predicate(it) } + return filterInputs(T::class.java, Predicate { predicate(it) }) } /** @@ -481,7 +481,7 @@ private constructor( } inline fun filterReferenceInputs(crossinline predicate: (T) -> Boolean): List { - return filterReferenceInputs(T::class.java) { predicate(it) } + return filterReferenceInputs(T::class.java, Predicate { predicate(it) }) } /** @@ -497,7 +497,7 @@ private constructor( } inline fun filterInRefs(crossinline predicate: (T) -> Boolean): List> { - return filterInRefs(T::class.java) { predicate(it) } + return filterInRefs(T::class.java, Predicate { predicate(it) }) } /** @@ -513,7 +513,7 @@ private constructor( } inline fun filterReferenceInputRefs(crossinline predicate: (T) -> Boolean): List> { - return filterReferenceInputRefs(T::class.java) { predicate(it) } + return filterReferenceInputRefs(T::class.java, Predicate { predicate(it) }) } /** @@ -530,7 +530,7 @@ private constructor( } inline fun findInput(crossinline predicate: (T) -> Boolean): T { - return findInput(T::class.java) { predicate(it) } + return findInput(T::class.java, Predicate { predicate(it) }) } /** @@ -543,11 +543,11 @@ private constructor( * @throws IllegalArgumentException if no item, or multiple items are found matching the requirements. */ fun findReference(clazz: Class, predicate: Predicate): T { - return referenceInputsOfType(clazz).single(predicate::test) + return referenceInputsOfType(clazz).single { predicate.test(it) } } inline fun findReference(crossinline predicate: (T) -> Boolean): T { - return findReference(T::class.java) { predicate(it) } + return findReference(T::class.java, Predicate { predicate(it) }) } /** @@ -564,7 +564,7 @@ private constructor( } inline fun findInRef(crossinline predicate: (T) -> Boolean): StateAndRef { - return findInRef(T::class.java) { predicate(it) } + return findInRef(T::class.java, Predicate { predicate(it) }) } /** @@ -581,7 +581,7 @@ private constructor( } inline fun findReferenceInputRef(crossinline predicate: (T) -> Boolean): StateAndRef { - return findReferenceInputRef(T::class.java) { predicate(it) } + return findReferenceInputRef(T::class.java, Predicate { predicate(it) }) } /** @@ -616,7 +616,7 @@ private constructor( } inline fun filterCommands(crossinline predicate: (T) -> Boolean): List> { - return filterCommands(T::class.java) { predicate(it) } + return filterCommands(T::class.java, Predicate { predicate(it) }) } /** @@ -633,7 +633,7 @@ private constructor( } inline fun findCommand(crossinline predicate: (T) -> Boolean): Command { - return findCommand(T::class.java) { predicate(it) } + return findCommand(T::class.java, Predicate { predicate(it) }) } /** diff --git a/core/src/main/kotlin/net/corda/core/transactions/TransactionBuilder.kt b/core/src/main/kotlin/net/corda/core/transactions/TransactionBuilder.kt index 32ef6351ca..ed97d740d8 100644 --- a/core/src/main/kotlin/net/corda/core/transactions/TransactionBuilder.kt +++ b/core/src/main/kotlin/net/corda/core/transactions/TransactionBuilder.kt @@ -489,7 +489,8 @@ open class TransactionBuilder( } if (explicitContractAttachment != null && hashAttachments.singleOrNull() != null) { - require(explicitContractAttachment == hashAttachments.single().attachment.id) { + @Suppress("USELESS_CAST") // Because the external verifier uses Kotlin 1.2 + require(explicitContractAttachment == (hashAttachments.single() as ContractAttachment).attachment.id) { "An attachment has been explicitly set for contract $contractClassName in the transaction builder which conflicts with the HashConstraint of a state." } } diff --git a/core/src/main/kotlin/net/corda/core/utilities/ProgressTracker.kt b/core/src/main/kotlin/net/corda/core/utilities/ProgressTracker.kt index f3c0eb265b..0cd5f284a0 100644 --- a/core/src/main/kotlin/net/corda/core/utilities/ProgressTracker.kt +++ b/core/src/main/kotlin/net/corda/core/utilities/ProgressTracker.kt @@ -8,7 +8,6 @@ import rx.Subscription import rx.functions.Action1 import rx.subjects.ReplaySubject import java.io.Serializable -import java.util.* /** * A progress tracker helps surface information about the progress of an operation to a user interface or API of some @@ -34,12 +33,12 @@ import java.util.* */ @CordaSerializable class ProgressTracker(vararg inputSteps: Step) { - private companion object { private val log = contextLogger() } - private fun interface SerializableAction: Action1, Serializable + @FunctionalInterface + private interface SerializableAction1 : Action1, Serializable @CordaSerializable sealed class Change(val progressTracker: ProgressTracker) { @@ -61,7 +60,11 @@ class ProgressTracker(vararg inputSteps: Step) { */ @CordaSerializable open class Step(open val label: String) { - private fun definitionLocation(): String = Exception().stackTrace.first { it.className != ProgressTracker.Step::class.java.name }.let { "${it.className}:${it.lineNumber}" } + private fun definitionLocation(): String { + return Exception().stackTrace + .first { it.className != Step::class.java.name } + .let { "${it.className}:${it.lineNumber}" } + } // Required when Steps with the same name are defined in multiple places. private val discriminator: String = definitionLocation() @@ -149,10 +152,17 @@ class ProgressTracker(vararg inputSteps: Step) { stepIndex = index _changes.onNext(Change.Position(this, steps[index])) recalculateStepsTreeIndex() - curChangeSubscription = currentStep.changes.subscribe((SerializableAction { - _changes.onNext(it) - if (it is Change.Structural || it is Change.Rendering) rebuildStepsTree() else recalculateStepsTreeIndex() - }), (SerializableAction { _changes.onError(it) })) + curChangeSubscription = currentStep.changes.subscribe( + object : SerializableAction1 { + override fun call(c: Change) { + _changes.onNext(c) + if (c is Change.Structural || c is Change.Rendering) rebuildStepsTree() else recalculateStepsTreeIndex() + } + }, + object : SerializableAction1 { + override fun call(t: Throwable) = _changes.onError(t) + } + ) if (currentStep == DONE) { _changes.onCompleted() @@ -182,9 +192,7 @@ class ProgressTracker(vararg inputSteps: Step) { * The zero-based index of the current step in the [steps] array (i.e. with UNSTARTED and DONE) */ var stepIndex: Int = 0 - private set(value) { - field = value - } + private set /** * The zero-bases index of the current step in a [allStepsLabels] list @@ -206,18 +214,25 @@ class ProgressTracker(vararg inputSteps: Step) { fun getChildProgressTracker(step: Step): ProgressTracker? = childProgressTrackers[step]?.tracker - fun setChildProgressTracker(step: ProgressTracker.Step, childProgressTracker: ProgressTracker) { - val subscription = childProgressTracker.changes.subscribe((SerializableAction{ - _changes.onNext(it) - if (it is Change.Structural || it is Change.Rendering) rebuildStepsTree() else recalculateStepsTreeIndex() - }), (SerializableAction { _changes.onError(it) })) + fun setChildProgressTracker(step: Step, childProgressTracker: ProgressTracker) { + val subscription = childProgressTracker.changes.subscribe( + object : SerializableAction1 { + override fun call(c: Change) { + _changes.onNext(c) + if (c is Change.Structural || c is Change.Rendering) rebuildStepsTree() else recalculateStepsTreeIndex() + } + }, + object : SerializableAction1 { + override fun call(t: Throwable) = _changes.onError(t) + } + ) childProgressTrackers[step] = Child(childProgressTracker, subscription) childProgressTracker.parent = this _changes.onNext(Change.Structural(this, step)) rebuildStepsTree() } - private fun removeChildProgressTracker(step: ProgressTracker.Step) { + private fun removeChildProgressTracker(step: Step) { childProgressTrackers.remove(step)?.let { it.tracker.parent = null it.subscription?.unsubscribe() diff --git a/experimental/netparams/build.gradle b/experimental/netparams/build.gradle index d59978b2e8..20a2836371 100644 --- a/experimental/netparams/build.gradle +++ b/experimental/netparams/build.gradle @@ -1,4 +1,3 @@ -apply plugin: 'java' apply plugin: 'org.jetbrains.kotlin.jvm' description 'NetworkParameters signing tool' @@ -24,7 +23,7 @@ jar { exclude "META-INF/*.DSA" exclude "META-INF/*.RSA" } - baseName = "netparams" + archiveBaseName = "netparams" manifest { attributes( 'Main-Class': 'net.corda.netparams.NetParamsKt' diff --git a/experimental/nodeinfo/build.gradle b/experimental/nodeinfo/build.gradle index 6557fd612e..387c5b9ad0 100644 --- a/experimental/nodeinfo/build.gradle +++ b/experimental/nodeinfo/build.gradle @@ -1,4 +1,3 @@ -apply plugin: 'java' apply plugin: 'org.jetbrains.kotlin.jvm' description 'NodeInfo signing tool' @@ -23,7 +22,7 @@ jar { exclude "META-INF/*.DSA" exclude "META-INF/*.RSA" } - baseName = "nodeinfo" + archiveBaseName = "nodeinfo" manifest { attributes( 'Main-Class': 'net.corda.nodeinfo.NodeInfoKt' diff --git a/node-api/build.gradle b/node-api/build.gradle index de20afd37f..16d7c46244 100644 --- a/node-api/build.gradle +++ b/node-api/build.gradle @@ -11,6 +11,9 @@ dependencies { implementation project(':common-logging') implementation project(":common-validation") + implementation "io.opentelemetry:opentelemetry-api:$open_telemetry_version" + compileOnly project(':opentelemetry') + implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version" // TODO: remove the forced update of commons-collections and beanutils when artemis updates them @@ -90,7 +93,7 @@ configurations { testArtifacts.extendsFrom testRuntimeOnlyClasspath } -task testJar(type: Jar) { +tasks.register('testJar', Jar) { classifier "tests" from sourceSets.test.output } diff --git a/core/src/main/kotlin/net/corda/core/internal/telemetry/OpenTelemetryComponent.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/telemetry/OpenTelemetryComponent.kt similarity index 95% rename from core/src/main/kotlin/net/corda/core/internal/telemetry/OpenTelemetryComponent.kt rename to node-api/src/main/kotlin/net/corda/nodeapi/internal/telemetry/OpenTelemetryComponent.kt index ca5ae34549..73595dd460 100644 --- a/core/src/main/kotlin/net/corda/core/internal/telemetry/OpenTelemetryComponent.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/telemetry/OpenTelemetryComponent.kt @@ -1,4 +1,4 @@ -package net.corda.core.internal.telemetry +package net.corda.nodeapi.internal.telemetry import co.paralleluniverse.fibers.instrument.DontInstrument import io.opentelemetry.api.GlobalOpenTelemetry @@ -12,14 +12,23 @@ import io.opentelemetry.api.trace.StatusCode import io.opentelemetry.api.trace.Tracer import io.opentelemetry.context.Context import io.opentelemetry.context.Scope -import net.corda.core.flows.FlowLogic -import net.corda.core.serialization.CordaSerializable -import org.slf4j.Logger -import org.slf4j.LoggerFactory -import java.util.* -import java.util.concurrent.ConcurrentHashMap -import net.corda.opentelemetrydriver.OpenTelemetryDriver import io.opentelemetry.context.propagation.TextMapGetter +import net.corda.core.flows.FlowLogic +import net.corda.core.internal.telemetry.EndSpanEvent +import net.corda.core.internal.telemetry.EndSpanForFlowEvent +import net.corda.core.internal.telemetry.RecordExceptionEvent +import net.corda.core.internal.telemetry.SetStatusEvent +import net.corda.core.internal.telemetry.ShutdownTelemetryEvent +import net.corda.core.internal.telemetry.StartSpanEvent +import net.corda.core.internal.telemetry.StartSpanForFlowEvent +import net.corda.core.internal.telemetry.TelemetryComponent +import net.corda.core.internal.telemetry.TelemetryDataItem +import net.corda.core.internal.telemetry.TelemetryEvent +import net.corda.core.internal.telemetry.TelemetryStatusCode +import net.corda.core.serialization.CordaSerializable +import net.corda.opentelemetrydriver.OpenTelemetryDriver +import java.util.UUID +import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.ConcurrentLinkedDeque @CordaSerializable @@ -54,12 +63,11 @@ class TracerSetup(serviceName: String) { } @Suppress("TooManyFunctions") -class OpenTelemetryComponent(val serviceName: String, val spanStartEndEventsEnabled: Boolean, val copyBaggageToTags: Boolean) : TelemetryComponent { +class OpenTelemetryComponent(serviceName: String, val spanStartEndEventsEnabled: Boolean, val copyBaggageToTags: Boolean) : TelemetryComponent { val tracerSetup = TracerSetup(serviceName) val tracer: Tracer = tracerSetup.getTracer() companion object { - private val log: Logger = LoggerFactory.getLogger(OpenTelemetryComponent::class.java) const val OPENTELEMETRY_COMPONENT_NAME = "OpenTelemetry" } diff --git a/node/build.gradle b/node/build.gradle index 8009dc9ffe..310875b833 100644 --- a/node/build.gradle +++ b/node/build.gradle @@ -6,7 +6,6 @@ apply plugin: 'org.jetbrains.kotlin.jvm' // Java Persistence API support: create no-arg constructor // see: http://stackoverflow.com/questions/32038177/kotlin-with-jpa-default-constructor-hell apply plugin: 'org.jetbrains.kotlin.plugin.jpa' -apply plugin: 'java' apply plugin: 'net.corda.plugins.quasar-utils' apply plugin: 'corda.common-publishing' @@ -73,7 +72,7 @@ jib.container { processResources { from file("$rootDir/config/dev/log4j2.xml") from file("$rootDir/config/dev/jolokia-access.xml") - from(tasks.findByPath(":verifier:shadowJar")) { + from(tasks.getByPath(":verifier:shadowJar")) { into("net/corda/node/verification") rename { "external-verifier.jar" } } diff --git a/node/src/integration-test/kotlin/net/corda/node/verification/ExternalVerificationTest.kt b/node/src/integration-test/kotlin/net/corda/node/verification/ExternalVerificationTest.kt index 810b1ada08..091564fafb 100644 --- a/node/src/integration-test/kotlin/net/corda/node/verification/ExternalVerificationTest.kt +++ b/node/src/integration-test/kotlin/net/corda/node/verification/ExternalVerificationTest.kt @@ -6,7 +6,8 @@ import net.corda.core.contracts.CommandData import net.corda.core.contracts.Contract import net.corda.core.contracts.ContractState import net.corda.core.contracts.StateAndRef -import net.corda.core.contracts.TransactionVerificationException +import net.corda.core.contracts.TransactionVerificationException.ContractRejection +import net.corda.core.contracts.TypeOnlyCommandData import net.corda.core.crypto.SecureHash import net.corda.core.flows.FinalityFlow import net.corda.core.flows.FlowLogic @@ -16,6 +17,7 @@ import net.corda.core.flows.InitiatingFlow import net.corda.core.flows.NotaryChangeFlow import net.corda.core.flows.ReceiveFinalityFlow import net.corda.core.flows.StartableByRPC +import net.corda.core.flows.UnexpectedFlowEndException import net.corda.core.identity.AbstractParty import net.corda.core.identity.CordaX500Name import net.corda.core.identity.Party @@ -32,7 +34,6 @@ import net.corda.finance.DOLLARS import net.corda.finance.contracts.asset.Cash import net.corda.finance.flows.CashIssueFlow import net.corda.finance.flows.CashPaymentFlow -import net.corda.node.verification.ExternalVerificationTest.FailExternallyContract.State import net.corda.testing.core.ALICE_NAME import net.corda.testing.core.BOB_NAME import net.corda.testing.core.BOC_NAME @@ -89,6 +90,30 @@ class ExternalVerificationTest { } } + @Test(timeout=300_000) + fun `external verifier is unable to verify contracts which use new Kotlin APIs`() { + check(!IntArray::maxOrNull.isInline) + + internalDriver( + systemProperties = mapOf("net.corda.node.verification.external" to "true"), + cordappsForAllNodes = listOf(cordappWithPackages("net.corda.node.verification")) + ) { + val (alice, bob) = listOf( + startNode(NodeParameters(providedName = ALICE_NAME)), + startNode(NodeParameters(providedName = BOB_NAME)), + ).transpose().getOrThrow() + + assertThatExceptionOfType(UnexpectedFlowEndException::class.java).isThrownBy { + alice.rpc.startFlow(::NewKotlinApiFlow, bob.nodeInfo).returnValue.getOrThrow() + } + + assertThat(bob.externalVerifierLogs()).contains(""" + java.lang.NoSuchMethodError: 'java.lang.Integer kotlin.collections.ArraysKt.maxOrNull(int[])' + at net.corda.node.verification.ExternalVerificationTest${'$'}NewKotlinApiContract.verify(ExternalVerificationTest.kt: + """.trimIndent()) + } + } + @Test(timeout=300_000) fun `regular transactions can fail verification in external verifier`() { internalDriver( @@ -104,7 +129,7 @@ class ExternalVerificationTest { // Create a transaction from Alice to Bob, where Charlie is specified as the contract verification trigger val firstState = alice.rpc.startFlow(::FailExternallyFlow, null, charlie.nodeInfo, bob.nodeInfo).returnValue.getOrThrow() // When the transaction chain tries to moves onto Charlie, it will trigger the failure - assertThatExceptionOfType(TransactionVerificationException.ContractRejection::class.java) + assertThatExceptionOfType(ContractRejection::class.java) .isThrownBy { bob.rpc.startFlow(::FailExternallyFlow, firstState, charlie.nodeInfo, charlie.nodeInfo).returnValue.getOrThrow() } .withMessageContaining("Fail in external verifier: ${firstState.ref.txhash}") @@ -149,6 +174,7 @@ class ExternalVerificationTest { return verifierLogs[0].readText() } + class FailExternallyContract : Contract { override fun verify(tx: LedgerTransaction) { val command = tx.commandsOfType().single() @@ -165,40 +191,46 @@ class ExternalVerificationTest { } } - data class State(val party: Party) : ContractState { - override val participants: List get() = listOf(party) - } - + data class State(override val party: Party) : TestState data class Command(val failForParty: Party) : CommandData } + @StartableByRPC @InitiatingFlow - class FailExternallyFlow(private val inputState: StateAndRef?, + class FailExternallyFlow(inputState: StateAndRef?, private val failForParty: NodeInfo, - private val recipient: NodeInfo) : FlowLogic>() { - @Suspendable - override fun call(): StateAndRef { - val myParty = serviceHub.myInfo.legalIdentities[0] - val txBuilder = TransactionBuilder(serviceHub.networkMapCache.notaryIdentities[0]) - inputState?.let(txBuilder::addInputState) - txBuilder.addOutputState(State(myParty), FailExternallyContract::class.java.name) - txBuilder.addCommand(FailExternallyContract.Command(failForParty.legalIdentities[0]), myParty.owningKey) - val initialTx = serviceHub.signInitialTransaction(txBuilder) - val sessions = arrayListOf(initiateFlow(recipient.legalIdentities[0])) - inputState?.let { sessions += initiateFlow(it.state.data.party) } - val notarisedTx = subFlow(FinalityFlow(initialTx, sessions)) - return notarisedTx.toLedgerTransaction(serviceHub).outRef(0) - } + recipient: NodeInfo) : TestFlow(inputState, recipient) { + override fun newOutput() = FailExternallyContract.State(serviceHub.myInfo.legalIdentities[0]) + override fun newCommand() = FailExternallyContract.Command(failForParty.legalIdentities[0]) + + @Suppress("unused") + @InitiatedBy(FailExternallyFlow::class) + class ReceiverFlow(otherSide: FlowSession) : TestReceiverFlow(otherSide) } - @Suppress("unused") - @InitiatedBy(FailExternallyFlow::class) - class ReceiverFlow(private val otherSide: FlowSession) : FlowLogic() { - @Suspendable - override fun call() { - subFlow(ReceiveFinalityFlow(otherSide)) + + class NewKotlinApiContract : Contract { + override fun verify(tx: LedgerTransaction) { + check(tx.commandsOfType().isNotEmpty()) + // New post-1.2 API which is non-inlined + intArrayOf().maxOrNull() } + + data class State(override val party: Party) : TestState + object Command : TypeOnlyCommandData() + } + + + @StartableByRPC + @InitiatingFlow + class NewKotlinApiFlow(recipient: NodeInfo) : TestFlow(null, recipient) { + override fun newOutput() = NewKotlinApiContract.State(serviceHub.myInfo.legalIdentities[0]) + override fun newCommand() = NewKotlinApiContract.Command + + @Suppress("unused") + @InitiatedBy(NewKotlinApiFlow::class) + class ReceiverFlow(otherSide: FlowSession) : TestReceiverFlow(otherSide) } @@ -216,4 +248,39 @@ class ExternalVerificationTest { return notaryChangeTx!!.id } } + + + abstract class TestFlow( + private val inputState: StateAndRef?, + private val recipient: NodeInfo + ) : FlowLogic>() { + @Suspendable + override fun call(): StateAndRef { + val myParty = serviceHub.myInfo.legalIdentities[0] + val txBuilder = TransactionBuilder(serviceHub.networkMapCache.notaryIdentities[0]) + inputState?.let(txBuilder::addInputState) + txBuilder.addOutputState(newOutput()) + txBuilder.addCommand(newCommand(), myParty.owningKey) + val initialTx = serviceHub.signInitialTransaction(txBuilder) + val sessions = arrayListOf(initiateFlow(recipient.legalIdentities[0])) + inputState?.let { sessions += initiateFlow(it.state.data.party) } + val notarisedTx = subFlow(FinalityFlow(initialTx, sessions)) + return notarisedTx.toLedgerTransaction(serviceHub).outRef(0) + } + + protected abstract fun newOutput(): T + protected abstract fun newCommand(): CommandData + } + + abstract class TestReceiverFlow(private val otherSide: FlowSession) : FlowLogic() { + @Suspendable + override fun call() { + subFlow(ReceiveFinalityFlow(otherSide)) + } + } + + interface TestState : ContractState { + val party: Party + override val participants: List get() = listOf(party) + } } diff --git a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt index f11f5d314d..68b08e951c 100644 --- a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt +++ b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt @@ -42,7 +42,6 @@ import net.corda.core.internal.cordapp.CordappProviderInternal import net.corda.core.internal.messaging.AttachmentTrustInfoRPCOps import net.corda.core.internal.notary.NotaryService import net.corda.core.internal.rootMessage -import net.corda.core.internal.telemetry.OpenTelemetryComponent import net.corda.core.internal.telemetry.SimpleLogTelemetryComponent import net.corda.core.internal.telemetry.TelemetryComponent import net.corda.core.internal.telemetry.TelemetryServiceImpl @@ -167,6 +166,7 @@ import net.corda.nodeapi.internal.persistence.RestrictedEntityManager import net.corda.nodeapi.internal.persistence.SchemaMigration import net.corda.nodeapi.internal.persistence.contextDatabase import net.corda.nodeapi.internal.persistence.withoutDatabaseAccess +import net.corda.nodeapi.internal.telemetry.OpenTelemetryComponent import org.apache.activemq.artemis.utils.ReusableLatch import org.jolokia.jvmagent.JolokiaServer import org.jolokia.jvmagent.JolokiaServerConfig diff --git a/node/src/main/kotlin/net/corda/node/internal/NodeStartup.kt b/node/src/main/kotlin/net/corda/node/internal/NodeStartup.kt index 90eb84d1ed..2bfe9d023b 100644 --- a/node/src/main/kotlin/net/corda/node/internal/NodeStartup.kt +++ b/node/src/main/kotlin/net/corda/node/internal/NodeStartup.kt @@ -276,7 +276,7 @@ open class NodeStartup : NodeStartupLogging { logger.info("Platform Version: ${versionInfo.platformVersion}") logger.info("Revision: ${versionInfo.revision}") val info = ManagementFactory.getRuntimeMXBean() - logger.info("PID: ${info.name.split("@").firstOrNull()}") // TODO Java 9 has better support for this + logger.info("PID: ${ProcessHandle.current().pid()}") logger.info("Main class: ${NodeConfiguration::class.java.location.toURI().path}") logger.info("CommandLine Args: ${info.inputArguments.joinToString(" ")}") // JDK 11 (bootclasspath no longer supported from JDK 9) diff --git a/samples/bank-of-corda-demo/build.gradle b/samples/bank-of-corda-demo/build.gradle index e4297747dd..4917688ab5 100644 --- a/samples/bank-of-corda-demo/build.gradle +++ b/samples/bank-of-corda-demo/build.gradle @@ -1,4 +1,5 @@ -apply plugin: 'java' +import net.corda.plugins.Cordform + apply plugin: 'org.jetbrains.kotlin.jvm' apply plugin: 'idea' apply plugin: 'net.corda.plugins.quasar-utils' @@ -54,10 +55,9 @@ dependencies { testRuntimeOnly "org.junit.platform:junit-platform-launcher:${junit_platform_version}" } -def nodeTask = tasks.getByPath(':node:capsule:assemble') -def webTask = tasks.getByPath(':testing:testserver:testcapsule::assemble') configurations.cordaCordapp.canBeResolved = true -task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar', nodeTask, webTask]) { +tasks.register('deployNodes', Cordform) { + dependsOn('jar', ':node:capsule:assemble', ':testing:testserver:testcapsule::assemble') nodeDefaults { cordapp project(':finance:workflows') cordapp project(':finance:contracts') @@ -65,7 +65,7 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar', nodeTask, } node { name "O=Notary Node,L=Zurich,C=CH" - notary = [validating: true, + notary = [validating : true, serviceLegalName: "O=Notary Service,L=Zurich,C=CH" ] p2pPort 10002 @@ -113,9 +113,9 @@ idea { } } -task runRPCCashIssue(type: JavaExec) { +tasks.register('runRPCCashIssue', JavaExec) { classpath = sourceSets.main.runtimeClasspath - main = 'net.corda.bank.IssueCash' + mainClass = 'net.corda.bank.IssueCash' jvmArgs test_add_opens jvmArgs test_add_exports @@ -131,9 +131,9 @@ task runRPCCashIssue(type: JavaExec) { jvmArgs test_add_exports } -task runWebCashIssue(type: JavaExec) { +tasks.register('runWebCashIssue', JavaExec) { classpath = sourceSets.main.runtimeClasspath - main = 'net.corda.bank.IssueCash' + mainClass = 'net.corda.bank.IssueCash' jvmArgs test_add_opens jvmArgs test_add_exports diff --git a/samples/simm-valuation-demo/contracts-states/build.gradle b/samples/simm-valuation-demo/contracts-states/build.gradle index 777a908910..8601d61dc6 100644 --- a/samples/simm-valuation-demo/contracts-states/build.gradle +++ b/samples/simm-valuation-demo/contracts-states/build.gradle @@ -1,10 +1,15 @@ +apply plugin: 'org.jetbrains.kotlin.jvm' apply plugin: 'net.corda.plugins.cordapp' def javaHome = System.getProperty('java.home') def shrinkJar = file("$buildDir/libs/${project.name}-${project.version}-tiny.jar") -import java.security.NoSuchAlgorithmException + +import net.corda.plugins.SignJar +import proguard.gradle.ProGuardTask + import java.security.MessageDigest +import java.security.NoSuchAlgorithmException static String sha256(File jarFile) throws FileNotFoundException, NoSuchAlgorithmException { InputStream input = new FileInputStream(jarFile) @@ -58,10 +63,10 @@ dependencies { implementation "com.opengamma.strata:strata-market:$strata_version" } -def cordappDependencies = file("${sourceSets['main'].output.resourcesDir}/META-INF/Cordapp-Dependencies") configurations.cordapp.canBeResolved = true -task generateDependencies { +tasks.register('generateDependencies') { dependsOn project(':finance:contracts').tasks.jar + def cordappDependencies = file("${sourceSets.main.output.resourcesDir}/META-INF/Cordapp-Dependencies") inputs.files(configurations.cordapp) outputs.files(cordappDependencies) doLast { @@ -75,11 +80,10 @@ task generateDependencies { processResources.finalizedBy generateDependencies jar { - classifier = 'fat' + archiveClassifier = 'fat' } -import proguard.gradle.ProGuardTask -task shrink(type: ProGuardTask) { +tasks.register('shrink', ProGuardTask) { injars jar outjars shrinkJar @@ -103,18 +107,18 @@ task shrink(type: ProGuardTask) { verbose // These are our CorDapp classes, so don't change these. - keep 'class net.corda.vega.** { *; }', includedescriptorclasses:true + keep 'class net.corda.vega.** { *; }', includedescriptorclasses: true // Until CorDapps are isolated from each other, we need to ensure that the // versions of the classes that this CorDapp needs are still usable by other // CorDapps. Unfortunately, this means that we cannot shrink them as much as // we'd like to. - keepclassmembers 'class com.opengamma.strata.** { *; }', includedescriptorclasses:true - keepclassmembers 'class com.google.** { *; }', includedescriptorclasses:true - keepclassmembers 'class org.joda.** { *; }', includedescriptorclasses:true + keepclassmembers 'class com.opengamma.strata.** { *; }', includedescriptorclasses: true + keepclassmembers 'class com.google.** { *; }', includedescriptorclasses: true + keepclassmembers 'class org.joda.** { *; }', includedescriptorclasses: true } -task sign(type: net.corda.plugins.SignJar) { +tasks.register('sign', SignJar) { inputJars shrink } diff --git a/samples/simm-valuation-demo/flows/build.gradle b/samples/simm-valuation-demo/flows/build.gradle index 01f8d37985..188b30ec45 100644 --- a/samples/simm-valuation-demo/flows/build.gradle +++ b/samples/simm-valuation-demo/flows/build.gradle @@ -1,3 +1,4 @@ +apply plugin: 'org.jetbrains.kotlin.jvm' apply plugin: 'net.corda.plugins.quasar-utils' apply plugin: 'net.corda.plugins.cordapp' diff --git a/serialization-1.2/README.md b/serialization-1.2/README.md new file mode 100644 index 0000000000..58313da830 --- /dev/null +++ b/serialization-1.2/README.md @@ -0,0 +1,5 @@ +This is a Kotlin 1.2 version of the `serialization` module, which is consumed by the `verifier` module, for verifying contracts written in +Kotlin 1.2. This is just a "shell" module which uses the existing the code in `serialization` and compiles it with the 1.2 compiler. + +To allow `serialization` to benefit from new APIs introduced since 1.2, those APIs much be copied into the `core-1.2` module with the same +`kotlin` package. diff --git a/serialization-1.2/build.gradle b/serialization-1.2/build.gradle new file mode 100644 index 0000000000..3893e3490b --- /dev/null +++ b/serialization-1.2/build.gradle @@ -0,0 +1,35 @@ +apply plugin: "corda.kotlin-1.2" +apply plugin: "corda.common-publishing" + +description 'Corda serialization built with Kotlin 1.2' + +sourceSets { + main { + java.srcDir("../serialization/src/main/java") + kotlin.srcDir("../serialization/src/main/kotlin") + } +} + +dependencies { + implementation project(":core-1.2") + // Use the same dependencies as serialization (minus Kotlin and core) + implementation(project(path: ":serialization", configuration: "resolvableImplementation")) { + exclude(module: "core") + exclude(group: "org.jetbrains.kotlin") + } + implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_1_2_version" +} + +jar { + archiveBaseName = 'corda-serialization-1.2' +} + +// TODO Don't publish publicly as it's only needed by the `verifier` module which consumes this into a fat jar. +publishing { + publications { + maven(MavenPublication) { + artifactId 'corda-serialization-1.2' + from components.java + } + } +} diff --git a/serialization/build.gradle b/serialization/build.gradle index 6192d30da2..5eae716e21 100644 --- a/serialization/build.gradle +++ b/serialization/build.gradle @@ -3,28 +3,25 @@ apply plugin: 'corda.common-publishing' description 'Corda serialization' +configurations { + resolvableImplementation.extendsFrom implementation + + testArtifacts.extendsFrom testRuntimeClasspath +} + dependencies { implementation project(":core") - implementation "io.reactivex:rxjava:$rxjava_version" - implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version" - implementation "org.apache.activemq:artemis-commons:${artemis_version}" - implementation "org.ow2.asm:asm:$asm_version" - implementation "com.google.guava:guava:$guava_version" - // For AMQP serialisation. implementation "org.apache.qpid:proton-j:$protonj_version" - // ClassGraph: classpath scanning implementation "io.github.classgraph:classgraph:$class_graph_version" - // Pure-Java Snappy compression implementation "org.iq80.snappy:snappy:$snappy_version" - // For caches rather than guava implementation "com.github.ben-manes.caffeine:caffeine:$caffeine_version" @@ -43,16 +40,12 @@ dependencies { testImplementation "com.fasterxml.jackson.core:jackson-databind:$jackson_version" } -configurations { - testArtifacts.extendsFrom testRuntimeClasspath -} - tasks.withType(Javadoc).configureEach { // We have no public or protected Java classes to document. enabled = false } -task testJar(type: Jar) { +tasks.register('testJar', Jar) { archiveClassifier = 'tests' from sourceSets.test.output } diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/verifier/ExternalVerifierTypes.kt b/serialization/src/main/kotlin/net/corda/serialization/internal/verifier/ExternalVerifierTypes.kt index 61f00b2b98..3dd893dbb5 100644 --- a/serialization/src/main/kotlin/net/corda/serialization/internal/verifier/ExternalVerifierTypes.kt +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/verifier/ExternalVerifierTypes.kt @@ -21,14 +21,14 @@ import java.security.PublicKey typealias SerializedNetworkParameters = SerializedBytes @CordaSerializable -sealed interface ExternalVerifierInbound { +sealed class ExternalVerifierInbound { data class Initialisation( val customSerializerClassNames: Set, val serializationWhitelistClassNames: Set, val customSerializationSchemeClassName: String?, val serializedCurrentNetworkParameters: SerializedNetworkParameters - ) : ExternalVerifierInbound { - val currentNetworkParameters: NetworkParameters by lazy(serializedCurrentNetworkParameters::deserialize) + ) : ExternalVerifierInbound() { + val currentNetworkParameters: NetworkParameters by lazy { serializedCurrentNetworkParameters.deserialize() } override fun toString(): String { return "Initialisation(" + @@ -43,31 +43,31 @@ sealed interface ExternalVerifierInbound { val stx: SignedTransaction, val stxInputsAndReferences: Map, val checkSufficientSignatures: Boolean - ) : ExternalVerifierInbound + ) : ExternalVerifierInbound() - data class PartiesResult(val parties: List) : ExternalVerifierInbound - data class AttachmentResult(val attachment: AttachmentWithTrust?) : ExternalVerifierInbound - data class AttachmentsResult(val attachments: List) : ExternalVerifierInbound - data class NetworkParametersResult(val networkParameters: NetworkParameters?) : ExternalVerifierInbound - data class TrustedClassAttachmentResult(val id: SecureHash?) : ExternalVerifierInbound + data class PartiesResult(val parties: List) : ExternalVerifierInbound() + data class AttachmentResult(val attachment: AttachmentWithTrust?) : ExternalVerifierInbound() + data class AttachmentsResult(val attachments: List) : ExternalVerifierInbound() + data class NetworkParametersResult(val networkParameters: NetworkParameters?) : ExternalVerifierInbound() + data class TrustedClassAttachmentResult(val id: SecureHash?) : ExternalVerifierInbound() } @CordaSerializable data class AttachmentWithTrust(val attachment: Attachment, val isTrusted: Boolean) @CordaSerializable -sealed interface ExternalVerifierOutbound { - sealed interface VerifierRequest : ExternalVerifierOutbound { - data class GetParties(val keys: Set) : VerifierRequest { +sealed class ExternalVerifierOutbound { + sealed class VerifierRequest : ExternalVerifierOutbound() { + data class GetParties(val keys: Set) : VerifierRequest() { override fun toString(): String = "GetParty(keys=${keys.map { it.toStringShort() }}})" } - data class GetAttachment(val id: SecureHash) : VerifierRequest - data class GetAttachments(val ids: Set) : VerifierRequest - data class GetNetworkParameters(val id: SecureHash) : VerifierRequest - data class GetTrustedClassAttachment(val className: String) : VerifierRequest + data class GetAttachment(val id: SecureHash) : VerifierRequest() + data class GetAttachments(val ids: Set) : VerifierRequest() + data class GetNetworkParameters(val id: SecureHash) : VerifierRequest() + data class GetTrustedClassAttachment(val className: String) : VerifierRequest() } - data class VerificationResult(val result: Try) : ExternalVerifierOutbound + data class VerificationResult(val result: Try) : ExternalVerifierOutbound() } fun DataOutputStream.writeCordaSerializable(payload: Any) { diff --git a/settings.gradle b/settings.gradle index 8f2543aebb..5e16f4041c 100644 --- a/settings.gradle +++ b/settings.gradle @@ -42,6 +42,7 @@ include 'confidential-identities' include 'finance:contracts' include 'finance:workflows' include 'core' +include 'core-1.2' include 'core-tests' include 'docs' include 'node-api' @@ -103,6 +104,7 @@ include 'samples:cordapp-configuration:workflows' include 'samples:network-verifier:contracts' include 'samples:network-verifier:workflows' include 'serialization' +include 'serialization-1.2' include 'serialization-tests' include 'testing:cordapps:dbfailure:dbfcontracts' include 'testing:cordapps:dbfailure:dbfworkflows' diff --git a/testing/test-cli/build.gradle b/testing/test-cli/build.gradle index 2aa92314d3..9016202d1d 100644 --- a/testing/test-cli/build.gradle +++ b/testing/test-cli/build.gradle @@ -1,4 +1,3 @@ -apply plugin: 'java' apply plugin: 'org.jetbrains.kotlin.jvm' dependencies { diff --git a/testing/test-common/build.gradle b/testing/test-common/build.gradle index 1a84f6863f..0e59212b70 100644 --- a/testing/test-common/build.gradle +++ b/testing/test-common/build.gradle @@ -1,3 +1,4 @@ +apply plugin: 'org.jetbrains.kotlin.jvm' apply plugin: 'net.corda.plugins.api-scanner' apply plugin: 'corda.common-publishing' diff --git a/testing/testserver/build.gradle b/testing/testserver/build.gradle index f4b029efd4..373b43e41d 100644 --- a/testing/testserver/build.gradle +++ b/testing/testserver/build.gradle @@ -1,5 +1,4 @@ apply plugin: 'org.jetbrains.kotlin.jvm' -apply plugin: 'java' apply plugin: 'corda.common-publishing' description 'Corda node web server' @@ -78,7 +77,7 @@ dependencies { integrationTestImplementation project(':node-driver') } -task integrationTest(type: Test) { +tasks.register('integrationTest', Test) { testClassesDirs = sourceSets.integrationTest.output.classesDirs classpath = sourceSets.integrationTest.runtimeClasspath diff --git a/tools/blobinspector/build.gradle b/tools/blobinspector/build.gradle index cdda5e9a7e..a28cf20f6d 100644 --- a/tools/blobinspector/build.gradle +++ b/tools/blobinspector/build.gradle @@ -1,4 +1,3 @@ -apply plugin: 'java' apply plugin: 'org.jetbrains.kotlin.jvm' apply plugin: 'corda.common-publishing' diff --git a/tools/cliutils/build.gradle b/tools/cliutils/build.gradle index c07abdf9b1..a4463a41d9 100644 --- a/tools/cliutils/build.gradle +++ b/tools/cliutils/build.gradle @@ -1,4 +1,3 @@ -apply plugin: 'java' apply plugin: 'org.jetbrains.kotlin.jvm' apply plugin: 'corda.common-publishing' diff --git a/tools/demobench/build.gradle b/tools/demobench/build.gradle index 9b5e536ff2..c898246854 100644 --- a/tools/demobench/build.gradle +++ b/tools/demobench/build.gradle @@ -27,7 +27,6 @@ ext { pkg_macosxKeyUserName = 'R3CEV' } -apply plugin: 'java' apply plugin: 'org.jetbrains.kotlin.jvm' apply plugin: 'application' @@ -156,7 +155,8 @@ distributions { * Bundles the application using JavaPackager, * using the ZIP distribution as source. */ -task javapackage(dependsOn: distZip) { +tasks.register('javapackage') { + dependsOn distZip doLast { delete([pkg_source, pkg_outDir]) @@ -190,15 +190,14 @@ task javapackage(dependsOn: distZip) { include '**/*.manifest' } filter { line -> - line.replaceAll('@pkg_version@', pkg_version) - .replaceAll('@signingKeyUserName@', pkg_macosxKeyUserName) + line.replaceAll('@pkg_version@', pkg_version).replaceAll('@signingKeyUserName@', pkg_macosxKeyUserName) } into "$pkg_source/package" } ant.taskdef( - resource: 'com/sun/javafx/tools/ant/antlib.xml', - classpath: "$pkg_source:$java_home/../lib/ant-javafx.jar" + resource: 'com/sun/javafx/tools/ant/antlib.xml', + classpath: "$pkg_source:$java_home/../lib/ant-javafx.jar" ) ant.deploy(nativeBundles: packageType, outdir: pkg_outDir, outfile: 'DemoBench', verbose: 'true') { diff --git a/tools/network-builder/build.gradle b/tools/network-builder/build.gradle index dc72c205d8..d587af84ee 100644 --- a/tools/network-builder/build.gradle +++ b/tools/network-builder/build.gradle @@ -21,7 +21,6 @@ ext { apply plugin: 'org.jetbrains.kotlin.jvm' apply plugin: 'idea' -apply plugin: 'java' apply plugin: 'application' // We need to set mainClassName before applying the shadow plugin. mainClassName = 'net.corda.networkbuilder.Main' diff --git a/verifier/README.md b/verifier/README.md new file mode 100644 index 0000000000..7427a3f7f9 --- /dev/null +++ b/verifier/README.md @@ -0,0 +1,6 @@ +This is the external verifier process, which the node kicks off when it needs to verify transactions which itself can't. This will be mainly +due to differences in the Kotlin version used in the transaction contract compared to the Kotlin version used by the node. + +This module is built with Kotlin 1.2 and so is only able to verify transactions which have contracts compiled with Kotlin 1.2. It relies on +specially compiled versions of `core` and `serialization` also compiled with Kotlin 1.2 (`core-1.2` and `serialization-1.2` respectively) +to ensure compatibility. diff --git a/verifier/build.gradle b/verifier/build.gradle index e794026070..b583b39ece 100644 --- a/verifier/build.gradle +++ b/verifier/build.gradle @@ -1,5 +1,5 @@ plugins { - id "org.jetbrains.kotlin.jvm" + id "corda.kotlin-1.2" id "application" id "com.github.johnrengelman.shadow" } @@ -9,12 +9,12 @@ application { } dependencies { - implementation project(":core") - implementation project(":serialization") + implementation project(":core-1.2") + implementation project(":serialization-1.2") implementation "com.github.ben-manes.caffeine:caffeine:$caffeine_version" implementation "org.slf4j:jul-to-slf4j:$slf4j_version" - runtimeOnly "org.apache.logging.log4j:log4j-slf4j-impl:${log4j_version}" + runtimeOnly "org.apache.logging.log4j:log4j-slf4j-impl:$log4j_version" } jar { diff --git a/verifier/src/main/kotlin/net/corda/verifier/ExternalVerifier.kt b/verifier/src/main/kotlin/net/corda/verifier/ExternalVerifier.kt index 5b10672c38..902d03d0be 100644 --- a/verifier/src/main/kotlin/net/corda/verifier/ExternalVerifier.kt +++ b/verifier/src/main/kotlin/net/corda/verifier/ExternalVerifier.kt @@ -135,7 +135,7 @@ class ExternalVerifier( private fun verifyTransaction(request: VerificationRequest) { val verificationContext = ExternalVerificationContext(appClassLoader, attachmentsClassLoaderCache, this, request.stxInputsAndReferences) - val result = try { + val result: Try = try { request.stx.verifyInternal(verificationContext, request.checkSufficientSignatures) log.info("${request.stx} verified") Try.Success(Unit) @@ -213,7 +213,7 @@ class ExternalVerifier( override fun rpcServerSerializerFactory(context: SerializationContext) = throw UnsupportedOperationException() companion object { - inline fun Set?.load(classLoader: ClassLoader?): Set { + inline fun Set?.load(classLoader: ClassLoader?): Set { return this?.mapToSet { loadClassOfType(it, classLoader = classLoader).kotlin.objectOrNewInstance() } ?: emptySet() } }