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.
This commit is contained in:
Shams Asari 2024-01-02 17:02:20 +00:00 committed by GitHub
parent 4791f0d84f
commit 406f7ff292
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
49 changed files with 585 additions and 257 deletions

View File

@ -245,7 +245,7 @@ logger.lifecycle("Building Corda version: {}", corda_release_version)
logger.lifecycle("User home: {}", System.getProperty('user.home')) logger.lifecycle("User home: {}", System.getProperty('user.home'))
allprojects { allprojects {
apply plugin: 'org.jetbrains.kotlin.jvm' apply plugin: 'java'
apply plugin: 'kotlin-allopen' apply plugin: 'kotlin-allopen'
apply plugin: 'jacoco' apply plugin: 'jacoco'
apply plugin: 'org.owasp.dependencycheck' apply plugin: 'org.owasp.dependencycheck'
@ -437,10 +437,12 @@ allprojects {
} }
configurations { configurations {
all { configureEach {
resolutionStrategy { resolutionStrategy {
if (pluginManager.hasPlugin("org.jetbrains.kotlin.jvm")) {
// Force dependencies to use the same version of Kotlin as Corda. // Force dependencies to use the same version of Kotlin as Corda.
force "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version" force "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
}
// Force dependencies to use the same version of Guava as Corda. // Force dependencies to use the same version of Guava as Corda.
force "com.google.guava:guava:$guava_version" force "com.google.guava:guava:$guava_version"
@ -487,9 +489,13 @@ allprojects {
// Effectively delete this unused and unwanted transitive dependency of Artemis. // Effectively delete this unused and unwanted transitive dependency of Artemis.
substitute module('org.jgroups:jgroups') with module("org.apache.activemq:artemis-commons:$artemis_version") substitute module('org.jgroups:jgroups') with module("org.apache.activemq:artemis-commons:$artemis_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, // Select all of the compileClasspath and runtimeClasspath etc configurations,
// but NOT the "classpath" configuration, as that is used by the Gradle plugins. // but NOT the "classpath" configuration, as that is used by the Gradle plugins.
matching { it.name.endsWith("Classpath") }.configureEach { cfg -> matching { it.name.endsWith("Classpath") }.configureEach { cfg ->
@ -500,9 +506,7 @@ allprojects {
substitute module('org.jetbrains.kotlin:kotlin-stdlib') with module("org.jetbrains.kotlin:kotlin-stdlib:$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") 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'
} }
} }
} }
@ -557,9 +561,9 @@ tasks.register('jacocoRootReport', JacocoReport) {
// classDirectories = files(subprojects.sourceSets.main.output) // classDirectories = files(subprojects.sourceSets.main.output)
// executionData = files(subprojects.jacocoTestReport.executionData) // executionData = files(subprojects.jacocoTestReport.executionData)
reports { reports {
html.enabled = true html.required = true
xml.enabled = true xml.required = true
csv.enabled = false csv.required = false
} }
onlyIf = { onlyIf = {
true true
@ -595,7 +599,7 @@ tasks.register('detektBaseline', JavaExec) {
} }
tasks.withType(Test).configureEach { tasks.withType(Test).configureEach {
reports.html.destination = file("${reporting.baseDir}/${name}") reports.html.outputLocation.set(file("${reporting.baseDir}/${name}"))
} }
tasks.register('testReport', TestReport) { tasks.register('testReport', TestReport) {

View File

@ -46,8 +46,9 @@ repositories {
} }
dependencies { dependencies {
implementation group: 'com.github.docker-java', name: 'docker-java', version: constants.dockerJavaVersion implementation "com.github.docker-java:docker-java:$constants.dockerJavaVersion"
implementation group: 'com.github.docker-java', name: 'docker-java-transport-httpclient5', version: 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')) { if (System.getenv('CORDA_ARTIFACTORY_USERNAME') != null || project.hasProperty('cordaArtifactoryUsername')) {
implementation "com.r3.internal.gradle.plugins:publish:$internalPublishVersion" implementation "com.r3.internal.gradle.plugins:publish:$internalPublishVersion"

View File

@ -0,0 +1,91 @@
import org.gradle.api.internal.file.DefaultSourceDirectorySet
import static org.joor.Reflect.onClass
pluginManager.apply(Kotlin12Plugin.class)
// We cannot use the 1.2 Kotlin plugin as it only works with a very old version of Gradle, which itself will only work on Java 8. So we need
// our own plugin which calls the 1.2 compiler directly.
class Kotlin12Plugin implements Plugin<Project> {
private static final KOTLIN_VERSION = "1.2.71"
@Override
void apply(Project project) {
project.pluginManager.apply(JavaPlugin.class)
project.extensions.add("kotlin_1_2_version", KOTLIN_VERSION)
project.dependencies.add("implementation", "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$KOTLIN_VERSION")
def kotlinCompilerConfiguration = project.configurations.create("kotlinCompiler")
project.dependencies.add("kotlinCompiler", "org.jetbrains.kotlin:kotlin-compiler:$KOTLIN_VERSION")
project.extensions.getByType(JavaPluginExtension.class).sourceSets.configureEach { sourceSet ->
// Create the "src/*/kotlin" SourceDirectorySet, alongside the "java" one
def kotlinSourceDirectorySet = new DefaultSourceDirectorySet(project.objects.sourceDirectorySet("kotlin", "${sourceSet.displayName} Kotlin source"))
sourceSet.extensions.add(SourceDirectorySet.class, "kotlin", kotlinSourceDirectorySet)
kotlinSourceDirectorySet.filter.include("**/*.java", "**/*.kt")
kotlinSourceDirectorySet.srcDir(project.file("src/${sourceSet.name}/kotlin"))
def allKotlin = project.objects.sourceDirectorySet("allkotlin", "${sourceSet.displayName} Kotlin source")
allKotlin.filter.include("**/*.kt")
allKotlin.source(kotlinSourceDirectorySet)
sourceSet.allJava.source(kotlinSourceDirectorySet)
sourceSet.allSource.source(kotlinSourceDirectorySet)
def kotlinBuildDir = project.layout.buildDirectory.dir("classes/kotlin/${sourceSet.name}")
sourceSet.output.dir(kotlinBuildDir)
def taskSourceSetName = isMain(sourceSet) ? "" : sourceSet.name.capitalize()
def compileKotlin = project.tasks.register("compile${taskSourceSetName}Kotlin", KotlinCompile.class) { task ->
// The 1.2 compiler needs to be laoded in a separate class loader, as the build classpath already contains its own version
// of Kotlin.
task.compilerClasspath.from(kotlinCompilerConfiguration)
task.source(allKotlin)
// Paradoxically, the Java sources are also required by the Kotlin compiler. This is actually so that it can correctly
// resolve any references the Kotlin code makes to Java code.
task.source(sourceSet.allJava)
task.classpath = sourceSet.compileClasspath
task.destinationDirectory = kotlinBuildDir
}
// Compiling the Java code needs the compiled Kotlin code first
project.tasks.named("compile${taskSourceSetName}Java", JavaCompile.class) { task ->
task.classpath += project.files(compileKotlin.map { it.destinationDirectory })
}
}
}
}
abstract class KotlinCompile extends AbstractCompile {
@Classpath
abstract ConfigurableFileCollection getCompilerClasspath()
@TaskAction
void compile() {
def args = [
"-jvm-target", "1.8",
"-language-version", "1.2",
"-api-version", "1.2",
"-java-parameters",
"-Xjvm-default=compatibility",
"-no-stdlib",
"-Xallow-kotlin-package", // We may have copies of stdlib APIs (see `core-1.2`)
"-cp", classpath.asPath,
"-d", destinationDirectory.get().asFile.absolutePath
]
args.addAll(source.collect { it.absolutePath })
logger.info("args: {}", args)
def compilerClassLoader = new URLClassLoader(compilerClasspath.collect { it.toURI().toURL() } as URL[])
def exitCode = onClass("org.jetbrains.kotlin.cli.jvm.K2JVMCompiler", compilerClassLoader)
.create()
.call("exec", System.err, args as String[])
.get()
if (exitCode.toString() != "OK") {
throw new GradleException("Compilation error. See log for more details")
}
}
}

View File

@ -1,4 +1,3 @@
apply plugin: 'java'
apply plugin: 'org.jetbrains.kotlin.jvm' apply plugin: 'org.jetbrains.kotlin.jvm'
apply plugin: 'net.corda.plugins.api-scanner' apply plugin: 'net.corda.plugins.api-scanner'
apply plugin: 'corda.common-publishing' apply plugin: 'corda.common-publishing'

View File

@ -1,14 +1,15 @@
package net.corda.client.rpc.internal 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.SimpleLogTelemetryComponent
import net.corda.core.internal.telemetry.TelemetryServiceImpl import net.corda.core.internal.telemetry.TelemetryServiceImpl
import net.corda.core.utilities.contextLogger import net.corda.core.utilities.contextLogger
import net.corda.nodeapi.internal.telemetry.OpenTelemetryComponent
class RPCClientTelemetry(val serviceName: String, val openTelemetryEnabled: Boolean, class RPCClientTelemetry(serviceName: String,
val simpleLogTelemetryEnabled: Boolean, val spanStartEndEventsEnabled: Boolean, val openTelemetryEnabled: Boolean,
val simpleLogTelemetryEnabled: Boolean,
val spanStartEndEventsEnabled: Boolean,
val copyBaggageToTags: Boolean) { val copyBaggageToTags: Boolean) {
companion object { companion object {
private val log = contextLogger() private val log = contextLogger()
} }

View File

@ -1,5 +1,6 @@
// This contains the SwapIdentitiesFlow which can be used for exchanging confidential identities as part of a flow. // This contains the SwapIdentitiesFlow which can be used for exchanging confidential identities as part of a flow.
// TODO: Merge this into core: the original plan was to develop it independently but in practice it's too widely used to break compatibility now, as finance uses it. // TODO: Merge this into core: the original plan was to develop it independently but in practice it's too widely used to break compatibility now, as finance uses it.
apply plugin: 'org.jetbrains.kotlin.jvm'
apply plugin: 'net.corda.plugins.quasar-utils' apply plugin: 'net.corda.plugins.quasar-utils'
apply plugin: 'net.corda.plugins.cordapp' apply plugin: 'net.corda.plugins.cordapp'
apply plugin: 'corda.common-publishing' apply plugin: 'corda.common-publishing'

View File

@ -100,3 +100,4 @@ controlsfxVersion=8.40.15
fontawesomefxCommonsVersion=11.0 fontawesomefxCommonsVersion=11.0
fontawesomefxFontawesomeVersion=4.7.0-11 fontawesomefxFontawesomeVersion=4.7.0-11
javaassistVersion=3.29.2-GA javaassistVersion=3.29.2-GA
joorVersion=0.9.15

4
core-1.2/README.md Normal file
View File

@ -0,0 +1,4 @@
This is a Kotlin 1.2 version of the `core` module, which is consumed by the `verifier` module, for verifying contracts written in Kotlin
1.2. This is just a "shell" module which uses the existing the code in `core` and compiles it with the 1.2 compiler.
To allow `core` to benefit from new APIs introduced since 1.2, those APIs much be copied into this module with the same `kotlin` package.

34
core-1.2/build.gradle Normal file
View File

@ -0,0 +1,34 @@
apply plugin: "corda.kotlin-1.2"
apply plugin: "corda.common-publishing"
description 'Corda core built with Kotlin 1.2'
sourceSets {
main {
java.srcDir("../core/src/main/java")
kotlin.srcDir("../core/src/main/kotlin")
}
}
dependencies {
// Use the same dependencies as core (minus Kotlin)
implementation(project(path: ":core", configuration: "resolvableImplementation")) {
exclude(group: "org.jetbrains.kotlin")
}
implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_1_2_version"
implementation "co.paralleluniverse:quasar-core:$quasar_version"
}
jar {
archiveBaseName = 'corda-core-1.2'
}
// TODO Don't publish publicly as it's only needed by the `verifier` module which consumes this into a fat jar.
publishing {
publications {
maven(MavenPublication) {
artifactId 'corda-core-1.2'
from components.java
}
}
}

View File

@ -0,0 +1,37 @@
// Implement the new post-1.2 APIs which are used by core and serialization
@file:Suppress("unused", "MagicNumber", "INVISIBLE_MEMBER")
package kotlin.collections
inline fun <K, V> Iterable<K>.associateWith(valueSelector: (K) -> V): Map<K, V> {
val result = LinkedHashMap<K, V>(mapCapacity(if (this is Collection<*>) size else 10).coerceAtLeast(16))
return associateWithTo(result, valueSelector)
}
inline fun <K, V, M : MutableMap<in K, in V>> Iterable<K>.associateWithTo(destination: M, valueSelector: (K) -> V): M {
for (element in this) {
destination.put(element, valueSelector(element))
}
return destination
}
inline fun <T> Iterable<T>.sumOf(selector: (T) -> Int): Int {
var sum = 0
for (element in this) {
sum += selector(element)
}
return sum
}
inline fun <T, R : Comparable<R>> Iterable<T>.maxOf(selector: (T) -> R): R {
val iterator = iterator()
if (!iterator.hasNext()) throw NoSuchElementException()
var maxValue = selector(iterator.next())
while (iterator.hasNext()) {
val v = selector(iterator.next())
if (maxValue < v) {
maxValue = v
}
}
return maxValue
}

View File

@ -0,0 +1,37 @@
// Implement the new post-1.2 APIs which are used by core and serialization
@file:Suppress("unused", "SpreadOperator", "NOTHING_TO_INLINE")
package kotlin.io.path
import java.io.InputStream
import java.io.OutputStream
import java.nio.file.Files
import java.nio.file.LinkOption
import java.nio.file.OpenOption
import java.nio.file.Path
import java.nio.file.attribute.FileAttribute
inline operator fun Path.div(other: String): Path = this.resolve(other)
fun Path.listDirectoryEntries(glob: String = "*"): List<Path> = Files.newDirectoryStream(this, glob).use { it.toList() }
inline fun Path.createDirectories(vararg attributes: FileAttribute<*>): Path = Files.createDirectories(this, *attributes)
inline fun Path.deleteIfExists(): Boolean = Files.deleteIfExists(this)
inline fun Path.exists(vararg options: LinkOption): Boolean = Files.exists(this, *options)
inline fun Path.inputStream(vararg options: OpenOption): InputStream = Files.newInputStream(this, *options)
inline fun Path.outputStream(vararg options: OpenOption): OutputStream = Files.newOutputStream(this, *options)
inline fun Path.isDirectory(vararg options: LinkOption): Boolean = Files.isDirectory(this, *options)
inline fun Path.isSymbolicLink(): Boolean = Files.isSymbolicLink(this)
inline fun Path.readSymbolicLink(): Path = Files.readSymbolicLink(this)
val Path.name: String
get() = fileName?.toString().orEmpty()
inline fun Path.readBytes(): ByteArray = Files.readAllBytes(this)

View File

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

View File

@ -11,68 +11,51 @@ sourceSets {
} }
configurations { configurations {
resolvableImplementation.extendsFrom implementation
integrationTestImplementation.extendsFrom testImplementation integrationTestImplementation.extendsFrom testImplementation
integrationTestRuntimeOnly.extendsFrom testRuntimeOnly integrationTestRuntimeOnly.extendsFrom testRuntimeOnly
smokeTestCompile.extendsFrom compile smokeTestCompile.extendsFrom compile
smokeTestRuntimeOnly.extendsFrom runtimeOnly smokeTestRuntimeOnly.extendsFrom runtimeOnly
testArtifacts.extendsFrom(testRuntimeClasspath)
} }
dependencies { 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" 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 // SLF4J: commons-logging bindings for a SLF4J back end
implementation "org.slf4j:jcl-over-slf4j:$slf4j_version" implementation "org.slf4j:jcl-over-slf4j:$slf4j_version"
implementation "org.slf4j:slf4j-api:$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. // Guava: Google utilities library.
implementation "com.google.guava:guava:$guava_version" implementation "com.google.guava:guava:$guava_version"
// For caches rather than guava // For caches rather than guava
implementation "com.github.ben-manes.caffeine:caffeine:$caffeine_version" implementation "com.github.ben-manes.caffeine:caffeine:$caffeine_version"
// RxJava: observable streams of events. // RxJava: observable streams of events.
implementation "io.reactivex:rxjava:$rxjava_version" implementation "io.reactivex:rxjava:$rxjava_version"
implementation "org.apache.commons:commons-lang3:$commons_lang3_version" implementation "org.apache.commons:commons-lang3:$commons_lang3_version"
// Java ed25519 implementation. See https://github.com/str4d/ed25519-java/ // Java ed25519 implementation. See https://github.com/str4d/ed25519-java/
implementation "net.i2p.crypto:eddsa:$eddsa_version" implementation "net.i2p.crypto:eddsa:$eddsa_version"
// Bouncy castle support needed for X509 certificate manipulation // Bouncy castle support needed for X509 certificate manipulation
implementation "org.bouncycastle:bcprov-jdk18on:${bouncycastle_version}" implementation "org.bouncycastle:bcprov-jdk18on:${bouncycastle_version}"
testImplementation "org.bouncycastle:bcpkix-jdk18on:${bouncycastle_version}"
// required to use @Type annotation // required to use @Type annotation
implementation "org.hibernate:hibernate-core:$hibernate_version" implementation "org.hibernate:hibernate-core:$hibernate_version"
// FastThreadLocal // FastThreadLocal
implementation "io.netty:netty-common:$netty_version" 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" testImplementation "org.ow2.asm:asm:$asm_version"
// JDK11: required by Quasar at run-time // JDK11: required by Quasar at run-time
@ -103,10 +86,6 @@ jar {
} }
} }
configurations {
testArtifacts.extendsFrom testRuntimeClasspath
}
processTestResources { processTestResources {
inputs.files(jar) inputs.files(jar)
into("zip") { into("zip") {

View File

@ -1,3 +1,5 @@
@file:Suppress("MagicNumber")
package net.corda.core.internal package net.corda.core.internal
import net.corda.core.crypto.Crypto import net.corda.core.crypto.Crypto
@ -34,6 +36,7 @@ import java.nio.ByteBuffer
import java.nio.file.CopyOption import java.nio.file.CopyOption
import java.nio.file.Files import java.nio.file.Files
import java.nio.file.Path import java.nio.file.Path
import java.nio.file.Paths
import java.security.KeyPair import java.security.KeyPair
import java.security.MessageDigest import java.security.MessageDigest
import java.security.PrivateKey import java.security.PrivateKey
@ -60,6 +63,8 @@ import java.util.Spliterator.SUBSIZED
import java.util.Spliterators import java.util.Spliterators
import java.util.concurrent.ExecutorService import java.util.concurrent.ExecutorService
import java.util.concurrent.TimeUnit 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
import java.util.stream.Collectors.toCollection import java.util.stream.Collectors.toCollection
import java.util.stream.IntStream import java.util.stream.IntStream
@ -68,7 +73,6 @@ import java.util.stream.StreamSupport
import java.util.zip.Deflater import java.util.zip.Deflater
import java.util.zip.ZipEntry import java.util.zip.ZipEntry
import java.util.zip.ZipOutputStream import java.util.zip.ZipOutputStream
import kotlin.io.path.toPath
import kotlin.math.roundToLong import kotlin.math.roundToLong
import kotlin.reflect.KClass import kotlin.reflect.KClass
import kotlin.reflect.full.createInstance import kotlin.reflect.full.createInstance
@ -132,14 +136,17 @@ fun <T> List<T>.indexOfOrThrow(item: T): Int {
/** /**
* Similar to [Iterable.map] except it maps to a [Set] which preserves the iteration order. * 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 <T, R> Iterable<T>.mapToSet(transform: (T) -> R): Set<R> { inline fun <T, R> Iterable<T>.mapToSet(transform: (T) -> R): Set<R> {
if (this is Collection) { return if (this is Collection) {
when (size) { when (size) {
0 -> return emptySet() 0 -> return emptySet()
1 -> return setOf(transform(first())) 1 -> return setOf(transform(first()))
else -> mapTo(LinkedHashSet<R>(mapCapacity(size)), transform)
} }
} else {
mapTo(LinkedHashSet<R>(), transform)
} }
return mapTo(LinkedHashSet(), transform)
} }
/** /**
@ -176,6 +183,8 @@ fun InputStream.hash(): SecureHash {
inline fun <reified T : Any> InputStream.readObject(): T = readFully().deserialize() inline fun <reified T : Any> InputStream.readObject(): T = readFully().deserialize()
fun JarInputStream.entries(): Sequence<JarEntry> = generateSequence(nextJarEntry) { nextJarEntry }
fun String.abbreviate(maxWidth: Int): String = if (length <= maxWidth) this else "${take(maxWidth - 1)}" fun String.abbreviate(maxWidth: Int): String = if (length <= maxWidth) this else "${take(maxWidth - 1)}"
/** /**
@ -370,17 +379,10 @@ class DeclaredField<T>(clazz: Class<*>, name: String, private val receiver: Any?
val name: String = javaField.name val name: String = javaField.name
private fun <RESULT> Field.accessible(action: Field.() -> RESULT): RESULT { private fun <RESULT> Field.accessible(action: Field.() -> RESULT): RESULT {
@Suppress("DEPRECATION") // JDK11: isAccessible() should be replaced with canAccess() (since 9)
val accessible = isAccessible
isAccessible = true isAccessible = true
try {
return action(this) return action(this)
} finally {
isAccessible = accessible
}
} }
@Throws(NoSuchFieldException::class)
private fun findField(fieldName: String, clazz: Class<*>?): Field { private fun findField(fieldName: String, clazz: Class<*>?): Field {
if (clazz == null) { if (clazz == null) {
throw NoSuchFieldException(fieldName) 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) 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_CONNECT_TIMEOUT = 30.seconds.toMillis()
val DEFAULT_HTTP_READ_TIMEOUT = 30.seconds.toMillis() val DEFAULT_HTTP_READ_TIMEOUT = 30.seconds.toMillis()

View File

@ -57,8 +57,8 @@ object JarSignatureCollector {
return firstSignerSet return firstSignerSet
} }
private val JarInputStream.fileSignerSets: List<Pair<String, Set<CodeSigner>>> get() = private val JarInputStream.fileSignerSets: List<Pair<String, Set<CodeSigner>>>
entries.thatAreSignable.shreddedFrom(this).toFileSignerSet().toList() get() = entries().thatAreSignable.shreddedFrom(this).toFileSignerSet().toList()
private val Sequence<JarEntry>.thatAreSignable: Sequence<JarEntry> get() = filterNot { isNotSignable(it) } private val Sequence<JarEntry>.thatAreSignable: Sequence<JarEntry> get() = filterNot { isNotSignable(it) }
@ -85,8 +85,6 @@ object JarSignatureCollector {
private fun Set<CodeSigner>.toCertificates(): List<X509Certificate> = map { private fun Set<CodeSigner>.toCertificates(): List<X509Certificate> = map {
it.signerCertPath.certificates[0] as X509Certificate it.signerCertPath.certificates[0] as X509Certificate
}.sortedBy { it.toString() } // Sorted for determinism. }.sortedBy { it.toString() } // Sorted for determinism.
private val JarInputStream.entries get(): Sequence<JarEntry> = generateSequence(nextJarEntry) { nextJarEntry }
} }
class InvalidJarSignersException(msg: String) : Exception(msg) class InvalidJarSignersException(msg: String) : Exception(msg)

View File

@ -56,7 +56,7 @@ class AttachmentFixups {
private fun parseIds(ids: String): Set<AttachmentId> { private fun parseIds(ids: String): Set<AttachmentId> {
return ids.splitToSequence(",") return ids.splitToSequence(",")
.map(String::trim) .map(String::trim)
.filterNot(String::isEmpty) .filter { it.isNotEmpty() }
.mapTo(LinkedHashSet(), SecureHash.Companion::create) .mapTo(LinkedHashSet(), SecureHash.Companion::create)
} }

View File

@ -9,6 +9,7 @@ import net.corda.core.identity.Party
import net.corda.core.internal.AttachmentTrustCalculator import net.corda.core.internal.AttachmentTrustCalculator
import net.corda.core.internal.SerializedTransactionState import net.corda.core.internal.SerializedTransactionState
import net.corda.core.internal.TRUSTED_UPLOADERS import net.corda.core.internal.TRUSTED_UPLOADERS
import net.corda.core.internal.entries
import net.corda.core.internal.getRequiredTransaction import net.corda.core.internal.getRequiredTransaction
import net.corda.core.node.NetworkParameters import net.corda.core.node.NetworkParameters
import net.corda.core.node.services.AttachmentStorage 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.NotaryChangeWireTransaction
import net.corda.core.transactions.WireTransaction import net.corda.core.transactions.WireTransaction
import java.security.PublicKey import java.security.PublicKey
import java.util.jar.JarInputStream
/** /**
* Implements [VerificationSupport] in terms of node-based services. * Implements [VerificationSupport] in terms of node-based services.
@ -135,19 +135,12 @@ interface VerificationService : VerificationSupport {
// TODO - add caching if performance is affected. // TODO - add caching if performance is affected.
for (attId in allTrusted) { for (attId in allTrusted) {
val attch = attachmentStorage.openAttachment(attId)!! val attch = attachmentStorage.openAttachment(attId)!!
if (attch.openAsJAR().use { hasFile(it, "$className.class") }) return attch if (attch.hasFile("$className.class")) return attch
} }
return null return null
} }
private fun hasFile(jarStream: JarInputStream, className: String): Boolean { private fun Attachment.hasFile(className: String): Boolean = openAsJAR().use { it.entries().any { entry -> entry.name == className } }
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 isAttachmentTrusted(attachment: Attachment): Boolean = attachmentTrustCalculator.calculate(attachment)

View File

@ -39,7 +39,8 @@ interface VerifyingServiceHub : ServiceHub, VerificationService {
private fun loadContractAttachment(stateRef: StateRef, forContractClassName: String?): Attachment { private fun loadContractAttachment(stateRef: StateRef, forContractClassName: String?): Attachment {
val stx = getRequiredTransaction(stateRef.txhash) val stx = getRequiredTransaction(stateRef.txhash)
return when (val ctx = stx.coreTransaction) { val ctx = stx.coreTransaction
return when (ctx) {
is WireTransaction -> { is WireTransaction -> {
val contractClassName = forContractClassName ?: ctx.outRef<ContractState>(stateRef.index).state.contract val contractClassName = forContractClassName ?: ctx.outRef<ContractState>(stateRef.index).state.contract
ctx.attachments ctx.attachments

View File

@ -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 * 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). * [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 @MappedSuperclass
@CordaSerializable @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. * Embedded [StateRef] representation used in state mapping.

View File

@ -17,6 +17,7 @@ import net.corda.core.internal.VisibleForTesting
import net.corda.core.internal.cordapp.targetPlatformVersion import net.corda.core.internal.cordapp.targetPlatformVersion
import net.corda.core.internal.createInstancesOfClassesImplementing import net.corda.core.internal.createInstancesOfClassesImplementing
import net.corda.core.internal.createSimpleCache import net.corda.core.internal.createSimpleCache
import net.corda.core.internal.entries
import net.corda.core.internal.toSynchronised import net.corda.core.internal.toSynchronised
import net.corda.core.node.NetworkParameters import net.corda.core.node.NetworkParameters
import net.corda.core.serialization.AMQP_ENVELOPE_CACHE_INITIAL_CAPACITY 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.WeakHashMap
import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.atomic.AtomicLong import java.util.concurrent.atomic.AtomicLong
import java.util.function.Function
import kotlin.collections.component1 import kotlin.collections.component1
import kotlin.collections.component2 import kotlin.collections.component2
import kotlin.collections.set import kotlin.collections.set
@ -170,13 +170,7 @@ class AttachmentsClassLoader(attachments: List<Attachment>,
} }
private fun containsClasses(attachment: Attachment): Boolean { private fun containsClasses(attachment: Attachment): Boolean {
attachment.openAsJAR().use { jar -> return attachment.openAsJAR().use { it.entries().any { entry -> entry.name.endsWith(".class", ignoreCase = true) } }
while (true) {
val entry = jar.nextJarEntry ?: return false
if (entry.name.endsWith(".class", ignoreCase = true)) return true
}
}
return false
} }
// This function attempts to strike a balance between security and usability when it comes to the no-overlap rule. // This function attempts to strike a balance between security and usability when it comes to the no-overlap rule.
@ -412,7 +406,7 @@ object AttachmentURLStreamHandlerFactory : URLStreamHandlerFactory {
@Synchronized @Synchronized
fun toUrl(attachment: Attachment): URL { 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 loadedAttachments[uniqueURL] = attachment
return uniqueURL return uniqueURL
} }
@ -429,12 +423,12 @@ object AttachmentURLStreamHandlerFactory : URLStreamHandlerFactory {
override fun equals(attachmentUrl: URL, otherURL: URL?): Boolean { override fun equals(attachmentUrl: URL, otherURL: URL?): Boolean {
if (attachmentUrl.protocol != otherURL?.protocol) return false 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 return attachmentUrl.file == otherURL?.file
} }
override fun hashCode(url: URL): Int { 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() return url.file.hashCode()
} }
} }
@ -466,7 +460,10 @@ private class AttachmentsHolderImpl : AttachmentsHolder {
} }
interface AttachmentsClassLoaderCache { interface AttachmentsClassLoaderCache {
fun computeIfAbsent(key: AttachmentsClassLoaderKey, mappingFunction: Function<in AttachmentsClassLoaderKey, out SerializationContext>): SerializationContext fun computeIfAbsent(
key: AttachmentsClassLoaderKey,
mappingFunction: (AttachmentsClassLoaderKey) -> SerializationContext
): SerializationContext
} }
class AttachmentsClassLoaderCacheImpl(cacheFactory: NamedCacheFactory) : SingletonSerializeAsToken(), AttachmentsClassLoaderCache { class AttachmentsClassLoaderCacheImpl(cacheFactory: NamedCacheFactory) : SingletonSerializeAsToken(), AttachmentsClassLoaderCache {
@ -514,18 +511,23 @@ class AttachmentsClassLoaderCacheImpl(cacheFactory: NamedCacheFactory) : Singlet
}, "AttachmentsClassLoader_cache" }, "AttachmentsClassLoader_cache"
) )
override fun computeIfAbsent(key: AttachmentsClassLoaderKey, mappingFunction: Function<in AttachmentsClassLoaderKey, out SerializationContext>): SerializationContext { override fun computeIfAbsent(
key: AttachmentsClassLoaderKey,
mappingFunction: (AttachmentsClassLoaderKey) -> SerializationContext
): SerializationContext {
purgeExpiryQueue() purgeExpiryQueue()
return cache.get(key, mappingFunction) ?: throw NullPointerException("null returned from cache mapping function") return cache.get(key, mappingFunction) ?: throw NullPointerException("null returned from cache mapping function")
} }
} }
class AttachmentsClassLoaderSimpleCacheImpl(cacheSize: Int) : AttachmentsClassLoaderCache { class AttachmentsClassLoaderSimpleCacheImpl(cacheSize: Int) : AttachmentsClassLoaderCache {
private val cache: MutableMap<AttachmentsClassLoaderKey, SerializationContext> private val cache: MutableMap<AttachmentsClassLoaderKey, SerializationContext>
= createSimpleCache<AttachmentsClassLoaderKey, SerializationContext>(cacheSize).toSynchronised() = createSimpleCache<AttachmentsClassLoaderKey, SerializationContext>(cacheSize).toSynchronised()
override fun computeIfAbsent(key: AttachmentsClassLoaderKey, mappingFunction: Function<in AttachmentsClassLoaderKey, out SerializationContext>): SerializationContext { override fun computeIfAbsent(
key: AttachmentsClassLoaderKey,
mappingFunction: (AttachmentsClassLoaderKey) -> SerializationContext
): SerializationContext {
return cache.computeIfAbsent(key, mappingFunction) return cache.computeIfAbsent(key, mappingFunction)
} }
} }

View File

@ -58,7 +58,7 @@ import java.util.function.Supplier
* *
* [LedgerTransaction]s should never be instantiated directly from client code, but rather via WireTransaction.toLedgerTransaction * [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 class LedgerTransaction
private constructor( private constructor(
// DOCSTART 1 // DOCSTART 1
@ -465,7 +465,7 @@ private constructor(
} }
inline fun <reified T : ContractState> filterInputs(crossinline predicate: (T) -> Boolean): List<T> { inline fun <reified T : ContractState> filterInputs(crossinline predicate: (T) -> Boolean): List<T> {
return filterInputs(T::class.java) { predicate(it) } return filterInputs(T::class.java, Predicate { predicate(it) })
} }
/** /**
@ -481,7 +481,7 @@ private constructor(
} }
inline fun <reified T : ContractState> filterReferenceInputs(crossinline predicate: (T) -> Boolean): List<T> { inline fun <reified T : ContractState> filterReferenceInputs(crossinline predicate: (T) -> Boolean): List<T> {
return filterReferenceInputs(T::class.java) { predicate(it) } return filterReferenceInputs(T::class.java, Predicate { predicate(it) })
} }
/** /**
@ -497,7 +497,7 @@ private constructor(
} }
inline fun <reified T : ContractState> filterInRefs(crossinline predicate: (T) -> Boolean): List<StateAndRef<T>> { inline fun <reified T : ContractState> filterInRefs(crossinline predicate: (T) -> Boolean): List<StateAndRef<T>> {
return filterInRefs(T::class.java) { predicate(it) } return filterInRefs(T::class.java, Predicate { predicate(it) })
} }
/** /**
@ -513,7 +513,7 @@ private constructor(
} }
inline fun <reified T : ContractState> filterReferenceInputRefs(crossinline predicate: (T) -> Boolean): List<StateAndRef<T>> { inline fun <reified T : ContractState> filterReferenceInputRefs(crossinline predicate: (T) -> Boolean): List<StateAndRef<T>> {
return filterReferenceInputRefs(T::class.java) { predicate(it) } return filterReferenceInputRefs(T::class.java, Predicate { predicate(it) })
} }
/** /**
@ -530,7 +530,7 @@ private constructor(
} }
inline fun <reified T : ContractState> findInput(crossinline predicate: (T) -> Boolean): T { inline fun <reified T : ContractState> 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. * @throws IllegalArgumentException if no item, or multiple items are found matching the requirements.
*/ */
fun <T : ContractState> findReference(clazz: Class<T>, predicate: Predicate<T>): T { fun <T : ContractState> findReference(clazz: Class<T>, predicate: Predicate<T>): T {
return referenceInputsOfType(clazz).single(predicate::test) return referenceInputsOfType(clazz).single { predicate.test(it) }
} }
inline fun <reified T : ContractState> findReference(crossinline predicate: (T) -> Boolean): T { inline fun <reified T : ContractState> 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 <reified T : ContractState> findInRef(crossinline predicate: (T) -> Boolean): StateAndRef<T> { inline fun <reified T : ContractState> findInRef(crossinline predicate: (T) -> Boolean): StateAndRef<T> {
return findInRef(T::class.java) { predicate(it) } return findInRef(T::class.java, Predicate { predicate(it) })
} }
/** /**
@ -581,7 +581,7 @@ private constructor(
} }
inline fun <reified T : ContractState> findReferenceInputRef(crossinline predicate: (T) -> Boolean): StateAndRef<T> { inline fun <reified T : ContractState> findReferenceInputRef(crossinline predicate: (T) -> Boolean): StateAndRef<T> {
return findReferenceInputRef(T::class.java) { predicate(it) } return findReferenceInputRef(T::class.java, Predicate { predicate(it) })
} }
/** /**
@ -616,7 +616,7 @@ private constructor(
} }
inline fun <reified T : CommandData> filterCommands(crossinline predicate: (T) -> Boolean): List<Command<T>> { inline fun <reified T : CommandData> filterCommands(crossinline predicate: (T) -> Boolean): List<Command<T>> {
return filterCommands(T::class.java) { predicate(it) } return filterCommands(T::class.java, Predicate { predicate(it) })
} }
/** /**
@ -633,7 +633,7 @@ private constructor(
} }
inline fun <reified T : CommandData> findCommand(crossinline predicate: (T) -> Boolean): Command<T> { inline fun <reified T : CommandData> findCommand(crossinline predicate: (T) -> Boolean): Command<T> {
return findCommand(T::class.java) { predicate(it) } return findCommand(T::class.java, Predicate { predicate(it) })
} }
/** /**

View File

@ -489,7 +489,8 @@ open class TransactionBuilder(
} }
if (explicitContractAttachment != null && hashAttachments.singleOrNull() != null) { 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." "An attachment has been explicitly set for contract $contractClassName in the transaction builder which conflicts with the HashConstraint of a state."
} }
} }

View File

@ -8,7 +8,6 @@ import rx.Subscription
import rx.functions.Action1 import rx.functions.Action1
import rx.subjects.ReplaySubject import rx.subjects.ReplaySubject
import java.io.Serializable 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 * 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 @CordaSerializable
class ProgressTracker(vararg inputSteps: Step) { class ProgressTracker(vararg inputSteps: Step) {
private companion object { private companion object {
private val log = contextLogger() private val log = contextLogger()
} }
private fun interface SerializableAction<T>: Action1<T>, Serializable @FunctionalInterface
private interface SerializableAction1<T> : Action1<T>, Serializable
@CordaSerializable @CordaSerializable
sealed class Change(val progressTracker: ProgressTracker) { sealed class Change(val progressTracker: ProgressTracker) {
@ -61,7 +60,11 @@ class ProgressTracker(vararg inputSteps: Step) {
*/ */
@CordaSerializable @CordaSerializable
open class Step(open val label: String) { 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. // Required when Steps with the same name are defined in multiple places.
private val discriminator: String = definitionLocation() private val discriminator: String = definitionLocation()
@ -149,10 +152,17 @@ class ProgressTracker(vararg inputSteps: Step) {
stepIndex = index stepIndex = index
_changes.onNext(Change.Position(this, steps[index])) _changes.onNext(Change.Position(this, steps[index]))
recalculateStepsTreeIndex() recalculateStepsTreeIndex()
curChangeSubscription = currentStep.changes.subscribe((SerializableAction<Change> { curChangeSubscription = currentStep.changes.subscribe(
_changes.onNext(it) object : SerializableAction1<Change> {
if (it is Change.Structural || it is Change.Rendering) rebuildStepsTree() else recalculateStepsTreeIndex() override fun call(c: Change) {
}), (SerializableAction { _changes.onError(it) })) _changes.onNext(c)
if (c is Change.Structural || c is Change.Rendering) rebuildStepsTree() else recalculateStepsTreeIndex()
}
},
object : SerializableAction1<Throwable> {
override fun call(t: Throwable) = _changes.onError(t)
}
)
if (currentStep == DONE) { if (currentStep == DONE) {
_changes.onCompleted() _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) * The zero-based index of the current step in the [steps] array (i.e. with UNSTARTED and DONE)
*/ */
var stepIndex: Int = 0 var stepIndex: Int = 0
private set(value) { private set
field = value
}
/** /**
* The zero-bases index of the current step in a [allStepsLabels] list * 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 getChildProgressTracker(step: Step): ProgressTracker? = childProgressTrackers[step]?.tracker
fun setChildProgressTracker(step: ProgressTracker.Step, childProgressTracker: ProgressTracker) { fun setChildProgressTracker(step: Step, childProgressTracker: ProgressTracker) {
val subscription = childProgressTracker.changes.subscribe((SerializableAction<Change>{ val subscription = childProgressTracker.changes.subscribe(
_changes.onNext(it) object : SerializableAction1<Change> {
if (it is Change.Structural || it is Change.Rendering) rebuildStepsTree() else recalculateStepsTreeIndex() override fun call(c: Change) {
}), (SerializableAction { _changes.onError(it) })) _changes.onNext(c)
if (c is Change.Structural || c is Change.Rendering) rebuildStepsTree() else recalculateStepsTreeIndex()
}
},
object : SerializableAction1<Throwable> {
override fun call(t: Throwable) = _changes.onError(t)
}
)
childProgressTrackers[step] = Child(childProgressTracker, subscription) childProgressTrackers[step] = Child(childProgressTracker, subscription)
childProgressTracker.parent = this childProgressTracker.parent = this
_changes.onNext(Change.Structural(this, step)) _changes.onNext(Change.Structural(this, step))
rebuildStepsTree() rebuildStepsTree()
} }
private fun removeChildProgressTracker(step: ProgressTracker.Step) { private fun removeChildProgressTracker(step: Step) {
childProgressTrackers.remove(step)?.let { childProgressTrackers.remove(step)?.let {
it.tracker.parent = null it.tracker.parent = null
it.subscription?.unsubscribe() it.subscription?.unsubscribe()

View File

@ -1,4 +1,3 @@
apply plugin: 'java'
apply plugin: 'org.jetbrains.kotlin.jvm' apply plugin: 'org.jetbrains.kotlin.jvm'
description 'NetworkParameters signing tool' description 'NetworkParameters signing tool'
@ -24,7 +23,7 @@ jar {
exclude "META-INF/*.DSA" exclude "META-INF/*.DSA"
exclude "META-INF/*.RSA" exclude "META-INF/*.RSA"
} }
baseName = "netparams" archiveBaseName = "netparams"
manifest { manifest {
attributes( attributes(
'Main-Class': 'net.corda.netparams.NetParamsKt' 'Main-Class': 'net.corda.netparams.NetParamsKt'

View File

@ -1,4 +1,3 @@
apply plugin: 'java'
apply plugin: 'org.jetbrains.kotlin.jvm' apply plugin: 'org.jetbrains.kotlin.jvm'
description 'NodeInfo signing tool' description 'NodeInfo signing tool'
@ -23,7 +22,7 @@ jar {
exclude "META-INF/*.DSA" exclude "META-INF/*.DSA"
exclude "META-INF/*.RSA" exclude "META-INF/*.RSA"
} }
baseName = "nodeinfo" archiveBaseName = "nodeinfo"
manifest { manifest {
attributes( attributes(
'Main-Class': 'net.corda.nodeinfo.NodeInfoKt' 'Main-Class': 'net.corda.nodeinfo.NodeInfoKt'

View File

@ -11,6 +11,9 @@ dependencies {
implementation project(':common-logging') implementation project(':common-logging')
implementation project(":common-validation") implementation project(":common-validation")
implementation "io.opentelemetry:opentelemetry-api:$open_telemetry_version"
compileOnly project(':opentelemetry')
implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version" implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
// TODO: remove the forced update of commons-collections and beanutils when artemis updates them // TODO: remove the forced update of commons-collections and beanutils when artemis updates them
@ -90,7 +93,7 @@ configurations {
testArtifacts.extendsFrom testRuntimeOnlyClasspath testArtifacts.extendsFrom testRuntimeOnlyClasspath
} }
task testJar(type: Jar) { tasks.register('testJar', Jar) {
classifier "tests" classifier "tests"
from sourceSets.test.output from sourceSets.test.output
} }

View File

@ -1,4 +1,4 @@
package net.corda.core.internal.telemetry package net.corda.nodeapi.internal.telemetry
import co.paralleluniverse.fibers.instrument.DontInstrument import co.paralleluniverse.fibers.instrument.DontInstrument
import io.opentelemetry.api.GlobalOpenTelemetry import io.opentelemetry.api.GlobalOpenTelemetry
@ -12,14 +12,23 @@ import io.opentelemetry.api.trace.StatusCode
import io.opentelemetry.api.trace.Tracer import io.opentelemetry.api.trace.Tracer
import io.opentelemetry.context.Context import io.opentelemetry.context.Context
import io.opentelemetry.context.Scope 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 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 import java.util.concurrent.ConcurrentLinkedDeque
@CordaSerializable @CordaSerializable
@ -54,12 +63,11 @@ class TracerSetup(serviceName: String) {
} }
@Suppress("TooManyFunctions") @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 tracerSetup = TracerSetup(serviceName)
val tracer: Tracer = tracerSetup.getTracer() val tracer: Tracer = tracerSetup.getTracer()
companion object { companion object {
private val log: Logger = LoggerFactory.getLogger(OpenTelemetryComponent::class.java)
const val OPENTELEMETRY_COMPONENT_NAME = "OpenTelemetry" const val OPENTELEMETRY_COMPONENT_NAME = "OpenTelemetry"
} }

View File

@ -6,7 +6,6 @@ apply plugin: 'org.jetbrains.kotlin.jvm'
// Java Persistence API support: create no-arg constructor // Java Persistence API support: create no-arg constructor
// see: http://stackoverflow.com/questions/32038177/kotlin-with-jpa-default-constructor-hell // see: http://stackoverflow.com/questions/32038177/kotlin-with-jpa-default-constructor-hell
apply plugin: 'org.jetbrains.kotlin.plugin.jpa' apply plugin: 'org.jetbrains.kotlin.plugin.jpa'
apply plugin: 'java'
apply plugin: 'net.corda.plugins.quasar-utils' apply plugin: 'net.corda.plugins.quasar-utils'
apply plugin: 'corda.common-publishing' apply plugin: 'corda.common-publishing'
@ -73,7 +72,7 @@ jib.container {
processResources { processResources {
from file("$rootDir/config/dev/log4j2.xml") from file("$rootDir/config/dev/log4j2.xml")
from file("$rootDir/config/dev/jolokia-access.xml") from file("$rootDir/config/dev/jolokia-access.xml")
from(tasks.findByPath(":verifier:shadowJar")) { from(tasks.getByPath(":verifier:shadowJar")) {
into("net/corda/node/verification") into("net/corda/node/verification")
rename { "external-verifier.jar" } rename { "external-verifier.jar" }
} }

View File

@ -6,7 +6,8 @@ import net.corda.core.contracts.CommandData
import net.corda.core.contracts.Contract import net.corda.core.contracts.Contract
import net.corda.core.contracts.ContractState import net.corda.core.contracts.ContractState
import net.corda.core.contracts.StateAndRef import net.corda.core.contracts.StateAndRef
import net.corda.core.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.crypto.SecureHash
import net.corda.core.flows.FinalityFlow import net.corda.core.flows.FinalityFlow
import net.corda.core.flows.FlowLogic 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.NotaryChangeFlow
import net.corda.core.flows.ReceiveFinalityFlow import net.corda.core.flows.ReceiveFinalityFlow
import net.corda.core.flows.StartableByRPC import net.corda.core.flows.StartableByRPC
import net.corda.core.flows.UnexpectedFlowEndException
import net.corda.core.identity.AbstractParty import net.corda.core.identity.AbstractParty
import net.corda.core.identity.CordaX500Name import net.corda.core.identity.CordaX500Name
import net.corda.core.identity.Party import net.corda.core.identity.Party
@ -32,7 +34,6 @@ import net.corda.finance.DOLLARS
import net.corda.finance.contracts.asset.Cash import net.corda.finance.contracts.asset.Cash
import net.corda.finance.flows.CashIssueFlow import net.corda.finance.flows.CashIssueFlow
import net.corda.finance.flows.CashPaymentFlow 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.ALICE_NAME
import net.corda.testing.core.BOB_NAME import net.corda.testing.core.BOB_NAME
import net.corda.testing.core.BOC_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) @Test(timeout=300_000)
fun `regular transactions can fail verification in external verifier`() { fun `regular transactions can fail verification in external verifier`() {
internalDriver( internalDriver(
@ -104,7 +129,7 @@ class ExternalVerificationTest {
// Create a transaction from Alice to Bob, where Charlie is specified as the contract verification trigger // 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() 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 // 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() } .isThrownBy { bob.rpc.startFlow(::FailExternallyFlow, firstState, charlie.nodeInfo, charlie.nodeInfo).returnValue.getOrThrow() }
.withMessageContaining("Fail in external verifier: ${firstState.ref.txhash}") .withMessageContaining("Fail in external verifier: ${firstState.ref.txhash}")
@ -149,6 +174,7 @@ class ExternalVerificationTest {
return verifierLogs[0].readText() return verifierLogs[0].readText()
} }
class FailExternallyContract : Contract { class FailExternallyContract : Contract {
override fun verify(tx: LedgerTransaction) { override fun verify(tx: LedgerTransaction) {
val command = tx.commandsOfType<Command>().single() val command = tx.commandsOfType<Command>().single()
@ -165,40 +191,46 @@ class ExternalVerificationTest {
} }
} }
data class State(val party: Party) : ContractState { data class State(override val party: Party) : TestState
override val participants: List<AbstractParty> get() = listOf(party)
}
data class Command(val failForParty: Party) : CommandData data class Command(val failForParty: Party) : CommandData
} }
@StartableByRPC @StartableByRPC
@InitiatingFlow @InitiatingFlow
class FailExternallyFlow(private val inputState: StateAndRef<State>?, class FailExternallyFlow(inputState: StateAndRef<FailExternallyContract.State>?,
private val failForParty: NodeInfo, private val failForParty: NodeInfo,
private val recipient: NodeInfo) : FlowLogic<StateAndRef<State>>() { recipient: NodeInfo) : TestFlow<FailExternallyContract.State>(inputState, recipient) {
@Suspendable override fun newOutput() = FailExternallyContract.State(serviceHub.myInfo.legalIdentities[0])
override fun call(): StateAndRef<State> { override fun newCommand() = FailExternallyContract.Command(failForParty.legalIdentities[0])
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)
}
}
@Suppress("unused") @Suppress("unused")
@InitiatedBy(FailExternallyFlow::class) @InitiatedBy(FailExternallyFlow::class)
class ReceiverFlow(private val otherSide: FlowSession) : FlowLogic<Unit>() { class ReceiverFlow(otherSide: FlowSession) : TestReceiverFlow(otherSide)
@Suspendable
override fun call() {
subFlow(ReceiveFinalityFlow(otherSide))
} }
class NewKotlinApiContract : Contract {
override fun verify(tx: LedgerTransaction) {
check(tx.commandsOfType<Command>().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<NewKotlinApiContract.State>(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 return notaryChangeTx!!.id
} }
} }
abstract class TestFlow<T : TestState>(
private val inputState: StateAndRef<T>?,
private val recipient: NodeInfo
) : FlowLogic<StateAndRef<T>>() {
@Suspendable
override fun call(): StateAndRef<T> {
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<Unit>() {
@Suspendable
override fun call() {
subFlow(ReceiveFinalityFlow(otherSide))
}
}
interface TestState : ContractState {
val party: Party
override val participants: List<AbstractParty> get() = listOf(party)
}
} }

View File

@ -42,7 +42,6 @@ import net.corda.core.internal.cordapp.CordappProviderInternal
import net.corda.core.internal.messaging.AttachmentTrustInfoRPCOps import net.corda.core.internal.messaging.AttachmentTrustInfoRPCOps
import net.corda.core.internal.notary.NotaryService import net.corda.core.internal.notary.NotaryService
import net.corda.core.internal.rootMessage 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.SimpleLogTelemetryComponent
import net.corda.core.internal.telemetry.TelemetryComponent import net.corda.core.internal.telemetry.TelemetryComponent
import net.corda.core.internal.telemetry.TelemetryServiceImpl 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.SchemaMigration
import net.corda.nodeapi.internal.persistence.contextDatabase import net.corda.nodeapi.internal.persistence.contextDatabase
import net.corda.nodeapi.internal.persistence.withoutDatabaseAccess import net.corda.nodeapi.internal.persistence.withoutDatabaseAccess
import net.corda.nodeapi.internal.telemetry.OpenTelemetryComponent
import org.apache.activemq.artemis.utils.ReusableLatch import org.apache.activemq.artemis.utils.ReusableLatch
import org.jolokia.jvmagent.JolokiaServer import org.jolokia.jvmagent.JolokiaServer
import org.jolokia.jvmagent.JolokiaServerConfig import org.jolokia.jvmagent.JolokiaServerConfig

View File

@ -276,7 +276,7 @@ open class NodeStartup : NodeStartupLogging {
logger.info("Platform Version: ${versionInfo.platformVersion}") logger.info("Platform Version: ${versionInfo.platformVersion}")
logger.info("Revision: ${versionInfo.revision}") logger.info("Revision: ${versionInfo.revision}")
val info = ManagementFactory.getRuntimeMXBean() 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("Main class: ${NodeConfiguration::class.java.location.toURI().path}")
logger.info("CommandLine Args: ${info.inputArguments.joinToString(" ")}") logger.info("CommandLine Args: ${info.inputArguments.joinToString(" ")}")
// JDK 11 (bootclasspath no longer supported from JDK 9) // JDK 11 (bootclasspath no longer supported from JDK 9)

View File

@ -1,4 +1,5 @@
apply plugin: 'java' import net.corda.plugins.Cordform
apply plugin: 'org.jetbrains.kotlin.jvm' apply plugin: 'org.jetbrains.kotlin.jvm'
apply plugin: 'idea' apply plugin: 'idea'
apply plugin: 'net.corda.plugins.quasar-utils' apply plugin: 'net.corda.plugins.quasar-utils'
@ -54,10 +55,9 @@ dependencies {
testRuntimeOnly "org.junit.platform:junit-platform-launcher:${junit_platform_version}" 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 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 { nodeDefaults {
cordapp project(':finance:workflows') cordapp project(':finance:workflows')
cordapp project(':finance:contracts') cordapp project(':finance:contracts')
@ -113,9 +113,9 @@ idea {
} }
} }
task runRPCCashIssue(type: JavaExec) { tasks.register('runRPCCashIssue', JavaExec) {
classpath = sourceSets.main.runtimeClasspath classpath = sourceSets.main.runtimeClasspath
main = 'net.corda.bank.IssueCash' mainClass = 'net.corda.bank.IssueCash'
jvmArgs test_add_opens jvmArgs test_add_opens
jvmArgs test_add_exports jvmArgs test_add_exports
@ -131,9 +131,9 @@ task runRPCCashIssue(type: JavaExec) {
jvmArgs test_add_exports jvmArgs test_add_exports
} }
task runWebCashIssue(type: JavaExec) { tasks.register('runWebCashIssue', JavaExec) {
classpath = sourceSets.main.runtimeClasspath classpath = sourceSets.main.runtimeClasspath
main = 'net.corda.bank.IssueCash' mainClass = 'net.corda.bank.IssueCash'
jvmArgs test_add_opens jvmArgs test_add_opens
jvmArgs test_add_exports jvmArgs test_add_exports

View File

@ -1,10 +1,15 @@
apply plugin: 'org.jetbrains.kotlin.jvm'
apply plugin: 'net.corda.plugins.cordapp' apply plugin: 'net.corda.plugins.cordapp'
def javaHome = System.getProperty('java.home') def javaHome = System.getProperty('java.home')
def shrinkJar = file("$buildDir/libs/${project.name}-${project.version}-tiny.jar") 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.MessageDigest
import java.security.NoSuchAlgorithmException
static String sha256(File jarFile) throws FileNotFoundException, NoSuchAlgorithmException { static String sha256(File jarFile) throws FileNotFoundException, NoSuchAlgorithmException {
InputStream input = new FileInputStream(jarFile) InputStream input = new FileInputStream(jarFile)
@ -58,10 +63,10 @@ dependencies {
implementation "com.opengamma.strata:strata-market:$strata_version" implementation "com.opengamma.strata:strata-market:$strata_version"
} }
def cordappDependencies = file("${sourceSets['main'].output.resourcesDir}/META-INF/Cordapp-Dependencies")
configurations.cordapp.canBeResolved = true configurations.cordapp.canBeResolved = true
task generateDependencies { tasks.register('generateDependencies') {
dependsOn project(':finance:contracts').tasks.jar dependsOn project(':finance:contracts').tasks.jar
def cordappDependencies = file("${sourceSets.main.output.resourcesDir}/META-INF/Cordapp-Dependencies")
inputs.files(configurations.cordapp) inputs.files(configurations.cordapp)
outputs.files(cordappDependencies) outputs.files(cordappDependencies)
doLast { doLast {
@ -75,11 +80,10 @@ task generateDependencies {
processResources.finalizedBy generateDependencies processResources.finalizedBy generateDependencies
jar { jar {
classifier = 'fat' archiveClassifier = 'fat'
} }
import proguard.gradle.ProGuardTask tasks.register('shrink', ProGuardTask) {
task shrink(type: ProGuardTask) {
injars jar injars jar
outjars shrinkJar outjars shrinkJar
@ -114,7 +118,7 @@ task shrink(type: ProGuardTask) {
keepclassmembers 'class org.joda.** { *; }', includedescriptorclasses: true keepclassmembers 'class org.joda.** { *; }', includedescriptorclasses: true
} }
task sign(type: net.corda.plugins.SignJar) { tasks.register('sign', SignJar) {
inputJars shrink inputJars shrink
} }

View File

@ -1,3 +1,4 @@
apply plugin: 'org.jetbrains.kotlin.jvm'
apply plugin: 'net.corda.plugins.quasar-utils' apply plugin: 'net.corda.plugins.quasar-utils'
apply plugin: 'net.corda.plugins.cordapp' apply plugin: 'net.corda.plugins.cordapp'

View File

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

View File

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

View File

@ -3,28 +3,25 @@ apply plugin: 'corda.common-publishing'
description 'Corda serialization' description 'Corda serialization'
configurations {
resolvableImplementation.extendsFrom implementation
testArtifacts.extendsFrom testRuntimeClasspath
}
dependencies { dependencies {
implementation project(":core") implementation project(":core")
implementation "io.reactivex:rxjava:$rxjava_version" implementation "io.reactivex:rxjava:$rxjava_version"
implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version" implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
implementation "org.apache.activemq:artemis-commons:${artemis_version}" implementation "org.apache.activemq:artemis-commons:${artemis_version}"
implementation "org.ow2.asm:asm:$asm_version" implementation "org.ow2.asm:asm:$asm_version"
implementation "com.google.guava:guava:$guava_version" implementation "com.google.guava:guava:$guava_version"
// For AMQP serialisation. // For AMQP serialisation.
implementation "org.apache.qpid:proton-j:$protonj_version" implementation "org.apache.qpid:proton-j:$protonj_version"
// ClassGraph: classpath scanning // ClassGraph: classpath scanning
implementation "io.github.classgraph:classgraph:$class_graph_version" implementation "io.github.classgraph:classgraph:$class_graph_version"
// Pure-Java Snappy compression // Pure-Java Snappy compression
implementation "org.iq80.snappy:snappy:$snappy_version" implementation "org.iq80.snappy:snappy:$snappy_version"
// For caches rather than guava // For caches rather than guava
implementation "com.github.ben-manes.caffeine:caffeine:$caffeine_version" implementation "com.github.ben-manes.caffeine:caffeine:$caffeine_version"
@ -43,16 +40,12 @@ dependencies {
testImplementation "com.fasterxml.jackson.core:jackson-databind:$jackson_version" testImplementation "com.fasterxml.jackson.core:jackson-databind:$jackson_version"
} }
configurations {
testArtifacts.extendsFrom testRuntimeClasspath
}
tasks.withType(Javadoc).configureEach { tasks.withType(Javadoc).configureEach {
// We have no public or protected Java classes to document. // We have no public or protected Java classes to document.
enabled = false enabled = false
} }
task testJar(type: Jar) { tasks.register('testJar', Jar) {
archiveClassifier = 'tests' archiveClassifier = 'tests'
from sourceSets.test.output from sourceSets.test.output
} }

View File

@ -21,14 +21,14 @@ import java.security.PublicKey
typealias SerializedNetworkParameters = SerializedBytes<NetworkParameters> typealias SerializedNetworkParameters = SerializedBytes<NetworkParameters>
@CordaSerializable @CordaSerializable
sealed interface ExternalVerifierInbound { sealed class ExternalVerifierInbound {
data class Initialisation( data class Initialisation(
val customSerializerClassNames: Set<String>, val customSerializerClassNames: Set<String>,
val serializationWhitelistClassNames: Set<String>, val serializationWhitelistClassNames: Set<String>,
val customSerializationSchemeClassName: String?, val customSerializationSchemeClassName: String?,
val serializedCurrentNetworkParameters: SerializedNetworkParameters val serializedCurrentNetworkParameters: SerializedNetworkParameters
) : ExternalVerifierInbound { ) : ExternalVerifierInbound() {
val currentNetworkParameters: NetworkParameters by lazy(serializedCurrentNetworkParameters::deserialize) val currentNetworkParameters: NetworkParameters by lazy { serializedCurrentNetworkParameters.deserialize() }
override fun toString(): String { override fun toString(): String {
return "Initialisation(" + return "Initialisation(" +
@ -43,31 +43,31 @@ sealed interface ExternalVerifierInbound {
val stx: SignedTransaction, val stx: SignedTransaction,
val stxInputsAndReferences: Map<StateRef, SerializedTransactionState>, val stxInputsAndReferences: Map<StateRef, SerializedTransactionState>,
val checkSufficientSignatures: Boolean val checkSufficientSignatures: Boolean
) : ExternalVerifierInbound ) : ExternalVerifierInbound()
data class PartiesResult(val parties: List<Party?>) : ExternalVerifierInbound data class PartiesResult(val parties: List<Party?>) : ExternalVerifierInbound()
data class AttachmentResult(val attachment: AttachmentWithTrust?) : ExternalVerifierInbound data class AttachmentResult(val attachment: AttachmentWithTrust?) : ExternalVerifierInbound()
data class AttachmentsResult(val attachments: List<AttachmentWithTrust?>) : ExternalVerifierInbound data class AttachmentsResult(val attachments: List<AttachmentWithTrust?>) : ExternalVerifierInbound()
data class NetworkParametersResult(val networkParameters: NetworkParameters?) : ExternalVerifierInbound data class NetworkParametersResult(val networkParameters: NetworkParameters?) : ExternalVerifierInbound()
data class TrustedClassAttachmentResult(val id: SecureHash?) : ExternalVerifierInbound data class TrustedClassAttachmentResult(val id: SecureHash?) : ExternalVerifierInbound()
} }
@CordaSerializable @CordaSerializable
data class AttachmentWithTrust(val attachment: Attachment, val isTrusted: Boolean) data class AttachmentWithTrust(val attachment: Attachment, val isTrusted: Boolean)
@CordaSerializable @CordaSerializable
sealed interface ExternalVerifierOutbound { sealed class ExternalVerifierOutbound {
sealed interface VerifierRequest : ExternalVerifierOutbound { sealed class VerifierRequest : ExternalVerifierOutbound() {
data class GetParties(val keys: Set<PublicKey>) : VerifierRequest { data class GetParties(val keys: Set<PublicKey>) : VerifierRequest() {
override fun toString(): String = "GetParty(keys=${keys.map { it.toStringShort() }}})" override fun toString(): String = "GetParty(keys=${keys.map { it.toStringShort() }}})"
} }
data class GetAttachment(val id: SecureHash) : VerifierRequest data class GetAttachment(val id: SecureHash) : VerifierRequest()
data class GetAttachments(val ids: Set<SecureHash>) : VerifierRequest data class GetAttachments(val ids: Set<SecureHash>) : VerifierRequest()
data class GetNetworkParameters(val id: SecureHash) : VerifierRequest data class GetNetworkParameters(val id: SecureHash) : VerifierRequest()
data class GetTrustedClassAttachment(val className: String) : VerifierRequest data class GetTrustedClassAttachment(val className: String) : VerifierRequest()
} }
data class VerificationResult(val result: Try<Unit>) : ExternalVerifierOutbound data class VerificationResult(val result: Try<Unit>) : ExternalVerifierOutbound()
} }
fun DataOutputStream.writeCordaSerializable(payload: Any) { fun DataOutputStream.writeCordaSerializable(payload: Any) {

View File

@ -42,6 +42,7 @@ include 'confidential-identities'
include 'finance:contracts' include 'finance:contracts'
include 'finance:workflows' include 'finance:workflows'
include 'core' include 'core'
include 'core-1.2'
include 'core-tests' include 'core-tests'
include 'docs' include 'docs'
include 'node-api' include 'node-api'
@ -103,6 +104,7 @@ include 'samples:cordapp-configuration:workflows'
include 'samples:network-verifier:contracts' include 'samples:network-verifier:contracts'
include 'samples:network-verifier:workflows' include 'samples:network-verifier:workflows'
include 'serialization' include 'serialization'
include 'serialization-1.2'
include 'serialization-tests' include 'serialization-tests'
include 'testing:cordapps:dbfailure:dbfcontracts' include 'testing:cordapps:dbfailure:dbfcontracts'
include 'testing:cordapps:dbfailure:dbfworkflows' include 'testing:cordapps:dbfailure:dbfworkflows'

View File

@ -1,4 +1,3 @@
apply plugin: 'java'
apply plugin: 'org.jetbrains.kotlin.jvm' apply plugin: 'org.jetbrains.kotlin.jvm'
dependencies { dependencies {

View File

@ -1,3 +1,4 @@
apply plugin: 'org.jetbrains.kotlin.jvm'
apply plugin: 'net.corda.plugins.api-scanner' apply plugin: 'net.corda.plugins.api-scanner'
apply plugin: 'corda.common-publishing' apply plugin: 'corda.common-publishing'

View File

@ -1,5 +1,4 @@
apply plugin: 'org.jetbrains.kotlin.jvm' apply plugin: 'org.jetbrains.kotlin.jvm'
apply plugin: 'java'
apply plugin: 'corda.common-publishing' apply plugin: 'corda.common-publishing'
description 'Corda node web server' description 'Corda node web server'
@ -78,7 +77,7 @@ dependencies {
integrationTestImplementation project(':node-driver') integrationTestImplementation project(':node-driver')
} }
task integrationTest(type: Test) { tasks.register('integrationTest', Test) {
testClassesDirs = sourceSets.integrationTest.output.classesDirs testClassesDirs = sourceSets.integrationTest.output.classesDirs
classpath = sourceSets.integrationTest.runtimeClasspath classpath = sourceSets.integrationTest.runtimeClasspath

View File

@ -1,4 +1,3 @@
apply plugin: 'java'
apply plugin: 'org.jetbrains.kotlin.jvm' apply plugin: 'org.jetbrains.kotlin.jvm'
apply plugin: 'corda.common-publishing' apply plugin: 'corda.common-publishing'

View File

@ -1,4 +1,3 @@
apply plugin: 'java'
apply plugin: 'org.jetbrains.kotlin.jvm' apply plugin: 'org.jetbrains.kotlin.jvm'
apply plugin: 'corda.common-publishing' apply plugin: 'corda.common-publishing'

View File

@ -27,7 +27,6 @@ ext {
pkg_macosxKeyUserName = 'R3CEV' pkg_macosxKeyUserName = 'R3CEV'
} }
apply plugin: 'java'
apply plugin: 'org.jetbrains.kotlin.jvm' apply plugin: 'org.jetbrains.kotlin.jvm'
apply plugin: 'application' apply plugin: 'application'
@ -156,7 +155,8 @@ distributions {
* Bundles the application using JavaPackager, * Bundles the application using JavaPackager,
* using the ZIP distribution as source. * using the ZIP distribution as source.
*/ */
task javapackage(dependsOn: distZip) { tasks.register('javapackage') {
dependsOn distZip
doLast { doLast {
delete([pkg_source, pkg_outDir]) delete([pkg_source, pkg_outDir])
@ -190,8 +190,7 @@ task javapackage(dependsOn: distZip) {
include '**/*.manifest' include '**/*.manifest'
} }
filter { line -> filter { line ->
line.replaceAll('@pkg_version@', pkg_version) line.replaceAll('@pkg_version@', pkg_version).replaceAll('@signingKeyUserName@', pkg_macosxKeyUserName)
.replaceAll('@signingKeyUserName@', pkg_macosxKeyUserName)
} }
into "$pkg_source/package" into "$pkg_source/package"
} }

View File

@ -21,7 +21,6 @@ ext {
apply plugin: 'org.jetbrains.kotlin.jvm' apply plugin: 'org.jetbrains.kotlin.jvm'
apply plugin: 'idea' apply plugin: 'idea'
apply plugin: 'java'
apply plugin: 'application' apply plugin: 'application'
// We need to set mainClassName before applying the shadow plugin. // We need to set mainClassName before applying the shadow plugin.
mainClassName = 'net.corda.networkbuilder.Main' mainClassName = 'net.corda.networkbuilder.Main'

6
verifier/README.md Normal file
View File

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

View File

@ -1,5 +1,5 @@
plugins { plugins {
id "org.jetbrains.kotlin.jvm" id "corda.kotlin-1.2"
id "application" id "application"
id "com.github.johnrengelman.shadow" id "com.github.johnrengelman.shadow"
} }
@ -9,12 +9,12 @@ application {
} }
dependencies { dependencies {
implementation project(":core") implementation project(":core-1.2")
implementation project(":serialization") implementation project(":serialization-1.2")
implementation "com.github.ben-manes.caffeine:caffeine:$caffeine_version" implementation "com.github.ben-manes.caffeine:caffeine:$caffeine_version"
implementation "org.slf4j:jul-to-slf4j:$slf4j_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 { jar {

View File

@ -135,7 +135,7 @@ class ExternalVerifier(
private fun verifyTransaction(request: VerificationRequest) { private fun verifyTransaction(request: VerificationRequest) {
val verificationContext = ExternalVerificationContext(appClassLoader, attachmentsClassLoaderCache, this, request.stxInputsAndReferences) val verificationContext = ExternalVerificationContext(appClassLoader, attachmentsClassLoaderCache, this, request.stxInputsAndReferences)
val result = try { val result: Try<Unit> = try {
request.stx.verifyInternal(verificationContext, request.checkSufficientSignatures) request.stx.verifyInternal(verificationContext, request.checkSufficientSignatures)
log.info("${request.stx} verified") log.info("${request.stx} verified")
Try.Success(Unit) Try.Success(Unit)
@ -213,7 +213,7 @@ class ExternalVerifier(
override fun rpcServerSerializerFactory(context: SerializationContext) = throw UnsupportedOperationException() override fun rpcServerSerializerFactory(context: SerializationContext) = throw UnsupportedOperationException()
companion object { companion object {
inline fun <reified T> Set<String>?.load(classLoader: ClassLoader?): Set<T> { inline fun <reified T : Any> Set<String>?.load(classLoader: ClassLoader?): Set<T> {
return this?.mapToSet { loadClassOfType<T>(it, classLoader = classLoader).kotlin.objectOrNewInstance() } ?: emptySet() return this?.mapToSet { loadClassOfType<T>(it, classLoader = classLoader).kotlin.objectOrNewInstance() } ?: emptySet()
} }
} }