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

View File

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

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: 'net.corda.plugins.api-scanner'
apply plugin: 'corda.common-publishing'

View File

@ -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 <T> getTelemetryHandle(telemetryClass: Class<T>): T? {
return telemetryService.getTelemetryHandle(telemetryClass)
}
}
}

View File

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

View File

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

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

View File

@ -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 <T> List<T>.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 <T, R> Iterable<T>.mapToSet(transform: (T) -> R): Set<R> {
if (this is Collection) {
return if (this is Collection) {
when (size) {
0 -> return emptySet()
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()
fun JarInputStream.entries(): Sequence<JarEntry> = generateSequence(nextJarEntry) { nextJarEntry }
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
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
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()

View File

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

View File

@ -56,7 +56,7 @@ class AttachmentFixups {
private fun parseIds(ids: String): Set<AttachmentId> {
return ids.splitToSequence(",")
.map(String::trim)
.filterNot(String::isEmpty)
.filter { it.isNotEmpty() }
.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.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)

View File

@ -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<ContractState>(stateRef.index).state.contract
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
* [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.

View File

@ -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<Attachment>,
}
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<in AttachmentsClassLoaderKey, out SerializationContext>): 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<in AttachmentsClassLoaderKey, out SerializationContext>): 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<AttachmentsClassLoaderKey, SerializationContext>
= 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)
}
}

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

View File

@ -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<T>: Action1<T>, Serializable
@FunctionalInterface
private interface SerializableAction1<T> : Action1<T>, 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<Change> {
_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<Change> {
override fun call(c: Change) {
_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) {
_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<Change>{
_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<Change> {
override fun call(c: Change) {
_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)
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()

View File

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

View File

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

View File

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

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

View File

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

View File

@ -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<Command>().single()
@ -165,40 +191,46 @@ class ExternalVerificationTest {
}
}
data class State(val party: Party) : ContractState {
override val participants: List<AbstractParty> 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<State>?,
class FailExternallyFlow(inputState: StateAndRef<FailExternallyContract.State>?,
private val failForParty: NodeInfo,
private val recipient: NodeInfo) : FlowLogic<StateAndRef<State>>() {
@Suspendable
override fun call(): StateAndRef<State> {
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<FailExternallyContract.State>(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<Unit>() {
@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
}
}
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.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

View File

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

View File

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

View File

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

View File

@ -1,3 +1,4 @@
apply plugin: 'org.jetbrains.kotlin.jvm'
apply plugin: 'net.corda.plugins.quasar-utils'
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'
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
}

View File

@ -21,14 +21,14 @@ import java.security.PublicKey
typealias SerializedNetworkParameters = SerializedBytes<NetworkParameters>
@CordaSerializable
sealed interface ExternalVerifierInbound {
sealed class ExternalVerifierInbound {
data class Initialisation(
val customSerializerClassNames: Set<String>,
val serializationWhitelistClassNames: Set<String>,
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<StateRef, SerializedTransactionState>,
val checkSufficientSignatures: Boolean
) : ExternalVerifierInbound
) : ExternalVerifierInbound()
data class PartiesResult(val parties: List<Party?>) : ExternalVerifierInbound
data class AttachmentResult(val attachment: AttachmentWithTrust?) : ExternalVerifierInbound
data class AttachmentsResult(val attachments: List<AttachmentWithTrust?>) : ExternalVerifierInbound
data class NetworkParametersResult(val networkParameters: NetworkParameters?) : ExternalVerifierInbound
data class TrustedClassAttachmentResult(val id: SecureHash?) : ExternalVerifierInbound
data class PartiesResult(val parties: List<Party?>) : ExternalVerifierInbound()
data class AttachmentResult(val attachment: AttachmentWithTrust?) : ExternalVerifierInbound()
data class AttachmentsResult(val attachments: List<AttachmentWithTrust?>) : 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<PublicKey>) : VerifierRequest {
sealed class ExternalVerifierOutbound {
sealed class VerifierRequest : ExternalVerifierOutbound() {
data class GetParties(val keys: Set<PublicKey>) : VerifierRequest() {
override fun toString(): String = "GetParty(keys=${keys.map { it.toStringShort() }}})"
}
data class GetAttachment(val id: SecureHash) : VerifierRequest
data class GetAttachments(val ids: Set<SecureHash>) : 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<SecureHash>) : VerifierRequest()
data class GetNetworkParameters(val id: SecureHash) : 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) {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

View File

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