From 0e047d9263e482dc1cfbe87ce1b9a2ec892dd77d Mon Sep 17 00:00:00 2001 From: Katelyn Baker Date: Thu, 11 Jan 2018 20:11:51 +0000 Subject: [PATCH 01/36] CORDA-914 - Deterministic property ordering for AMQP serialization --- .../amqp/CorDappCustomSerializer.kt | 4 +- .../serialization/amqp/CustomSerializer.kt | 13 +- .../serialization/amqp/EvolutionSerializer.kt | 2 +- .../serialization/amqp/ObjectSerializer.kt | 51 +++-- .../serialization/amqp/PropertySerializer.kt | 67 +------ .../serialization/amqp/PropertySerializers.kt | 179 ++++++++++++++++++ .../internal/serialization/amqp/Schema.kt | 9 +- .../serialization/amqp/SerializationHelper.kt | 134 +++++++------ .../amqp/custom/ThrowableSerializer.kt | 7 +- .../amqp/JavaPrivatePropertyTests.java | 12 +- .../amqp/PrivatePropertyTests.kt | 38 +++- .../amqp/SerializationPropertyOrdering.kt | 125 ++++++++++++ 12 files changed, 465 insertions(+), 176 deletions(-) create mode 100644 node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/PropertySerializers.kt create mode 100644 node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializationPropertyOrdering.kt diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/CorDappCustomSerializer.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/CorDappCustomSerializer.kt index 8e5e249457..003da13268 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/CorDappCustomSerializer.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/CorDappCustomSerializer.kt @@ -70,8 +70,8 @@ class CorDappCustomSerializer( data.withDescribed(descriptor) { data.withList { - for (property in proxySerializer.propertySerializers.getters) { - property.writeProperty(proxy, this, output) + proxySerializer.propertySerializers.serializationOrder.forEach { + it.getter.writeProperty(proxy, this, output) } } } diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/CustomSerializer.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/CustomSerializer.kt index 647da2fd05..fc0891e93a 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/CustomSerializer.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/CustomSerializer.kt @@ -61,7 +61,14 @@ abstract class CustomSerializer : AMQPSerializer, SerializerFor { override fun isSerializerFor(clazz: Class<*>): Boolean = clazz == this.clazz override val type: Type get() = clazz override val typeDescriptor = Symbol.valueOf("$DESCRIPTOR_DOMAIN:${fingerprintForDescriptors(superClassSerializer.typeDescriptor.toString(), nameForType(clazz))}") - private val typeNotation: TypeNotation = RestrictedType(SerializerFactory.nameForType(clazz), null, emptyList(), SerializerFactory.nameForType(superClassSerializer.type), Descriptor(typeDescriptor), emptyList()) + private val typeNotation: TypeNotation = RestrictedType( + SerializerFactory.nameForType(clazz), + null, + emptyList(), + SerializerFactory.nameForType(superClassSerializer.type), + Descriptor(typeDescriptor), + emptyList()) + override fun writeClassInfo(output: SerializationOutput) { output.writeTypeNotations(typeNotation) } @@ -132,8 +139,8 @@ abstract class CustomSerializer : AMQPSerializer, SerializerFor { override fun writeDescribedObject(obj: T, data: Data, type: Type, output: SerializationOutput) { val proxy = toProxy(obj) data.withList { - for (property in proxySerializer.propertySerializers.getters) { - property.writeProperty(proxy, this, output) + proxySerializer.propertySerializers.serializationOrder.forEach { + it.getter.writeProperty(proxy, this, output) } } } diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/EvolutionSerializer.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/EvolutionSerializer.kt index 7335db0fa3..e3357b84d9 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/EvolutionSerializer.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/EvolutionSerializer.kt @@ -20,7 +20,7 @@ class EvolutionSerializer( override val kotlinConstructor: KFunction?) : ObjectSerializer(clazz, factory) { // explicitly set as empty to indicate it's unused by this type of serializer - override val propertySerializers = ConstructorDestructorMethods (emptyList(), emptyList()) + override val propertySerializers = PropertySerializersEvolution() /** * Represents a parameter as would be passed to the constructor of the class as it was diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/ObjectSerializer.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/ObjectSerializer.kt index a9d099b2e3..034d4b7f93 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/ObjectSerializer.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/ObjectSerializer.kt @@ -11,7 +11,8 @@ import java.lang.reflect.Type import kotlin.reflect.jvm.javaConstructor /** - * Responsible for serializing and deserializing a regular object instance via a series of properties (matched with a constructor). + * Responsible for serializing and deserializing a regular object instance via a series of properties + * (matched with a constructor). */ open class ObjectSerializer(val clazz: Type, factory: SerializerFactory) : AMQPSerializer { override val type: Type get() = clazz @@ -22,7 +23,7 @@ open class ObjectSerializer(val clazz: Type, factory: SerializerFactory) : AMQPS private val logger = contextLogger() } - open internal val propertySerializers: ConstructorDestructorMethods by lazy { + open internal val propertySerializers: PropertySerializers by lazy { propertiesForSerialization(kotlinConstructor, clazz, factory) } @@ -31,17 +32,22 @@ open class ObjectSerializer(val clazz: Type, factory: SerializerFactory) : AMQPS private val typeName = nameForType(clazz) override val typeDescriptor = Symbol.valueOf("$DESCRIPTOR_DOMAIN:${fingerprintForType(type, factory)}") - private val interfaces = interfacesForSerialization(clazz, factory) // We restrict to only those annotated or whitelisted - open internal val typeNotation: TypeNotation by lazy { CompositeType(typeName, null, generateProvides(), Descriptor(typeDescriptor), generateFields()) } + // We restrict to only those annotated or whitelisted + private val interfaces = interfacesForSerialization(clazz, factory) + + open internal val typeNotation: TypeNotation by lazy { + CompositeType(typeName, null, generateProvides(), Descriptor(typeDescriptor), generateFields()) + } override fun writeClassInfo(output: SerializationOutput) { if (output.writeTypeNotations(typeNotation)) { for (iface in interfaces) { output.requireSerializer(iface) } - for (property in propertySerializers.getters) { - property.writeClassInfo(output) + + propertySerializers.serializationOrder.forEach { property -> + property.getter.writeClassInfo(output) } } } @@ -51,8 +57,8 @@ open class ObjectSerializer(val clazz: Type, factory: SerializerFactory) : AMQPS data.withDescribed(typeNotation.descriptor) { // Write list withList { - for (property in propertySerializers.getters) { - property.writeProperty(obj, this, output) + propertySerializers.serializationOrder.forEach { property -> + property.getter.writeProperty(obj, this, output) } } } @@ -63,16 +69,18 @@ open class ObjectSerializer(val clazz: Type, factory: SerializerFactory) : AMQPS schemas: SerializationSchemas, input: DeserializationInput): Any = ifThrowsAppend({ clazz.typeName }) { if (obj is List<*>) { - if (obj.size > propertySerializers.getters.size) { + if (obj.size > propertySerializers.size) { throw NotSerializableException("Too many properties in described type $typeName") } - return if (propertySerializers.setters.isEmpty()) { + return if (propertySerializers.byConstructor) { readObjectBuildViaConstructor(obj, schemas, input) } else { readObjectBuildViaSetters(obj, schemas, input) } - } else throw NotSerializableException("Body of described type is unexpected $obj") + } else { + throw NotSerializableException("Body of described type is unexpected $obj") + } } private fun readObjectBuildViaConstructor( @@ -81,7 +89,11 @@ open class ObjectSerializer(val clazz: Type, factory: SerializerFactory) : AMQPS input: DeserializationInput) : Any = ifThrowsAppend({ clazz.typeName }){ logger.trace { "Calling construction based construction for ${clazz.typeName}" } - return construct(obj.zip(propertySerializers.getters).map { it.second.readProperty(it.first, schemas, input) }) + return construct (propertySerializers.serializationOrder + .zip(obj) + .map { Pair(it.first.initialPosition, it.first.getter.readProperty(it.second, schemas, input)) } + .sortedWith(compareBy({it.first})) + .map { it.second }) } private fun readObjectBuildViaSetters( @@ -93,22 +105,23 @@ open class ObjectSerializer(val clazz: Type, factory: SerializerFactory) : AMQPS val instance : Any = javaConstructor?.newInstance() ?: throw NotSerializableException ( "Failed to instantiate instance of object $clazz") - // read the properties out of the serialised form + // read the properties out of the serialised form, since we're invoking the setters the order we + // do it in doesn't matter val propertiesFromBlob = obj - .zip(propertySerializers.getters) - .map { it.second.readProperty(it.first, schemas, input) } + .zip(propertySerializers.serializationOrder) + .map { it.second.getter.readProperty(it.first, schemas, input) } // one by one take a property and invoke the setter on the class - propertySerializers.setters.zip(propertiesFromBlob).forEach { - it.first?.invoke(instance, *listOf(it.second).toTypedArray()) + propertySerializers.serializationOrder.zip(propertiesFromBlob).forEach { + it.first.set(instance, it.second) } return instance } private fun generateFields(): List { - return propertySerializers.getters.map { - Field(it.name, it.type, it.requires, it.default, null, it.mandatory, false) + return propertySerializers.serializationOrder.map { + Field(it.getter.name, it.getter.type, it.getter.requires, it.getter.default, null, it.getter.mandatory, false) } } diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/PropertySerializer.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/PropertySerializer.kt index af7c089552..f749be0ca8 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/PropertySerializer.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/PropertySerializer.kt @@ -1,69 +1,8 @@ package net.corda.nodeapi.internal.serialization.amqp -import net.corda.core.utilities.loggerFor import org.apache.qpid.proton.amqp.Binary import org.apache.qpid.proton.codec.Data -import java.lang.reflect.Method import java.lang.reflect.Type -import java.lang.reflect.Field -import kotlin.reflect.full.memberProperties -import kotlin.reflect.jvm.javaGetter -import kotlin.reflect.jvm.kotlinProperty - -abstract class PropertyReader { - abstract fun read(obj: Any?): Any? - abstract fun isNullable(): Boolean -} - -class PublicPropertyReader(private val readMethod: Method?) : PropertyReader() { - init { - readMethod?.isAccessible = true - } - - private fun Method.returnsNullable(): Boolean { - try { - val returnTypeString = this.declaringClass.kotlin.memberProperties.firstOrNull { it.javaGetter == this }?.returnType?.toString() ?: "?" - return returnTypeString.endsWith('?') || returnTypeString.endsWith('!') - } catch (e: kotlin.reflect.jvm.internal.KotlinReflectionInternalError) { - // This might happen for some types, e.g. kotlin.Throwable? - the root cause of the issue is: https://youtrack.jetbrains.com/issue/KT-13077 - // TODO: Revisit this when Kotlin issue is fixed. - - loggerFor().error("Unexpected internal Kotlin error", e) - return true - } - } - - override fun read(obj: Any?): Any? { - return readMethod!!.invoke(obj) - } - - override fun isNullable(): Boolean = readMethod?.returnsNullable() ?: false -} - -class PrivatePropertyReader(val field: Field, parentType: Type) : PropertyReader() { - init { - loggerFor().warn("Create property Serializer for private property '${field.name}' not " - + "exposed by a getter on class '$parentType'\n" - + "\tNOTE: This behaviour will be deprecated at some point in the future and a getter required") - } - - override fun read(obj: Any?): Any? { - field.isAccessible = true - val rtn = field.get(obj) - field.isAccessible = false - return rtn - } - - override fun isNullable() = try { - field.kotlinProperty?.returnType?.isMarkedNullable ?: false - } catch (e: kotlin.reflect.jvm.internal.KotlinReflectionInternalError) { - // This might happen for some types, e.g. kotlin.Throwable? - the root cause of the issue is: https://youtrack.jetbrains.com/issue/KT-13077 - // TODO: Revisit this when Kotlin issue is fixed. - loggerFor().error("Unexpected internal Kotlin error", e) - true - } -} - /** * Base class for serialization of a property of an object. @@ -106,13 +45,13 @@ sealed class PropertySerializer(val name: String, val propertyReader: PropertyRe companion object { fun make(name: String, readMethod: PropertyReader, resolvedType: Type, factory: SerializerFactory): PropertySerializer { - if (SerializerFactory.isPrimitive(resolvedType)) { - return when (resolvedType) { + return if (SerializerFactory.isPrimitive(resolvedType)) { + when (resolvedType) { Char::class.java, Character::class.java -> AMQPCharPropertySerializer(name, readMethod) else -> AMQPPrimitivePropertySerializer(name, readMethod, resolvedType) } } else { - return DescribedTypePropertySerializer(name, readMethod, resolvedType) { factory.get(null, resolvedType) } + DescribedTypePropertySerializer(name, readMethod, resolvedType) { factory.get(null, resolvedType) } } } } diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/PropertySerializers.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/PropertySerializers.kt new file mode 100644 index 0000000000..537912a044 --- /dev/null +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/PropertySerializers.kt @@ -0,0 +1,179 @@ +package net.corda.nodeapi.internal.serialization.amqp + +import net.corda.core.utilities.loggerFor +import java.io.NotSerializableException +import java.lang.reflect.Method +import java.lang.reflect.Type +import kotlin.reflect.full.memberProperties +import kotlin.reflect.jvm.javaGetter +import kotlin.reflect.jvm.kotlinProperty +import java.lang.reflect.Field + +abstract class PropertyReader { + abstract fun read(obj: Any?): Any? + abstract fun isNullable(): Boolean +} + +class PublicPropertyReader(private val readMethod: Method?) : PropertyReader() { + init { + readMethod?.isAccessible = true + } + + private fun Method.returnsNullable(): Boolean { + try { + val returnTypeString = this.declaringClass.kotlin.memberProperties.firstOrNull { + it.javaGetter == this + }?.returnType?.toString() ?: "?" + + return returnTypeString.endsWith('?') || returnTypeString.endsWith('!') + } catch (e: kotlin.reflect.jvm.internal.KotlinReflectionInternalError) { + // This might happen for some types, e.g. kotlin.Throwable? - the root cause of the issue + // is: https://youtrack.jetbrains.com/issue/KT-13077 + // TODO: Revisit this when Kotlin issue is fixed. + + loggerFor().error("Unexpected internal Kotlin error", e) + return true + } + } + + override fun read(obj: Any?): Any? { + return readMethod!!.invoke(obj) + } + + override fun isNullable(): Boolean = readMethod?.returnsNullable() ?: false +} + +class PrivatePropertyReader(val field: Field, parentType: Type) : PropertyReader() { + init { + loggerFor().warn("Create property Serializer for private property '${field.name}' not " + + "exposed by a getter on class '$parentType'\n" + + "\tNOTE: This behaviour will be deprecated at some point in the future and a getter required") + } + + override fun read(obj: Any?): Any? { + field.isAccessible = true + val rtn = field.get(obj) + field.isAccessible = false + return rtn + } + + override fun isNullable() = try { + field.kotlinProperty?.returnType?.isMarkedNullable ?: false + } catch (e: kotlin.reflect.jvm.internal.KotlinReflectionInternalError) { + // This might happen for some types, e.g. kotlin.Throwable? - the root cause of the issue + // is: https://youtrack.jetbrains.com/issue/KT-13077 + // TODO: Revisit this when Kotlin issue is fixed. + loggerFor().error("Unexpected internal Kotlin error", e) + true + } +} + +/** + * Represents a generic interface to a serializable property of an object. + * + * @property initialPosition where in the constructor used for serialization the property occurs. + * @property getter a [PropertySerializer] wrapping access to the property. This will either be a + * method invocation on the getter or, if not publicly accessible, reflection based by temporally + * making the property accessible. + */ +abstract class PropertyAccessor( + val initialPosition: Int, + open val getter: PropertySerializer) { + companion object : Comparator { + override fun compare(p0: PropertyAccessor?, p1: PropertyAccessor?): Int { + return p0?.getter?.name?.compareTo(p1?.getter?.name ?: "") ?: 0 + } + } + + /** + * Override to control how the property is set on the object. + */ + abstract fun set(instance: Any, obj: Any?) + + override fun toString(): String { + return "${getter.name}($initialPosition)" + } +} + +/** + * Implementation of [PropertyAccessor] representing a property of an object that + * is serialized and deserialized via JavaBean getter and setter style methods. + */ +class PropertyAccessorGetterSetter( + initialPosition: Int, + getter: PropertySerializer, + private val setter: Method?) : PropertyAccessor(initialPosition, getter) { + /** + * Invokes the setter on the underlying object passing in the serialized value. + */ + override fun set(instance: Any, obj: Any?) { + setter?.invoke(instance, *listOf(obj).toTypedArray()) + } +} + +/** + * Implementation of [PropertyAccessor] representing a property of an object that + * is serialized via a JavaBean getter but deserialized using the constructor + * of the object the property belongs to. + */ +class PropertyAccessorConstructor( + initialPosition: Int, + override val getter: PropertySerializer) : PropertyAccessor(initialPosition, getter) { + /** + * Because the property should be being set on the obejct through the constructor any + * calls to the explicit setter should be an error. + */ + override fun set(instance: Any, obj: Any?) { + NotSerializableException ("Attempting to access a setter on an object being instantiated " + + "via its constructor.") + } +} + +/** + * Represents a collection of [PropertyAccessor]s that represent the serialized form + * of an object. + * + * @property serializationOrder a list of [PropertyAccessor]. For deterministic serialization + * should be sorted. + * @property size how many properties are being serialized. + * @property byConstructor are the properties of the class represented by this set of properties populated + * on deserialization via the object's constructor or the corresponding setter functions. Should be + * overridden and set appropriately by child types. + */ +abstract class PropertySerializers( + val serializationOrder: List) { + companion object { + fun make(serializationOrder: List) = + when (serializationOrder.firstOrNull()) { + is PropertyAccessorConstructor -> PropertySerializersConstructor(serializationOrder) + is PropertyAccessorGetterSetter -> PropertySerializersSetter(serializationOrder) + null -> PropertySerializersNoProperties() + else -> { + throw NotSerializableException ("Unknown Property Accessor type, cannot create set") + } + } + } + + val size get() = serializationOrder.size + abstract val byConstructor: Boolean +} + +class PropertySerializersNoProperties : PropertySerializers (emptyList()) { + override val byConstructor get() = true +} + +class PropertySerializersConstructor( + serializationOrder: List) : PropertySerializers(serializationOrder) { + override val byConstructor get() = true +} + +class PropertySerializersSetter( + serializationOrder: List) : PropertySerializers(serializationOrder) { + override val byConstructor get() = false +} + +class PropertySerializersEvolution : PropertySerializers(emptyList()) { + override val byConstructor get() = false +} + + diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/Schema.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/Schema.kt index e79336a731..596d04a2be 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/Schema.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/Schema.kt @@ -446,11 +446,12 @@ private fun fingerprintForObject( offset: Int = 0): Hasher { // Hash the class + properties + interfaces val name = type.asClass()?.name ?: throw NotSerializableException("Expected only Class or ParameterizedType but found $type") - propertiesForSerialization(constructorForDeserialization(type), contextType ?: type, factory).getters + propertiesForSerialization(constructorForDeserialization(type), contextType ?: type, factory) + .serializationOrder .fold(hasher.putUnencodedChars(name)) { orig, prop -> - fingerprintForType(prop.resolvedType, type, alreadySeen, orig, factory, offset+4) - .putUnencodedChars(prop.name) - .putUnencodedChars(if (prop.mandatory) NOT_NULLABLE_HASH else NULLABLE_HASH) + fingerprintForType(prop.getter.resolvedType, type, alreadySeen, orig, factory, offset+4) + .putUnencodedChars(prop.getter.name) + .putUnencodedChars(if (prop.getter.mandatory) NOT_NULLABLE_HASH else NULLABLE_HASH) } interfacesForSerialization(type, factory).map { fingerprintForType(it, type, alreadySeen, hasher, factory, offset+4) } return hasher diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializationHelper.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializationHelper.kt index e7b21f5e38..98274bd638 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializationHelper.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializationHelper.kt @@ -2,7 +2,6 @@ package net.corda.nodeapi.internal.serialization.amqp import com.google.common.primitives.Primitives import com.google.common.reflect.TypeToken -import io.netty.util.internal.EmptyArrays import net.corda.core.serialization.ClassWhitelist import net.corda.core.serialization.CordaSerializable import net.corda.core.serialization.SerializationContext @@ -20,7 +19,6 @@ import kotlin.reflect.full.findAnnotation import kotlin.reflect.full.primaryConstructor import kotlin.reflect.jvm.isAccessible import kotlin.reflect.jvm.javaType -import kotlin.reflect.jvm.kotlinProperty /** * Annotation indicating a constructor to be used to reconstruct instances of a class during deserialization. @@ -29,10 +27,6 @@ import kotlin.reflect.jvm.kotlinProperty @Retention(AnnotationRetention.RUNTIME) annotation class ConstructorForDeserialization -data class ConstructorDestructorMethods( - val getters: Collection, - val setters: Collection) - /** * Code for finding the constructor we will use for deserialization. * @@ -74,17 +68,22 @@ internal fun constructorForDeserialization(type: Type): KFunction? { * Note, you will need any Java classes to be compiled with the `-parameters` option to ensure constructor parameters have * names accessible via reflection. */ -internal fun propertiesForSerialization(kotlinConstructor: KFunction?, type: Type, factory: SerializerFactory): ConstructorDestructorMethods { - val clazz = type.asClass()!! - return if (kotlinConstructor != null) propertiesForSerializationFromConstructor(kotlinConstructor, type, factory) else propertiesForSerializationFromAbstract(clazz, type, factory) -} +internal fun propertiesForSerialization( + kotlinConstructor: KFunction?, + type: Type, + factory: SerializerFactory) = PropertySerializers.make ( + if (kotlinConstructor != null) { + propertiesForSerializationFromConstructor(kotlinConstructor, type, factory) + } else { + propertiesForSerializationFromAbstract(type.asClass()!!, type, factory) + }.sortedWith(PropertyAccessor)) fun isConcrete(clazz: Class<*>): Boolean = !(clazz.isInterface || Modifier.isAbstract(clazz.modifiers)) -private fun propertiesForSerializationFromConstructor( +internal fun propertiesForSerializationFromConstructor( kotlinConstructor: KFunction, type: Type, - factory: SerializerFactory): ConstructorDestructorMethods { + factory: SerializerFactory): List { val clazz = (kotlinConstructor.returnType.classifier as KClass<*>).javaObjectType // Kotlin reflection doesn't work with Java getters the way you might expect, so we drop back to good ol' beans. val properties = Introspector.getBeanInfo(clazz).propertyDescriptors @@ -96,39 +95,45 @@ private fun propertiesForSerializationFromConstructor( return propertiesForSerializationFromSetters(properties, type, factory) } - val rc: MutableList = ArrayList(kotlinConstructor.parameters.size) + return mutableListOf().apply { + kotlinConstructor.parameters.withIndex().forEach { param -> + val name = param.value.name ?: throw NotSerializableException("Constructor parameter of $clazz has no name.") - for (param in kotlinConstructor.parameters) { - val name = param.name ?: throw NotSerializableException("Constructor parameter of $clazz has no name.") + val propertyReader = if (name in properties) { + // it's a publicly accessible property + val matchingProperty = properties[name]!! - if (name in properties) { - val matchingProperty = properties[name]!! + // Check that the method has a getter in java. + val getter = matchingProperty.readMethod ?: throw NotSerializableException( + "Property has no getter method for $name of $clazz. If using Java and the parameter name" + + "looks anonymous, check that you have the -parameters option specified in the Java compiler." + + "Alternately, provide a proxy serializer (SerializationCustomSerializer) if " + + "recompiling isn't an option.") - // Check that the method has a getter in java. - val getter = matchingProperty.readMethod ?: throw NotSerializableException("Property has no getter method for $name of $clazz. " + - "If using Java and the parameter name looks anonymous, check that you have the -parameters option specified in the Java compiler." + - "Alternately, provide a proxy serializer (SerializationCustomSerializer) if recompiling isn't an option") - val returnType = resolveTypeVariables(getter.genericReturnType, type) - if (constructorParamTakesReturnTypeOfGetter(returnType, getter.genericReturnType, param)) { - rc += PropertySerializer.make(name, PublicPropertyReader(getter), returnType, factory) + val returnType = resolveTypeVariables(getter.genericReturnType, type) + if (!constructorParamTakesReturnTypeOfGetter(returnType, getter.genericReturnType, param.value)) { + throw NotSerializableException( + "Property type $returnType for $name of $clazz differs from constructor parameter " + + "type ${param.value.type.javaType}") + } + + Pair(PublicPropertyReader(getter), returnType) } else { - throw NotSerializableException("Property type $returnType for $name of $clazz differs from constructor parameter type ${param.type.javaType}") + try { + val field = clazz.getDeclaredField(param.value.name) + Pair(PrivatePropertyReader(field, type), field.genericType) + } catch (e: NoSuchFieldException) { + throw NotSerializableException("No property matching constructor parameter named '$name' of '$clazz'. " + + "If using Java, check that you have the -parameters option specified in the Java compiler. " + + "Alternately, provide a proxy serializer (SerializationCustomSerializer) if recompiling isn't an option") + } } - } else { - try { - val field = (clazz.getDeclaredField(param.name)) - rc += PropertySerializer.make(name, PrivatePropertyReader(field, type), field.genericType, factory) - } catch (e: NoSuchFieldException) { - throw NotSerializableException("No property matching constructor parameter named '$name' of '$clazz'. " + - "If using Java, check that you have the -parameters option specified in the Java compiler. " + - "Alternately, provide a proxy serializer (SerializationCustomSerializer) if recompiling isn't an option") - } + this += PropertyAccessorConstructor( + param.index, + PropertySerializer.make(name, propertyReader.first, propertyReader.second, factory)) } - } - - return ConstructorDestructorMethods(rc, emptyList()) } /** @@ -138,26 +143,26 @@ private fun propertiesForSerializationFromConstructor( private fun propertiesForSerializationFromSetters( properties: Map, type: Type, - factory: SerializerFactory): ConstructorDestructorMethods { - val getters: MutableList = ArrayList(properties.size) - val setters: MutableList = ArrayList(properties.size) + factory: SerializerFactory): List { + return mutableListOf().apply { + var idx = 0 + properties.forEach { property -> + val getter: Method? = property.value.readMethod + val setter: Method? = property.value.writeMethod - properties.forEach { property -> - val getter: Method? = property.value.readMethod - val setter: Method? = property.value.writeMethod + if (getter == null || setter == null) return@forEach - if (getter == null || setter == null) return@forEach + // NOTE: There is no need to check return and parameter types vs the underlying type for + // the getter / setter vs property as if there is a difference then that property isn't reported + // by the BEAN inspector and thus we don't consider that case here - // NOTE: There is no need to check return and parameter types vs the underlying type for - // the getter / setter vs property as if there is a difference then that property isn't reported - // by the BEAN inspector and thus we don't consider that case here - - getters += PropertySerializer.make(property.key, PublicPropertyReader(getter), - resolveTypeVariables(getter.genericReturnType, type), factory) - setters += setter + this += PropertyAccessorGetterSetter ( + idx++, + PropertySerializer.make(property.key, PublicPropertyReader(getter), + resolveTypeVariables(getter.genericReturnType, type), factory), + setter) + } } - - return ConstructorDestructorMethods(getters, setters) } private fun constructorParamTakesReturnTypeOfGetter(getterReturnType: Type, rawGetterReturnType: Type, param: KParameter): Boolean { @@ -168,21 +173,24 @@ private fun constructorParamTakesReturnTypeOfGetter(getterReturnType: Type, rawG private fun propertiesForSerializationFromAbstract( clazz: Class<*>, type: Type, - factory: SerializerFactory): ConstructorDestructorMethods { + factory: SerializerFactory): List { // Kotlin reflection doesn't work with Java getters the way you might expect, so we drop back to good ol' beans. val properties = Introspector.getBeanInfo(clazz).propertyDescriptors .filter { it.name != "class" } .sortedBy { it.name } .filterNot { it is IndexedPropertyDescriptor } - val rc: MutableList = ArrayList(properties.size) - for (property in properties) { - // Check that the method has a getter in java. - val getter = property.readMethod ?: throw NotSerializableException( - "Property has no getter method for ${property.name} of $clazz.") - val returnType = resolveTypeVariables(getter.genericReturnType, type) - rc += PropertySerializer.make(property.name, PublicPropertyReader(getter), returnType, factory) - } - return ConstructorDestructorMethods(rc, emptyList()) + + return mutableListOf().apply { + properties.withIndex().forEach { property -> + // Check that the method has a getter in java. + val getter = property.value.readMethod ?: throw NotSerializableException( + "Property has no getter method for ${property.value.name} of $clazz.") + val returnType = resolveTypeVariables(getter.genericReturnType, type) + this += PropertyAccessorConstructor( + property.index, + PropertySerializer.make(property.value.name, PublicPropertyReader(getter), returnType, factory)) + } + } } internal fun interfacesForSerialization(type: Type, serializerFactory: SerializerFactory): List { diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/custom/ThrowableSerializer.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/custom/ThrowableSerializer.kt index 93d8b0fbed..c4214394f3 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/custom/ThrowableSerializer.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/custom/ThrowableSerializer.kt @@ -23,9 +23,8 @@ class ThrowableSerializer(factory: SerializerFactory) : CustomSerializer.Proxy + extraProperties[property.getter.name] = property.getter.propertyReader.read(obj) } } catch (e: NotSerializableException) { logger.warn("Unexpected exception", e) @@ -90,4 +89,4 @@ class StackTraceElementSerializer(factory: SerializerFactory) : CustomSerializer override fun fromProxy(proxy: StackTraceElementProxy): StackTraceElement = StackTraceElement(proxy.declaringClass, proxy.methodName, proxy.fileName, proxy.lineNumber) data class StackTraceElementProxy(val declaringClass: String, val methodName: String, val fileName: String?, val lineNumber: Int) -} \ No newline at end of file +} diff --git a/node-api/src/test/java/net/corda/nodeapi/internal/serialization/amqp/JavaPrivatePropertyTests.java b/node-api/src/test/java/net/corda/nodeapi/internal/serialization/amqp/JavaPrivatePropertyTests.java index 29f1b949d5..59a1465710 100644 --- a/node-api/src/test/java/net/corda/nodeapi/internal/serialization/amqp/JavaPrivatePropertyTests.java +++ b/node-api/src/test/java/net/corda/nodeapi/internal/serialization/amqp/JavaPrivatePropertyTests.java @@ -47,9 +47,9 @@ public class JavaPrivatePropertyTests { assertEquals(1, serializersByDescriptor.size()); ObjectSerializer cSerializer = ((ObjectSerializer)serializersByDescriptor.values().toArray()[0]); - assertEquals(1, cSerializer.getPropertySerializers().component1().size()); - Object[] propertyReaders = cSerializer.getPropertySerializers().component1().toArray(); - assertTrue (((PropertySerializer)propertyReaders[0]).getPropertyReader() instanceof PrivatePropertyReader); + assertEquals(1, cSerializer.getPropertySerializers().getSerializationOrder().size()); + Object[] propertyReaders = cSerializer.getPropertySerializers().getSerializationOrder().toArray(); + assertTrue (((PropertyAccessor)propertyReaders[0]).getGetter().getPropertyReader() instanceof PrivatePropertyReader); } @Test @@ -76,8 +76,8 @@ public class JavaPrivatePropertyTests { assertEquals(1, serializersByDescriptor.size()); ObjectSerializer cSerializer = ((ObjectSerializer)serializersByDescriptor.values().toArray()[0]); - assertEquals(1, cSerializer.getPropertySerializers().component1().size()); - Object[] propertyReaders = cSerializer.getPropertySerializers().component1().toArray(); - assertTrue (((PropertySerializer)propertyReaders[0]).getPropertyReader() instanceof PublicPropertyReader); + assertEquals(1, cSerializer.getPropertySerializers().getSerializationOrder().size()); + Object[] propertyReaders = cSerializer.getPropertySerializers().getSerializationOrder().toArray(); + assertTrue (((PropertyAccessor)propertyReaders[0]).getGetter().getPropertyReader() instanceof PublicPropertyReader); } } diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/PrivatePropertyTests.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/PrivatePropertyTests.kt index 51405e8c26..0ee20ab311 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/PrivatePropertyTests.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/PrivatePropertyTests.kt @@ -2,12 +2,23 @@ package net.corda.nodeapi.internal.serialization.amqp import junit.framework.TestCase.assertTrue import junit.framework.TestCase.assertEquals +import org.slf4j.Logger +import org.slf4j.LoggerFactory import org.junit.Test import org.apache.qpid.proton.amqp.Symbol import java.util.concurrent.ConcurrentHashMap class PrivatePropertyTests { - private val factory = testDefaultFactory() + private val factory = testDefaultFactoryNoEvolution() + + companion object { + val fields : Map = mapOf ( + "serializersByDesc" to SerializerFactory::class.java.getDeclaredField("serializersByDescriptor")).apply { + this.values.forEach { + it.isAccessible = true + } + } + } @Test fun testWithOnePrivateProperty() { @@ -53,16 +64,14 @@ class PrivatePropertyTests { val schemaAndBlob = SerializationOutput(factory).serializeAndReturnSchema(c1) assertEquals(1, schemaAndBlob.schema.types.size) - val field = SerializerFactory::class.java.getDeclaredField("serializersByDescriptor") - field.isAccessible = true @Suppress("UNCHECKED_CAST") - val serializersByDescriptor = field.get(factory) as ConcurrentHashMap> + val serializersByDescriptor = fields["serializersByDesc"]?.get(factory) as ConcurrentHashMap> val schemaDescriptor = schemaAndBlob.schema.types.first().descriptor.name serializersByDescriptor.filterKeys { (it as Symbol) == schemaDescriptor }.values.apply { assertEquals(1, this.size) assertTrue(this.first() is ObjectSerializer) - val propertySerializers = (this.first() as ObjectSerializer).propertySerializers.getters.toList() + val propertySerializers = (this.first() as ObjectSerializer).propertySerializers.serializationOrder.map { it.getter } assertEquals(2, propertySerializers.size) // a was public so should have a synthesised getter assertTrue(propertySerializers[0].propertyReader is PublicPropertyReader) @@ -84,16 +93,14 @@ class PrivatePropertyTests { val schemaAndBlob = SerializationOutput(factory).serializeAndReturnSchema(c1) assertEquals(1, schemaAndBlob.schema.types.size) - val field = SerializerFactory::class.java.getDeclaredField("serializersByDescriptor") - field.isAccessible = true @Suppress("UNCHECKED_CAST") - val serializersByDescriptor = field.get(factory) as ConcurrentHashMap> + val serializersByDescriptor = fields["serializersByDesc"]?.get(factory) as ConcurrentHashMap> val schemaDescriptor = schemaAndBlob.schema.types.first().descriptor.name serializersByDescriptor.filterKeys { (it as Symbol) == schemaDescriptor }.values.apply { assertEquals(1, this.size) assertTrue(this.first() is ObjectSerializer) - val propertySerializers = (this.first() as ObjectSerializer).propertySerializers.getters.toList() + val propertySerializers = (this.first() as ObjectSerializer).propertySerializers.serializationOrder.map { it.getter } assertEquals(2, propertySerializers.size) // as before, a is public so we'll use the getter method @@ -105,13 +112,24 @@ class PrivatePropertyTests { } } + @Suppress("UNCHECKED_CAST") @Test fun testNested() { data class Inner(private val a: Int) data class Outer(private val i: Inner) val c1 = Outer(Inner(1010101)) - val c2 = DeserializationInput(factory).deserialize(SerializationOutput(factory).serialize(c1)) + val output = SerializationOutput(factory).serializeAndReturnSchema(c1) + println (output.schema) + + val serializersByDescriptor = fields["serializersByDesc"]!!.get(factory) as ConcurrentHashMap> + + // Inner and Outer + assertEquals(2, serializersByDescriptor.size) + + val schemaDescriptor = output.schema.types.first().descriptor.name + val c2 = DeserializationInput(factory).deserialize(output.obj) + assertEquals(c1, c2) } } \ No newline at end of file diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializationPropertyOrdering.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializationPropertyOrdering.kt new file mode 100644 index 0000000000..c23572db41 --- /dev/null +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializationPropertyOrdering.kt @@ -0,0 +1,125 @@ +package net.corda.nodeapi.internal.serialization.amqp + +import org.junit.Test +import java.util.concurrent.ConcurrentHashMap +import kotlin.test.assertEquals +import org.apache.qpid.proton.amqp.Symbol +import java.lang.reflect.Method +import kotlin.test.assertNotNull +import kotlin.test.assertTrue + +class SerializationPropertyOrdering { + companion object { + val VERBOSE get() = false + + val sf = testDefaultFactoryNoEvolution() + } + + // Force object references to be ued to ensure we go through that code path + // this test shows (not now it's fixed) a bug whereby deserializing objects + // would break where refferenced objects were accessed before they'd been + // processed thanks to the way the blob was deserialized + @Test + fun refferenceOrdering() { + data class Reffed(val c: String, val b: String, val a: String) + data class User(val b: List, val a: List) + + val r1 = Reffed("do not", "or", "do") + val r2 = Reffed("do not", "or", "do") + val l = listOf(r1, r2, r1, r2, r1, r2) + + val u = User(l,l) + val output = TestSerializationOutput(VERBOSE, sf).serializeAndReturnSchema(u) + val input = DeserializationInput(sf).deserialize(output.obj) + } + + @Test + fun randomOrder() { + data class C(val c: Int, val d: Int, val b: Int, val e: Int, val a: Int) + + val c = C(3,4,2,5,1) + val output = TestSerializationOutput(VERBOSE, sf).serializeAndReturnSchema(c) + + // the schema should reflect the serialized order of properties, not the + // construction order + assertEquals(1, output.schema.types.size) + output.schema.types.firstOrNull()?.apply { + assertEquals(5, (this as CompositeType).fields.size) + assertEquals("a", this.fields[0].name) + assertEquals("b", this.fields[1].name) + assertEquals("c", this.fields[2].name) + assertEquals("d", this.fields[3].name) + assertEquals("e", this.fields[4].name) + } + + // and deserializing it should construct the object as it was and not in the order prescribed + // by the serialized form + val input = DeserializationInput(sf).deserialize(output.obj) + assertEquals(1, input.a) + assertEquals(2, input.b) + assertEquals(3, input.c) + assertEquals(4, input.d) + assertEquals(5, input.e) + } + + @Suppress("UNCHECKED_CAST") + @Test + fun randomOrderSetter() { + data class C(var c: Int, var d: Int, var b: Int, var e: Int, var a: Int) { + // This will force the serialization engine to use getter / setter + // instantiation for the object rather than construction + @ConstructorForDeserialization + @Suppress("UNUSED") + constructor() : this(0, 0, 0, 0, 0) + } + + val c = C() + + c.a = 100 + c.b = 200 + c.c = 300 + c.d = 400 + c.e = 500 + + val output = TestSerializationOutput(VERBOSE, sf).serializeAndReturnSchema(c) + + // the schema should reflect the serialized order of properties, not the + // construction order + assertEquals(1, output.schema.types.size) + output.schema.types.firstOrNull()?.apply { + assertEquals(5, (this as CompositeType).fields.size) + assertEquals("a", this.fields[0].name) + assertEquals("b", this.fields[1].name) + assertEquals("c", this.fields[2].name) + assertEquals("d", this.fields[3].name) + assertEquals("e", this.fields[4].name) + } + + // Test needs to look at a bunch of private variables, change the access semantics for them + val fields : Map = mapOf ( + "serializersByDesc" to SerializerFactory::class.java.getDeclaredField("serializersByDescriptor"), + "setter" to PropertyAccessorGetterSetter::class.java.getDeclaredField("setter")).apply { + this.values.forEach { + it.isAccessible = true + } + } + + val serializersByDescriptor = fields["serializersByDesc"]!!.get(sf) as ConcurrentHashMap> + val schemaDescriptor = output.schema.types.first().descriptor.name + + // make sure that each property accessor has a setter to ensure we're using getter / setter instantiation + serializersByDescriptor.filterKeys { (it as Symbol) == schemaDescriptor }.values.apply { + assertEquals(1, this.size) + assertTrue(this.first() is ObjectSerializer) + val propertyAccessors = (this.first() as ObjectSerializer).propertySerializers.serializationOrder as List + propertyAccessors.forEach { property -> assertNotNull(fields["setter"]!!.get(property) as Method?) } + } + + val input = DeserializationInput(sf).deserialize(output.obj) + assertEquals(100, input.a) + assertEquals(200, input.b) + assertEquals(300, input.c) + assertEquals(400, input.d) + assertEquals(500, input.e) + } +} \ No newline at end of file From 038c4eb615f946f00fcbe4c881d2d26884592ce9 Mon Sep 17 00:00:00 2001 From: Joel Dudley Date: Thu, 18 Jan 2018 11:04:07 +0000 Subject: [PATCH 02/36] Removes cli vs ide from quickstart. Adds link to new utilities page. --- docs/source/CLI-vs-IDE.rst | 67 -------------------------------- docs/source/quickstart-index.rst | 5 +-- 2 files changed, 2 insertions(+), 70 deletions(-) delete mode 100644 docs/source/CLI-vs-IDE.rst diff --git a/docs/source/CLI-vs-IDE.rst b/docs/source/CLI-vs-IDE.rst deleted file mode 100644 index a4888a7adb..0000000000 --- a/docs/source/CLI-vs-IDE.rst +++ /dev/null @@ -1,67 +0,0 @@ -CLI vs IDE -========== - -We have tried to make every example, tutorial and sample usable via both the command line and the IntelliJ IDE. -Most developers will find writing, editing and debugging code more easy with tools such as an IDE. But when a production node -is deployed, it will be controlled via the command line - no organisation allows their systems to be running via -a developer environment. - -IDE - IntelliJ --------------- - -IntelliJ (the preferred IDE for Corda) integrates well with gradle (Corda's default build, deployment and CLI tool). -IntelliJ understands gradle tasks and dependencies, automatically loading them in the background when a project is -first opened or the gradle project changes. Occasionally, however, you may need to refresh the gradle project manually -- but this is hinted to you by the IDE. It's a good idea to do this before carrying on with other work (and in fact you -may find it is essential to pick up new libraries, etc.). - -There are some great resources about how to get started using IntelliJ. As opposed to trying to repeat them here, we advise -you to go to the `IntelliJ docs here `_. - -Command Line ------------- - -Windows vs Mac / Unix -********************* - -Due to the nature of their respective command interfaces, gradle is typically ran in windows with the command ``gradle.bat`` -(or ``gradlew.bat`` if using the wrapper) and in Mac / Unix environments it is ran via ``./gradlew``. For brevity, the -simple windows syntax ``gradle`` is used for the majority of the documentation. - -As well as including the most significant run and build configurations in the IDE, we also provide gradle tasks to build, install -and run significant parts of Corda demos and tools. Gradle is highly extensible and we use it for downloading required resources, -building components, installing those built components into shared areas, configuring the scripts that run nodes, starting -up demonstration API calls amongst other things. It is exceptionally good at deriving dependency maps and therefore performing -the preceding tasks required in order to do the requested task. However, when confusing build errors manifest, then sometimes -a ``gradle clean`` may be required in order to clear out any build areas that have an inconsistent state. The total build time -from downloading / cloning the repo to a complete build should be only a few minutes, obviously slightly longer if the -unit tests are run. - -Frequently Used Gradle Tasks -**************************** - -Note that the list of tasks can be ran for any gradle project can be displayed by running the task ``tasks``. Also, note that -gradle is hierarchical and therefore tasks in child directories can be run using a colon separator. For example, if you want to run -the sample attachment demo run configuration ``runSender``, you would use the command ``gradle samples:attachment-demo:runSender`` - -The most frequent gradle tasks you will probably be running are ``build`` and ``install``. The ``build`` command also executes the -unit tests as well. If you want to build without this level of verification, then use the ``assemble`` command - but we do -not recommend this. After running build, the ``install`` tasks copies over the built jars into the local maven repository -which will then make these available for either the sample code or use with the CorDapp template. - -Debugging ---------- - -Tasks and processes that are run directly via the IDE (including via the usage of the ``driver`` DSL) can be remotely debugged. -We do not have java debugging currently enabled in the ``runnodes`` scripts generated by a process we refer to as 'cordformation' -but we will be implementing that shortly. - -Via the IDE -*********** - -To debug: From the IDE, configure the debug connectivity option by the "Edit Configurations" and choosing "+" and then "Remote". -The debug port start at 5005 and increments for each additional node that starts, the order given by the list in the main -driver configuration (which is primarily listed in the ``main`` function of ``Main.kt`` for each sample. Look for the string -``Listening for transport dt_socket at address:5xxx`` in the log output to determine the exact port for that node. If the log -messages are mixed from several nodes to the same console, then (as earlier stated), the port numbers increment in the order -they are listed in the driver DSL configuration. diff --git a/docs/source/quickstart-index.rst b/docs/source/quickstart-index.rst index 02e9045ac9..fa884b229d 100644 --- a/docs/source/quickstart-index.rst +++ b/docs/source/quickstart-index.rst @@ -6,6 +6,5 @@ Quickstart getting-set-up tutorial-cordapp - Sample CorDapps - building-against-master - CLI-vs-IDE \ No newline at end of file + Other CorDapps + Utilities \ No newline at end of file From 4d4f12d5981c5a7dc9037e5f8e9a325b6ee71691 Mon Sep 17 00:00:00 2001 From: Joel Dudley Date: Thu, 18 Jan 2018 11:10:52 +0000 Subject: [PATCH 03/36] Reworks upgrade notes to improve structure and style. --- docs/source/upgrade-notes.rst | 367 +++++++++++++++++----------------- 1 file changed, 186 insertions(+), 181 deletions(-) diff --git a/docs/source/upgrade-notes.rst b/docs/source/upgrade-notes.rst index b7add79336..be5b870a5d 100644 --- a/docs/source/upgrade-notes.rst +++ b/docs/source/upgrade-notes.rst @@ -31,46 +31,50 @@ We also strongly recommend cross referencing with the :doc:`changelog` to confir UNRELEASED ---------- -* For existing contract ORM schemas that extend from `CommonSchemaV1.LinearState` or `CommonSchemaV1.FungibleState`, - you will need to explicitly map the `participants` collection to a database table. Previously this mapping was done in the - superclass, but that makes it impossible to properly configure the table name. - The required change is to add the ``override var participants: MutableSet? = null`` field to your class, and - add JPA mappings. For ex., see this example: +* For existing contract ORM schemas that extend from ``CommonSchemaV1.LinearState`` or ``CommonSchemaV1.FungibleState``, + you will need to explicitly map the ``participants`` collection to a database table. Previously this mapping was done + in the superclass, but that makes it impossible to properly configure the table name. The required changes are to: -.. sourcecode:: kotlin + * Add the ``override var participants: MutableSet? = null`` field to your class, and + * Add JPA mappings - @Entity - @Table(name = "cash_states_v2", - indexes = arrayOf(Index(name = "ccy_code_idx2", columnList = "ccy_code"))) - class PersistentCashState( + For example: - @ElementCollection - @Column(name = "participants") - @CollectionTable(name="cash_states_v2_participants", joinColumns = arrayOf( - JoinColumn(name = "output_index", referencedColumnName = "output_index"), - JoinColumn(name = "transaction_id", referencedColumnName = "transaction_id"))) - override var participants: MutableSet? = null, + .. sourcecode:: kotlin + + @Entity + @Table(name = "cash_states_v2", + indexes = arrayOf(Index(name = "ccy_code_idx2", columnList = "ccy_code"))) + class PersistentCashState( + + @ElementCollection + @Column(name = "participants") + @CollectionTable(name="cash_states_v2_participants", joinColumns = arrayOf( + JoinColumn(name = "output_index", referencedColumnName = "output_index"), + JoinColumn(name = "transaction_id", referencedColumnName = "transaction_id"))) + override var participants: MutableSet? = null, Testing ~~~~~~~ -* The registration mechanism for CorDapps in ``MockNetwork`` unit tests has changed. +* The registration mechanism for CorDapps in ``MockNetwork`` unit tests has changed: - It is now done via the ``cordappPackages`` constructor parameter of MockNetwork. - This takes a list of `String` values which should be the - package names of the CorDapps containing the contract verification code you wish to load. - The ``unsetCordappPackages`` method is now redundant and has been removed. + * CorDapp registration is now done via the ``cordappPackages`` constructor parameter of MockNetwork. This parameter + is a list of ``String`` values which should be the package names of the CorDapps containing the contract + verification code you wish to load + + * The ``unsetCordappPackages`` method is now redundant and has been removed V1.0 to V2.0 ------------ -You only need to update the ``corda_release_version`` identifier in your project gradle file. The -corda_gradle_plugins_version should remain at 1.0.0: +* You need to update the ``corda_release_version`` identifier in your project gradle file. The + corda_gradle_plugins_version should remain at 1.0.0: -.. sourcecode:: shell + .. sourcecode:: shell - ext.corda_release_version = '2.0.0' - ext.corda_gradle_plugins_version = '1.0.0' + ext.corda_release_version = '2.0.0' + ext.corda_gradle_plugins_version = '1.0.0' Public Beta (M12) to V1.0 ------------------------- @@ -81,203 +85,195 @@ Public Beta (M12) to V1.0 Build ^^^^^ -* MockNetwork has moved. +* MockNetwork has moved. To continue using ``MockNetwork`` for testing, you must add the following dependency to your + ``build.gradle`` file: - A new test driver module dependency needs to be including in your project: `corda-node-driver`. To continue using the - mock network for testing, add the following entry to your gradle build file: + .. sourcecode:: shell -.. sourcecode:: shell + testCompile "net.corda:corda-node-driver:$corda_release_version" - testCompile "net.corda:corda-node-driver:$corda_release_version" - -.. note:: you may only need `testCompile "net.corda:corda-test-utils:$corda_release_version"` if not using the Driver DSL. + .. note:: You may only need ``testCompile "net.corda:corda-test-utils:$corda_release_version"`` if not using the Driver + DSL Configuration ^^^^^^^^^^^^^ -* ``CordaPluginRegistry`` has been removed. - The one remaining configuration item ``customizeSerialisation``, which defined a optional whitelist of types for use in - object serialization, has been replaced with the ``SerializationWhitelist`` interface which should be implemented to - define a list of equivalent whitelisted classes. - You will need to rename your services resource file to the new class name: - 'resources/META-INF/services/net.corda.core.node.CordaPluginRegistry' becomes 'resources/META-INF/services/net.corda.core.serialization.SerializationWhitelist' - An associated property on ``MockNode`` was renamed from ``testPluginRegistries`` to ``testSerializationWhitelists``. - In general, the ``@CordaSerializable`` annotation is the preferred method for whitelisting as described in :doc:`serialization` +* ``CordaPluginRegistry`` has been removed: + + * The one remaining configuration item ``customizeSerialisation``, which defined a optional whitelist of types for + use in object serialization, has been replaced with the ``SerializationWhitelist`` interface which should be + implemented to define a list of equivalent whitelisted classes + + * You will need to rename your services resource file. 'resources/META-INF/services/net.corda.core.node.CordaPluginRegistry' + becomes 'resources/META-INF/services/net.corda.core.serialization.SerializationWhitelist' + + * ``MockNode.testPluginRegistries`` was renamed to ``MockNode.testSerializationWhitelists`` + + * In general, the ``@CordaSerializable`` annotation is the preferred method for whitelisting, as described in + :doc:`serialization` Missing imports ^^^^^^^^^^^^^^^ -Use the automatic imports feature of IntelliJ to intelligently resolve the new imports. +Use IntelliJ's automatic imports feature to intelligently resolve the new imports: -* Missing imports for contract types. +* Missing imports for contract types: - CommercialPaper and Cash are now contained within the `finance` module, as are associated helpers functions. - For example: - ``import net.corda.contracts.ICommercialPaperState`` becomes ``import net.corda.finance.contracts.ICommercialPaperState`` + * CommercialPaper and Cash are now contained within the ``finance`` module, as are associated helpers functions. For + example: - ``import net.corda.contracts.asset.sumCashBy`` becomes ``import net.corda.finance.utils.sumCashBy`` + * ``import net.corda.contracts.ICommercialPaperState`` becomes ``import net.corda.finance.contracts.ICommercialPaperState`` - ``import net.corda.core.contracts.DOLLARS`` becomes ``import net.corda.finance.DOLLARS`` + * ``import net.corda.contracts.asset.sumCashBy`` becomes ``import net.corda.finance.utils.sumCashBy`` - ``import net.corda.core.contracts.issued by`` becomes ``import net.corda.finance.issued by`` + * ``import net.corda.core.contracts.DOLLARS`` becomes ``import net.corda.finance.DOLLARS`` - ``import net.corda.contracts.asset.Cash`` becomes ``import net.corda.finance.contracts.asset.Cash`` + * ``import net.corda.core.contracts.issued by`` becomes ``import net.corda.finance.issued by`` -* Missing imports for utility functions. + * ``import net.corda.contracts.asset.Cash`` becomes ``import net.corda.finance.contracts.asset.Cash`` - Many common types and helper methods have been consolidated into `net.corda.core.utilities` package. - For example: - ``import net.corda.core.crypto.commonName`` becomes ``import net.corda.core.utilities.commonName`` +* Missing imports for utility functions: - ``import net.corda.core.crypto.toBase58String`` becomes ``import net.corda.core.utilities.toBase58String`` + * Many common types and helper methods have been consolidated into ``net.corda.core.utilities`` package. For example: - ``import net.corda.core.getOrThrow`` becomes ``import net.corda.core.utilities.getOrThrow`` + * ``import net.corda.core.crypto.commonName`` becomes ``import net.corda.core.utilities.commonName`` -* Missing flow imports. + * ``import net.corda.core.crypto.toBase58String`` becomes ``import net.corda.core.utilities.toBase58String`` - In general all reusable library flows are contained within the **core** API `net.corda.core.flows` package. - Financial domain library flows are contained within the **finance** module `net.corda.finance.flows` package. - Other flows that have moved include: + * ``import net.corda.core.getOrThrow`` becomes ``import net.corda.core.utilities.getOrThrow`` - ``import net.corda.core.flows.ResolveTransactionsFlow`` becomes ``import net.corda.core.internal.ResolveTransactionsFlow`` +* Missing flow imports: + + * In general, all reusable library flows are contained within the **core** API ``net.corda.core.flows`` package + + * Financial domain library flows are contained within the **finance** module ``net.corda.finance.flows`` package + + * Other flows that have moved include ``import net.corda.core.flows.ResolveTransactionsFlow``, which becomes + ``import net.corda.core.internal.ResolveTransactionsFlow`` Core data structures ^^^^^^^^^^^^^^^^^^^^ -* Missing Contract override. +* Missing ``Contract`` override: - The contract interace attribute ``legalContractReference`` has been removed, and replaced by - the optional annotation ``@LegalProseReference(uri = "")`` + * ``Contract.legalContractReference`` has been removed, and replaced by the optional annotation + ``@LegalProseReference(uri = "")`` -* Unresolved reference. +* Unresolved reference: - Calls to ``AuthenticatedObject`` are replaced by ``CommandWithParties`` + * ``AuthenticatedObject`` was renamed to ``CommandWithParties`` -* Overrides nothing: ``isRelevant`` in ``LinearState``. +* Overrides nothing: - Removed the concept of relevancy from ``LinearState``. A ``ContractState``'s relevance to the vault is now resolved - internally; the vault will process any transaction from a flow which is not derived from transaction resolution verification. - The notion of relevancy is subject to further improvements to enable a developer to control what state the vault thinks - are relevant. + * ``LinearState.isRelevant`` was removed. Whether a node stores a ``LinearState`` in its vault depends on whether the + node is one of the state's ``participants`` -* Calls to ``txBuilder.toLedgerTransaction()`` now requires a serviceHub parameter. - - Used by the new Contract Constraints functionality to validate and resolve attachments. + * ``txBuilder.toLedgerTransaction`` now requires a ``ServiceHub`` parameter. This is used by the new Contract + Constraints functionality to validate and resolve attachments Flow framework ^^^^^^^^^^^^^^ -* Flow session deprecations +* ``FlowLogic`` communication has been upgraded to use explicit ``FlowSession`` instances to communicate between nodes: - ``FlowLogic`` communication has been upgraded to use functions on ``FlowSession`` as the base for communication - between nodes. - - * Calls to ``send()``, ``receive()`` and ``sendAndReceive()`` on FlowLogic should be replaced with calls - to the function of the same name on ``FlowSession``. Note that the replacement functions do not take in a destination - parameter, as this is defined in the session. + * ``FlowLogic.send``/``FlowLogic.receive``/``FlowLogic.sendAndReceive`` has been replaced by ``FlowSession.send``/ + ``FlowSession.receive``/``FlowSession.sendAndReceive``. The replacement functions do not take a destination + parameter, as this is defined implictly by the session used * Initiated flows now take in a ``FlowSession`` instead of ``Party`` in their constructor. If you need to access the - counterparty identity, it is in the ``counterparty`` property of the flow session. - - See ``FlowSession`` for step by step instructions on porting existing flows to use the new mechanism. + counterparty identity, it is in the ``counterparty`` property of the flow session * ``FinalityFlow`` now returns a single ``SignedTransaction``, instead of a ``List`` -* ``TransactionKeyFlow`` renamed to ``SwapIdentitiesFlow`` +* ``TransactionKeyFlow`` was renamed to ``SwapIdentitiesFlow`` - Note that ``SwapIdentitiesFlow`` must be imported from the *confidential-identities** package ''net.corda.confidential'' +* ``SwapIdentitiesFlow`` must be imported from the *confidential-identities* package ``net.corda.confidential`` Node services (ServiceHub) ^^^^^^^^^^^^^^^^^^^^^^^^^^ -* VaultQueryService: unresolved reference to `vaultQueryService`. +* Unresolved reference to ``vaultQueryService``: - Replace all references to ``.vaultQueryService`` with ``.vaultService``. - Previously there were two vault APIs. Now there is a single unified API with the same functions: ``VaultService``. + * Replace all references to ``.vaultQueryService`` with ``.vaultService`` -* ``serviceHub.myInfo.legalIdentity`` no longer exists; use the ``ourIdentity`` property of the flow instead. + * Previously there were two vault APIs. Now there is a single unified API with the same functions: ``VaultService``. - ``FlowLogic.ourIdentity`` has been introduced as a shortcut for retrieving our identity in a flow +* ``FlowLogic.ourIdentity`` has been introduced as a shortcut for retrieving our identity in a flow -* ``getAnyNotary`` is gone - use ``serviceHub.networkMapCache.notaryIdentities[0]`` instead +* ``serviceHub.myInfo.legalIdentity`` no longer exists - Note: ongoing work to support multiple notary identities is still in progress. +* ``getAnyNotary`` has been removed. Use ``serviceHub.networkMapCache.notaryIdentities[0]`` instead * ``ServiceHub.networkMapUpdates`` is replaced by ``ServiceHub.networkMapFeed`` * ``ServiceHub.partyFromX500Name`` is replaced by ``ServiceHub.wellKnownPartyFromX500Name`` - Note: A "well known" party is one that isn't anonymous and this change was motivated by the confidential identities work. + + * A "well known" party is one that isn't anonymous. This change was motivated by the confidential identities work RPC Client ^^^^^^^^^^ -* Missing API methods on `CordaRPCOps` interface. +* Missing API methods on the ``CordaRPCOps`` interface: - * Calls to ``verifiedTransactionsFeed()`` and ``verifiedTransactions()`` have been replaced with: - ``internalVerifiedTransactionsSnapshot()`` and ``internalVerifiedTransactionsFeed()`` respectively + * ``verifiedTransactionsFeed`` has been replaced by ``internalVerifiedTransactionsFeed`` - This is in preparation for the planned integration of Intel SGX™, which will encrypt the transactions feed. - Apps that use this API will not work on encrypted ledgers: you should probably be using the vault query API instead. + * ``verifiedTransactions`` has been replaced by ``internalVerifiedTransactionsSnapshot`` - * Accessing the `networkMapCache` via ``services.nodeInfo().legalIdentities`` returns a list of identities. - The first element in the list is the Party object referring to a node's single identity. + * These changes are in preparation for the planned integration of Intel SGX™, which will encrypt the transactions + feed. Apps that use this API will not work on encrypted ledgers. They should generally be modified to use the vault + query API instead - This is in preparation for allowing a node to host multiple separate identities in future. + * Accessing the ``networkMapCache`` via ``services.nodeInfo().legalIdentities`` returns a list of identities + + * This change is in preparation for allowing a node to host multiple separate identities in the future Testing ^^^^^^^ -Please note that `Clauses` have been removed completely as of V1.0. -We will be revisiting this capability in a future release. +Please note that ``Clauses`` have been removed completely as of V1.0. We will be revisiting this capability in a future +release. -* CorDapps must be explicitly registered in ``MockNetwork`` unit tests. +* CorDapps must be explicitly registered in ``MockNetwork`` unit tests: - This is done by calling ``setCordappPackages``, an extension helper function in the ``net.corda.testing`` package, - on the first line of your `@Before` method. This takes a variable number of `String` arguments which should be the - package names of the CorDapps containing the contract verification code you wish to load. - You should unset CorDapp packages in your `@After` method by using ``unsetCordappPackages()`` after `stopNodes()`. + * This is done by calling ``setCordappPackages``, an extension helper function in the ``net.corda.testing`` package, + on the first line of your ``@Before`` method. This takes a variable number of ``String`` arguments which should be + the package names of the CorDapps containing the contract verification code you wish to load + * You should unset CorDapp packages in your ``@After`` method by using ``unsetCordappPackages`` after + ``stopNodes`` -* CorDapps must be explicitly registered in ``DriverDSL`` and ``RPCDriverDSL`` integration tests. +* CorDapps must be explicitly registered in ``DriverDSL`` and ``RPCDriverDSL`` integration tests: - Similarly, you must also register package names of the CorDapps containing the contract verification code you wish to load - using the ``extraCordappPackagesToScan: List`` constructor parameter of the driver DSL. + * You must register package names of the CorDapps containing the contract verification code you wish to load using + the ``extraCordappPackagesToScan: List`` constructor parameter of the driver DSL Finance ^^^^^^^ -* `FungibleAsset` interface simplification. - - The ``FungibleAsset`` interface has been made simpler. The ``Commands`` grouping interface - that included the ``Move``, ``Issue`` and ``Exit`` interfaces have all been removed, while the ``move`` function has - been renamed to ``withNewOwnerAndAmount`` to be consistent with the ``withNewOwner`` function of the ``OwnableState``. - - The following errors may be reported: - - * override nothing (FungibleAsset): `move` - * not a subtype of overridden FungibleAsset: `withNewOwner` - * no longer need to override `override val contractHash: SecureHash? = null` - * need to override `override val contract: Class? = null` +* ``FungibleAsset`` interface simplification: + * The ``Commands`` grouping interface that included the ``Move``, ``Issue`` and ``Exit`` interfaces has been removed + * The ``move`` function has been renamed to ``withNewOwnerAndAmount`` + * This is for consistency with ``OwnableState.withNewOwner`` Miscellaneous ^^^^^^^^^^^^^ * ``args[0].parseNetworkHostAndPort()`` becomes ``NetworkHostAndPort.parse(args[0])`` -* There is no longer a ``NodeInfo.advertisedServices`` property. +* There is no longer a ``NodeInfo.advertisedServices`` property - The concept of advertised services has been removed from Corda. This is because it was vaguely defined and real world - apps would not typically select random, unknown counterparties from the network map based on self-declared capabilities. - We will introduce a replacement for this functionality, business networks, in a future release. - - For now, your should retrieve the service by legal name using ``NetworkMapCache.getNodeByLegalName``. + * The concept of advertised services has been removed from Corda. This is because it was vaguely defined and + real-world apps would not typically select random, unknown counterparties from the network map based on + self-declared capabilities + * We will introduce a replacement for this functionality, business networks, in a future release + * For now, services should be retrieved by legal name using ``NetworkMapCache.getNodeByLegalName`` Gotchas ^^^^^^^ -* Beware to use the correct identity when issuing cash: +* Be sure to use the correct identity when issuing cash: - The 3rd parameter to ``CashIssueFlow`` should be the ** notary ** (not the ** node identity **) + * The third parameter to ``CashIssueFlow`` should be the *notary* (and not the *node identity*) :ref:`From Milestone 13 ` @@ -286,67 +282,70 @@ Gotchas Core data structures ^^^^^^^^^^^^^^^^^^^^ -* `TransactionBuilder` changes. +* ``TransactionBuilder`` changes: - Use convenience class ``StateAndContract`` instead of ``TransactionBuilder.withItems()`` for passing - around a state and its contract. + * Use convenience class ``StateAndContract`` instead of ``TransactionBuilder.withItems`` for passing + around a state and its contract. -* Transaction building DSL changes: +* Transaction builder DSL changes: - * now need to explicitly pass the ContractClassName into all inputs and outputs. - * `ContractClassName` refers to the class containing the “verifier” method. + * When adding inputs and outputs to a transaction builder, you must also specify ``ContractClassName`` -* Contract verify method signature change. + * ``ContractClassName`` is the name of the ``Contract`` subclass used to verify the transaction - ``override fun verify(tx: TransactionForContract)`` becomes ``override fun verify(tx: LedgerTransaction)`` +* Contract verify method signature change: -* No longer need to override Contract ``contract()`` function. + * ``override fun verify(tx: TransactionForContract)`` becomes ``override fun verify(tx: LedgerTransaction)`` + +* You no longer need to override ``ContractState.contract`` function Node services (ServiceHub) ^^^^^^^^^^^^^^^^^^^^^^^^^^ -* ServiceHub API method changes. +* ServiceHub API method changes: - ``services.networkMapUpdates().justSnapshot`` becomes ``services.networkMapSnapshot()`` + * ``services.networkMapUpdates().justSnapshot`` becomes ``services.networkMapSnapshot()`` Configuration ^^^^^^^^^^^^^ -* No longer need to define ``CordaPluginRegistry`` and configure ``requiredSchemas`` +* No longer need to define ``CordaPluginRegistry`` and configure ``requiredSchemas``: - Custom contract schemas are automatically detected at startup time by class path scanning. - For testing purposes, use the ``SchemaService`` method to register new custom schemas: - eg. ``services.schemaService.registerCustomSchemas(setOf(YoSchemaV1))`` + * Custom contract schemas are automatically detected at startup time by class path scanning + + * For testing purposes, use the ``SchemaService`` method to register new custom schemas (e.g. + ``services.schemaService.registerCustomSchemas(setOf(YoSchemaV1))``) Identity ^^^^^^^^ -* Party names are now ``CordaX500Name``, not ``X500Name`` +* Party names are now ``CordaX500Name``, not ``X500Name``: - ``CordaX500Name`` specifies a predefined set of mandatory (organisation, locality, country) - and optional fields (commonName, organisationUnit, state) with validation checking. - Use new builder CordaX500Name.build(X500Name(target)) or, preferably, explicitly define X500Name parameters using - ``CordaX500Name`` constructor. + * ``CordaX500Name`` specifies a predefined set of mandatory (organisation, locality, country) and optional fields + (common name, organisation unit, state) with validation checking + * Use new builder ``CordaX500Name.build(X500Name(target))`` or explicitly define the X500Name parameters using the + ``CordaX500Name`` constructors Testing ^^^^^^^ -* MockNetwork Testing. +* MockNetwork testing: - Mock nodes in node tests are now of type ``StartedNode``, rather than ``MockNode`` - MockNetwork now returns a BasketOf(>) - Must call internals on StartedNode to get MockNode: - a = nodes.partyNodes[0].internals - b = nodes.partyNodes[1].internals + * Mock nodes in node tests are now of type ``StartedNode``, rather than ``MockNode`` -* Host and Port change. + * ``MockNetwork`` now returns a ``BasketOf(>)`` - Use string helper function ``parseNetworkHostAndPort()`` to parse a URL on startup. - eg. ``val hostAndPort = args[0].parseNetworkHostAndPort()`` + * You must call internals on ``StartedNode`` to get ``MockNode`` (e.g. ``a = nodes.partyNodes[0].internals``) -* The node driver parameters for starting a node have been reordered, and the node’s name needs to be given as an - ``CordaX500Name``, instead of using ``getX509Name`` +* Host and port changes: + * Use string helper function ``parseNetworkHostAndPort`` to parse a URL on startup (e.g. + ``val hostAndPort = args[0].parseNetworkHostAndPort()``) + +* Node driver parameter changes: + + * The node driver parameters for starting a node have been reordered + * The node’s name needs to be given as an ``CordaX500Name``, instead of using ``getX509Name`` :ref:`From Milestone 12 (First Public Beta) ` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -354,17 +353,17 @@ Testing Core data structures ^^^^^^^^^^^^^^^^^^^^ -* Transaction building +* Transaction building: - You no longer need to specify the type of a ``TransactionBuilder`` as ``TransactionType.General`` - ``TransactionType.General.Builder(notary)`` becomes ``TransactionBuilder(notary)`` + * You no longer need to specify the type of a ``TransactionBuilder`` as ``TransactionType.General`` + * ``TransactionType.General.Builder(notary)`` becomes ``TransactionBuilder(notary)`` Build ^^^^^ -* Gradle dependency reference changes. +* Gradle dependency reference changes: - Module name has changed to include `corda` in the artifacts jar name: + * Module names have changed to include ``corda`` in the artifacts' JAR names: .. sourcecode:: shell @@ -377,25 +376,31 @@ Build Node services (ServiceHub) ^^^^^^^^^^^^^^^^^^^^^^^^^^ -* ServiceHub API changes. +* ``ServiceHub`` API changes: - ``services.networkMapUpdates()`` becomes ``services.networkMapFeed()`` - ``services.getCashBalances()`` becomes a helper method within the **finance** module contracts package: ``net.corda.finance.contracts.getCashBalances`` + * ``services.networkMapUpdates`` becomes ``services.networkMapFeed`` + + * ``services.getCashBalances`` becomes a helper method in the *finance* module contracts package + (``net.corda.finance.contracts.getCashBalances``) Finance ^^^^^^^ -* Financial asset contracts (Cash, CommercialPaper, Obligations) are now a standalone CorDapp within the **finance** module. +* Financial asset contracts (``Cash``, ``CommercialPaper``, ``Obligations``) are now a standalone CorDapp within the + ``finance`` module: - Need to import from respective package within `finance` module: - eg. ``net.corda.finance.contracts.asset.Cash`` + * You need to import them from their respective packages within the ``finance`` module (e.g. + ``net.corda.finance.contracts.asset.Cash``) - Likewise, need to import associated asset flows from respective package within `finance` module: - eg. ``net.corda.finance.flows.CashIssueFlow`` - ``net.corda.finance.flows.CashIssueAndPaymentFlow`` - ``net.corda.finance.flows.CashExitFlow`` + * You need to import the associated asset flows from their respective packages within ``finance`` module. For + example: -* Moved ``finance`` gradle project files into a ``net.corda.finance`` package namespace. + * ``net.corda.finance.flows.CashIssueFlow`` + * ``net.corda.finance.flows.CashIssueAndPaymentFlow`` + * ``net.corda.finance.flows.CashExitFlow`` - This may require adjusting imports of Cash flow references and also of ``StartFlow`` permission in ``gradle.build`` files. - Associated flows (`Cash*Flow`, `TwoPartyTradeFlow`, `TwoPartyDealFlow`) must now be imported from this package. +* The ``finance`` gradle project files have been moved into a ``net.corda.finance`` package namespace: + + * Adjust imports of Cash flow references + * Adjust the ``StartFlow`` permission in ``gradle.build`` files + * Adjust imports of the associated flows (``Cash*Flow``, ``TwoPartyTradeFlow``, ``TwoPartyDealFlow``) \ No newline at end of file From af081a7170c6de4cb9788247ba66272623024c39 Mon Sep 17 00:00:00 2001 From: Katarzyna Streich Date: Thu, 18 Jan 2018 16:23:41 +0000 Subject: [PATCH 04/36] Remove primary key constraint on DBHostAndPort (#2318) Remove primary key constraint on DBHostAndPort Return always first node if more are matching by address. --- .../core/internal/schemas/NodeInfoSchema.kt | 18 ++++++++--------- .../network/PersistentNetworkMapCacheTest.kt | 20 +++++++++++++++---- .../network/PersistentNetworkMapCache.kt | 6 +++--- .../services/network/NetworkMapCacheTest.kt | 12 +++++++++++ 4 files changed, 39 insertions(+), 17 deletions(-) diff --git a/core/src/main/kotlin/net/corda/core/internal/schemas/NodeInfoSchema.kt b/core/src/main/kotlin/net/corda/core/internal/schemas/NodeInfoSchema.kt index 6e35a631c4..fe7067d768 100644 --- a/core/src/main/kotlin/net/corda/core/internal/schemas/NodeInfoSchema.kt +++ b/core/src/main/kotlin/net/corda/core/internal/schemas/NodeInfoSchema.kt @@ -63,26 +63,24 @@ object NodeInfoSchemaV1 : MappedSchema( } } - @Embeddable - data class PKHostAndPort( - val host: String? = null, - val port: Int? = null - ) : Serializable - @Entity @Table(name = "node_info_hosts") data class DBHostAndPort( - @EmbeddedId - private val pk: PKHostAndPort + @Id + @GeneratedValue + @Column(name = "hosts_id") + var id: Int, + val host: String? = null, + val port: Int? = null ) { companion object { fun fromHostAndPort(hostAndPort: NetworkHostAndPort) = DBHostAndPort( - PKHostAndPort(hostAndPort.host, hostAndPort.port) + 0, hostAndPort.host, hostAndPort.port ) } fun toHostAndPort(): NetworkHostAndPort { - return NetworkHostAndPort(this.pk.host!!, this.pk.port!!) + return NetworkHostAndPort(host!!, port!!) } } diff --git a/node/src/integration-test/kotlin/net/corda/node/services/network/PersistentNetworkMapCacheTest.kt b/node/src/integration-test/kotlin/net/corda/node/services/network/PersistentNetworkMapCacheTest.kt index 1b98c4f54e..67ec3d592c 100644 --- a/node/src/integration-test/kotlin/net/corda/node/services/network/PersistentNetworkMapCacheTest.kt +++ b/node/src/integration-test/kotlin/net/corda/node/services/network/PersistentNetworkMapCacheTest.kt @@ -1,20 +1,19 @@ package net.corda.node.services.network +import net.corda.core.crypto.generateKeyPair import net.corda.core.identity.CordaX500Name import net.corda.core.identity.Party import net.corda.core.node.NodeInfo import net.corda.core.utilities.NetworkHostAndPort import net.corda.node.internal.Node import net.corda.node.internal.StartedNode -import net.corda.testing.ALICE_NAME -import net.corda.testing.BOB_NAME -import net.corda.testing.TestIdentity -import net.corda.testing.chooseIdentity +import net.corda.testing.* import net.corda.testing.node.internal.NodeBasedTest import org.junit.Before import org.junit.Test import kotlin.test.assertEquals +// TODO Clean up these tests, they were written with old network map design in place. class PersistentNetworkMapCacheTest : NodeBasedTest() { private companion object { val ALICE = TestIdentity(ALICE_NAME, 70).party @@ -58,6 +57,19 @@ class PersistentNetworkMapCacheTest : NodeBasedTest() { } } + // This test has to be done as normal node not mock, because MockNodes don't have addresses. + @Test + fun `insert two node infos with the same host and port`() { + val aliceNode = startNode(ALICE_NAME) + val charliePartyCert = getTestPartyAndCertificate(CHARLIE_NAME, generateKeyPair().public) + val aliceCache = aliceNode.services.networkMapCache + aliceCache.addNode(aliceNode.info.copy(legalIdentitiesAndCerts = listOf(charliePartyCert))) + val res = aliceNode.database.transaction { + aliceCache.allNodes.filter { aliceNode.info.addresses[0] in it.addresses } + } + assertEquals(2, res.size) + } + @Test fun `restart node with DB map cache`() { val alice = startNodesWithPort(listOf(ALICE))[0] diff --git a/node/src/main/kotlin/net/corda/node/services/network/PersistentNetworkMapCache.kt b/node/src/main/kotlin/net/corda/node/services/network/PersistentNetworkMapCache.kt index ca54c89019..1eafc11b00 100644 --- a/node/src/main/kotlin/net/corda/node/services/network/PersistentNetworkMapCache.kt +++ b/node/src/main/kotlin/net/corda/node/services/network/PersistentNetworkMapCache.kt @@ -292,13 +292,13 @@ open class PersistentNetworkMapCache( private fun queryByAddress(session: Session, hostAndPort: NetworkHostAndPort): NodeInfo? { val query = session.createQuery( - "SELECT n FROM ${NodeInfoSchemaV1.PersistentNodeInfo::class.java.name} n JOIN n.addresses a WHERE a.pk.host = :host AND a.pk.port = :port", + "SELECT n FROM ${NodeInfoSchemaV1.PersistentNodeInfo::class.java.name} n JOIN n.addresses a WHERE a.host = :host AND a.port = :port", NodeInfoSchemaV1.PersistentNodeInfo::class.java) query.setParameter("host", hostAndPort.host) query.setParameter("port", hostAndPort.port) + query.setMaxResults(1) val result = query.resultList - return if (result.isEmpty()) null - else result.map { it.toNodeInfo() }.singleOrNull() ?: throw IllegalStateException("More than one node with the same host and port") + return result.map { it.toNodeInfo() }.singleOrNull() } /** Object Relational Mapping support. */ diff --git a/node/src/test/kotlin/net/corda/node/services/network/NetworkMapCacheTest.kt b/node/src/test/kotlin/net/corda/node/services/network/NetworkMapCacheTest.kt index eb95a985dd..b56728fcf6 100644 --- a/node/src/test/kotlin/net/corda/node/services/network/NetworkMapCacheTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/network/NetworkMapCacheTest.kt @@ -1,9 +1,11 @@ package net.corda.node.services.network +import net.corda.core.crypto.generateKeyPair import net.corda.core.node.services.NetworkMapCache import net.corda.node.services.api.NetworkMapCacheInternal import net.corda.testing.ALICE_NAME import net.corda.testing.BOB_NAME +import net.corda.testing.getTestPartyAndCertificate import net.corda.testing.node.MockNetwork import net.corda.testing.node.MockNodeParameters import net.corda.testing.singleIdentity @@ -106,4 +108,14 @@ class NetworkMapCacheTest { assertThat(bobCache.getNodeByLegalName(alice.name) == null) } } + + @Test + fun `add two nodes the same name different keys`() { + val aliceNode = mockNet.createPartyNode(ALICE_NAME) + val aliceCache = aliceNode.services.networkMapCache + val alicePartyAndCert2 = getTestPartyAndCertificate(ALICE_NAME, generateKeyPair().public) + aliceCache.addNode(aliceNode.info.copy(legalIdentitiesAndCerts = listOf(alicePartyAndCert2))) + // This is correct behaviour as we may have distributed service nodes. + assertEquals(2, aliceCache.getNodesByLegalName(ALICE_NAME).size) + } } From 7427d63bbcf32739340acfe0a440f09cc560f1a2 Mon Sep 17 00:00:00 2001 From: Joel Dudley Date: Thu, 18 Jan 2018 18:08:22 +0000 Subject: [PATCH 05/36] Prevents checkSignatures from failing if tx also contains unrecognised sigs. --- .../main/kotlin/net/corda/core/flows/CollectSignaturesFlow.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/core/src/main/kotlin/net/corda/core/flows/CollectSignaturesFlow.kt b/core/src/main/kotlin/net/corda/core/flows/CollectSignaturesFlow.kt index 3424afa84a..733f52aaee 100644 --- a/core/src/main/kotlin/net/corda/core/flows/CollectSignaturesFlow.kt +++ b/core/src/main/kotlin/net/corda/core/flows/CollectSignaturesFlow.kt @@ -244,7 +244,9 @@ abstract class SignTransactionFlow(val otherSideSession: FlowSession, } @Suspendable private fun checkSignatures(stx: SignedTransaction) { - val signingWellKnownIdentities = groupPublicKeysByWellKnownParty(serviceHub, stx.sigs.map(TransactionSignature::by)) + // We set `ignoreUnrecognisedParties` to `true` in `groupPublicKeysByWellKnownParty`. This is because we don't + // need to recognise all keys, but just the initiator's. + val signingWellKnownIdentities = groupPublicKeysByWellKnownParty(serviceHub, stx.sigs.map(TransactionSignature::by), true) require(otherSideSession.counterparty in signingWellKnownIdentities) { "The Initiator of CollectSignaturesFlow must have signed the transaction. Found ${signingWellKnownIdentities}, expected ${otherSideSession}" } From de4c0625291f760f1160fd397937992f793e4f62 Mon Sep 17 00:00:00 2001 From: cburlinchon <31621751+cburlinchon@users.noreply.github.com> Date: Fri, 19 Jan 2018 10:19:12 +0000 Subject: [PATCH 06/36] Node restarted with shutdown executor (#2391) * Test for node restart * Executor gets shutdown on stop, make sure we have one on start * Reset shutdown otherwise AbstractNode.stop never gets called --- .../corda/test/node/NodeStartAndStopTest.kt | 19 +++++++++++++++++++ .../kotlin/net/corda/node/internal/Node.kt | 5 ++++- 2 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 node/src/integration-test/kotlin/net/corda/test/node/NodeStartAndStopTest.kt diff --git a/node/src/integration-test/kotlin/net/corda/test/node/NodeStartAndStopTest.kt b/node/src/integration-test/kotlin/net/corda/test/node/NodeStartAndStopTest.kt new file mode 100644 index 0000000000..73e041485c --- /dev/null +++ b/node/src/integration-test/kotlin/net/corda/test/node/NodeStartAndStopTest.kt @@ -0,0 +1,19 @@ +package net.corda.test.node + +import net.corda.core.utilities.getOrThrow +import net.corda.testing.ALICE_NAME +import net.corda.testing.node.internal.NodeBasedTest +import org.junit.Test + +class NodeStartAndStopTest : NodeBasedTest() { + + @Test + fun `start stop start`() { + val node = startNode(ALICE_NAME) + node.internals.startupComplete.get() + node.internals.stop() + + node.internals.start() + node.internals.startupComplete.getOrThrow() + } +} \ No newline at end of file diff --git a/node/src/main/kotlin/net/corda/node/internal/Node.kt b/node/src/main/kotlin/net/corda/node/internal/Node.kt index 21a16a1367..a36a8f78e7 100644 --- a/node/src/main/kotlin/net/corda/node/internal/Node.kt +++ b/node/src/main/kotlin/net/corda/node/internal/Node.kt @@ -133,7 +133,7 @@ open class Node(configuration: NodeConfiguration, // // The primary work done by the server thread is execution of flow logics, and related // serialisation/deserialisation work. - override val serverThread = AffinityExecutor.ServiceAffinityExecutor("Node thread-$sameVmNodeNumber", 1) + override lateinit var serverThread: AffinityExecutor.ServiceAffinityExecutor private var messageBroker: ArtemisMessagingServer? = null @@ -296,6 +296,7 @@ open class Node(configuration: NodeConfiguration, } override fun start(): StartedNode { + serverThread = AffinityExecutor.ServiceAffinityExecutor("Node thread-$sameVmNodeNumber", 1) initialiseSerialization() val started: StartedNode = uncheckedCast(super.start()) nodeReadyFuture.thenMatch({ @@ -372,6 +373,8 @@ open class Node(configuration: NodeConfiguration, // In particular this prevents premature shutdown of the Database by AbstractNode whilst the serverThread is active super.stop() + shutdown = false + log.info("Shutdown complete") } } From 35f89e03ea14948a7c2657fe4aafe56d4f0842a7 Mon Sep 17 00:00:00 2001 From: Anthony Keenan <34482776+anthonykr3@users.noreply.github.com> Date: Fri, 19 Jan 2018 10:57:08 +0000 Subject: [PATCH 07/36] Abstract InMemoryMessaging behind an interface so as not expose net.corda.nodeapi.internal.persistence.CordaPersistence in public API (#2390) --- .../testing/node/InMemoryMessagingNetwork.kt | 36 ++++++++++--------- .../kotlin/net/corda/testing/node/MockNode.kt | 29 ++++++++------- 2 files changed, 35 insertions(+), 30 deletions(-) diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/InMemoryMessagingNetwork.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/InMemoryMessagingNetwork.kt index 96b9a9d4c1..3bfa44fe5f 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/InMemoryMessagingNetwork.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/InMemoryMessagingNetwork.kt @@ -19,7 +19,6 @@ import net.corda.core.utilities.trace import net.corda.node.services.messaging.* import net.corda.node.utilities.AffinityExecutor import net.corda.nodeapi.internal.persistence.CordaPersistence -import net.corda.testing.node.InMemoryMessagingNetwork.InMemoryMessaging import org.apache.activemq.artemis.utils.ReusableLatch import org.slf4j.LoggerFactory import rx.Observable @@ -33,8 +32,8 @@ import kotlin.concurrent.schedule import kotlin.concurrent.thread /** - * An in-memory network allows you to manufacture [InMemoryMessaging]s for a set of participants. Each - * [InMemoryMessaging] maintains a queue of messages it has received, and a background thread that dispatches + * An in-memory network allows you to manufacture [TestMessagingService]s for a set of participants. Each + * [TestMessagingService] maintains a queue of messages it has received, and a background thread that dispatches * messages one by one to registered handlers. Alternatively, a messaging system may be manually pumped, in which * case no thread is created and a caller is expected to force delivery one at a time (this is useful for unit * testing). @@ -88,7 +87,7 @@ class InMemoryMessagingNetwork internal constructor( val receivedMessages: Observable get() = _receivedMessages - val endpoints: List @Synchronized get() = handleEndpointMap.values.toList() + val endpoints: List @Synchronized get() = handleEndpointMap.values.toList() /** * Creates a node at the given address: useful if you want to recreate a node to simulate a restart. * @@ -99,14 +98,14 @@ class InMemoryMessagingNetwork internal constructor( * @param id the numeric ID to use, e.g. set to whatever ID the node used last time. * @param description text string that identifies this node for message logging (if is enabled) or null to autogenerate. */ - fun createNodeWithID( + internal fun createNodeWithID( manuallyPumped: Boolean, id: Int, executor: AffinityExecutor, notaryService: PartyAndCertificate?, description: CordaX500Name = CordaX500Name(organisation = "In memory node $id", locality = "London", country = "UK"), database: CordaPersistence) - : InMemoryMessaging { + : TestMessagingService { val peerHandle = PeerHandle(id, description) peersMapping[peerHandle.description] = peerHandle // Assume that the same name - the same entity in MockNetwork. notaryService?.let { if (it.owningKey !is CompositeKey) peersMapping[it.name] = peerHandle } @@ -256,17 +255,20 @@ class InMemoryMessagingNetwork internal constructor( override val peer: CordaX500Name) : ReceivedMessage /** - * An [InMemoryMessaging] provides a [MessagingService] that isn't backed by any kind of network or disk storage - * system, but just uses regular queues on the heap instead. It is intended for unit testing and developer convenience - * when all entities on 'the network' are being simulated in-process. - * - * An instance can be obtained by creating a builder and then using the start method. + * A [TestMessagingService] that provides a [MessagingService] abstraction that also contains the ability to + * receive messages from the queue for testing purposes. */ + @DoNotImplement + interface TestMessagingService : MessagingService { + fun pumpReceive(block: Boolean): InMemoryMessagingNetwork.MessageTransfer? + fun stop() + } + @ThreadSafe - inner class InMemoryMessaging(private val manuallyPumped: Boolean, - private val peerHandle: PeerHandle, - private val executor: AffinityExecutor, - private val database: CordaPersistence) : SingletonSerializeAsToken(), MessagingService { + private inner class InMemoryMessaging(private val manuallyPumped: Boolean, + private val peerHandle: PeerHandle, + private val executor: AffinityExecutor, + private val database: CordaPersistence) : SingletonSerializeAsToken(), TestMessagingService { private inner class Handler(val topicSession: TopicSession, val callback: (ReceivedMessage, MessageHandlerRegistration) -> Unit) : MessageHandlerRegistration @@ -341,7 +343,7 @@ class InMemoryMessagingNetwork internal constructor( acknowledgementHandler?.invoke() } - fun stop() { + override fun stop() { if (backgroundThread != null) { backgroundThread.interrupt() backgroundThread.join() @@ -364,7 +366,7 @@ class InMemoryMessagingNetwork internal constructor( * * @return the message that was processed, if any in this round. */ - fun pumpReceive(block: Boolean): MessageTransfer? { + override fun pumpReceive(block: Boolean): MessageTransfer? { check(manuallyPumped) check(running) executor.flush() diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNode.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNode.kt index 7e8aa8225f..b9901a881c 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNode.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNode.kt @@ -62,7 +62,7 @@ import java.util.concurrent.TimeUnit import java.util.concurrent.atomic.AtomicInteger fun StartedNode.pumpReceive(block: Boolean = false): InMemoryMessagingNetwork.MessageTransfer? { - return (network as InMemoryMessagingNetwork.InMemoryMessaging).pumpReceive(block) + return (network as InMemoryMessagingNetwork.TestMessagingService).pumpReceive(block) } /** Helper builder for configuring a [MockNetwork] from Java. */ @@ -172,29 +172,32 @@ open class MockNetwork(private val cordappPackages: List, * Returns the single notary node on the network. Throws if there are none or more than one. * @see notaryNodes */ - val defaultNotaryNode: StartedNode get() { - return when (notaryNodes.size) { - 0 -> throw IllegalStateException("There are no notaries defined on the network") - 1 -> notaryNodes[0] - else -> throw IllegalStateException("There is more than one notary defined on the network") + val defaultNotaryNode: StartedNode + get() { + return when (notaryNodes.size) { + 0 -> throw IllegalStateException("There are no notaries defined on the network") + 1 -> notaryNodes[0] + else -> throw IllegalStateException("There is more than one notary defined on the network") + } } - } /** * Return the identity of the default notary node. * @see defaultNotaryNode */ - val defaultNotaryIdentity: Party get() { - return defaultNotaryNode.info.legalIdentities.singleOrNull() ?: throw IllegalStateException("Default notary has multiple identities") - } + val defaultNotaryIdentity: Party + get() { + return defaultNotaryNode.info.legalIdentities.singleOrNull() ?: throw IllegalStateException("Default notary has multiple identities") + } /** * Return the identity of the default notary node. * @see defaultNotaryNode */ - val defaultNotaryIdentityAndCert: PartyAndCertificate get() { - return defaultNotaryNode.info.legalIdentitiesAndCerts.singleOrNull() ?: throw IllegalStateException("Default notary has multiple identities") - } + val defaultNotaryIdentityAndCert: PartyAndCertificate + get() { + return defaultNotaryNode.info.legalIdentitiesAndCerts.singleOrNull() ?: throw IllegalStateException("Default notary has multiple identities") + } /** * Because this executor is shared, we need to be careful about nodes shutting it down. From 87b00fde7d8142fe1b494a34d3347c1d96e3227a Mon Sep 17 00:00:00 2001 From: Joel Dudley Date: Fri, 19 Jan 2018 11:47:19 +0000 Subject: [PATCH 08/36] Better instructions for building against master. --- docs/source/building-against-master.rst | 28 +++++++++++++------------ 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/docs/source/building-against-master.rst b/docs/source/building-against-master.rst index af3d87e25d..03016ef7f4 100644 --- a/docs/source/building-against-master.rst +++ b/docs/source/building-against-master.rst @@ -1,20 +1,19 @@ Building against Master ======================= -When developing a CorDapp, it is advisable to use the most recent Corda Milestone release, which has been extensively -tested. However, if you need to use a very recent feature of the codebase, you may need to work against the unstable -Master branch. +It is advisable to develop CorDapps against the most recent Corda stable release. However you may need to build +against the unstable Master branch if you are using a very recent feature, or are testing a PR on the main codebase. To work against the Master branch, proceed as follows: -* Open a terminal window in the folder where you cloned the Corda repository +1. Open a terminal window in the folder where you cloned the Corda repository (available `here `_) -* Use the following command to check out the latest master branch: +2. Use the following command to check out the latest master branch: - ``git fetch; git checkout master`` + ``git checkout master; git pull`` -* Publish Corda to your local Maven repository using the following commands: +3. Publish Corda to your local Maven repository using the following commands: * Unix/Mac OSX: ``./gradlew install`` * Windows: ``gradlew.bat install`` @@ -24,11 +23,14 @@ To work against the Master branch, proceed as follows: * ``~/.m2/repository`` on Unix/Mac OS X * ``%HOMEPATH%\.m2`` on Windows - This step is not necessary when using a Milestone releases, as the Milestone releases are published online + This step is not necessary when using a stable releases, as the stable releases are published online -.. warning:: If you do modify your local Corda repository after having published it to Maven local, then you must - re-publish it to Maven local for the local installation to reflect the changes you have made. + .. warning:: If you do modify your local Corda repository after having published it to Maven local, then you must +re-publish it to Maven local for the local installation to reflect the changes you have made. -.. warning:: As the Corda repository evolves on a daily basis, two clones of the Master branch at different points in - time may differ. If you are using a Master release and need help debugging an error, then please let us know the - **commit** you are working from. This will help us ascertain the issue. \ No newline at end of file + .. warning:: As the Corda repository evolves on a daily basis, two clones of the Master branch at different points in +time may differ. If you are using a Master release and need help debugging an error, then please let us know the + **commit** you are working from. This will help us ascertain the issue. + +4. Update the ``ext.corda_release_version`` property in your CorDapp's root ``build.gradle`` file to match the version + here: https://github.com/corda/corda/blob/master/build.gradle#L7 \ No newline at end of file From cfc5c6709acc81519cc2958eeedf9d6957f4996e Mon Sep 17 00:00:00 2001 From: Joel Dudley Date: Fri, 19 Jan 2018 13:43:17 +0000 Subject: [PATCH 09/36] Fixes formatting. --- docs/source/building-against-master.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/source/building-against-master.rst b/docs/source/building-against-master.rst index 03016ef7f4..0e0da92a5f 100644 --- a/docs/source/building-against-master.rst +++ b/docs/source/building-against-master.rst @@ -26,11 +26,11 @@ To work against the Master branch, proceed as follows: This step is not necessary when using a stable releases, as the stable releases are published online .. warning:: If you do modify your local Corda repository after having published it to Maven local, then you must -re-publish it to Maven local for the local installation to reflect the changes you have made. + re-publish it to Maven local for the local installation to reflect the changes you have made. .. warning:: As the Corda repository evolves on a daily basis, two clones of the Master branch at different points in -time may differ. If you are using a Master release and need help debugging an error, then please let us know the + time may differ. If you are using a Master release and need help debugging an error, then please let us know the **commit** you are working from. This will help us ascertain the issue. 4. Update the ``ext.corda_release_version`` property in your CorDapp's root ``build.gradle`` file to match the version - here: https://github.com/corda/corda/blob/master/build.gradle#L7 \ No newline at end of file + here: https://github.com/corda/corda/blob/master/build.gradle#L7 From ac7637e2b498ee4b8b01fd2ebe1758465fc2fe7c Mon Sep 17 00:00:00 2001 From: Mike Hearn Date: Fri, 19 Jan 2018 16:59:31 +0100 Subject: [PATCH 10/36] Revert "CORDA-599 RPCSecurityManager is no longer lateinit (#2347)" This reverts commit 75e74e67a1b589cbfa443fb8f05e625233bef78b. --- .../net/corda/core/internal/InternalUtils.kt | 10 +- .../corda/core/internal/InternalUtilsTest.kt | 13 - .../main/kotlin/net/corda/lazyhub/LazyHub.kt | 109 --- .../kotlin/net/corda/lazyhub/LazyHubImpl.kt | 200 ------ .../kotlin/net/corda/lazyhub/LazyHubModel.kt | 130 ---- .../net/corda/node/internal/AbstractNode.kt | 34 +- .../kotlin/net/corda/node/internal/Node.kt | 37 +- .../kotlin/net/corda/lazyhub/LazyHubTests.kt | 640 ------------------ .../kotlin/net/corda/testing/node/MockNode.kt | 20 +- 9 files changed, 42 insertions(+), 1151 deletions(-) delete mode 100644 node/src/main/kotlin/net/corda/lazyhub/LazyHub.kt delete mode 100644 node/src/main/kotlin/net/corda/lazyhub/LazyHubImpl.kt delete mode 100644 node/src/main/kotlin/net/corda/lazyhub/LazyHubModel.kt delete mode 100644 node/src/test/kotlin/net/corda/lazyhub/LazyHubTests.kt diff --git a/core/src/main/kotlin/net/corda/core/internal/InternalUtils.kt b/core/src/main/kotlin/net/corda/core/internal/InternalUtils.kt index b5b8bc6f72..70724cb884 100644 --- a/core/src/main/kotlin/net/corda/core/internal/InternalUtils.kt +++ b/core/src/main/kotlin/net/corda/core/internal/InternalUtils.kt @@ -242,14 +242,10 @@ private fun IntProgression.toSpliterator(): Spliterator.OfInt { } fun IntProgression.stream(parallel: Boolean = false): IntStream = StreamSupport.intStream(toSpliterator(), parallel) -inline fun Stream.toTypedArray() = toTypedArray(T::class.java) -// When toArray has filled in the array, the component type is no longer T? but T (that may itself be nullable): -fun Stream.toTypedArray(componentType: Class): Array = toArray { size -> - uncheckedCast>(java.lang.reflect.Array.newInstance(componentType, size)) -} -fun Stream.filterNotNull(): Stream = uncheckedCast(filter(Objects::nonNull)) -fun Stream>.toMap(): Map = collect>(::LinkedHashMap, { m, (k, v) -> m.put(k, v) }, { m, t -> m.putAll(t) }) +// When toArray has filled in the array, the component type is no longer T? but T (that may itself be nullable): +inline fun Stream.toTypedArray(): Array = uncheckedCast(toArray { size -> arrayOfNulls(size) }) + fun Class.castIfPossible(obj: Any): T? = if (isInstance(obj)) cast(obj) else null /** Returns a [DeclaredField] wrapper around the declared (possibly non-public) static field of the receiver [Class]. */ diff --git a/core/src/test/kotlin/net/corda/core/internal/InternalUtilsTest.kt b/core/src/test/kotlin/net/corda/core/internal/InternalUtilsTest.kt index 12818f9a81..0a2fb69f26 100644 --- a/core/src/test/kotlin/net/corda/core/internal/InternalUtilsTest.kt +++ b/core/src/test/kotlin/net/corda/core/internal/InternalUtilsTest.kt @@ -3,7 +3,6 @@ package net.corda.core.internal import org.assertj.core.api.Assertions import org.junit.Assert.assertArrayEquals import org.junit.Test -import java.io.Serializable import java.util.stream.IntStream import java.util.stream.Stream import kotlin.test.assertEquals @@ -88,17 +87,5 @@ class InternalUtilsTest { val b: Array = Stream.of("one", "two", null).toTypedArray() assertEquals(Array::class.java, b.javaClass) assertArrayEquals(arrayOf("one", "two", null), b) - val c: Array = Stream.of("x", "y").toTypedArray(CharSequence::class.java) - assertEquals(Array::class.java, c.javaClass) - assertArrayEquals(arrayOf("x", "y"), c) - val d: Array = Stream.of("x", "y", null).toTypedArray(uncheckedCast(CharSequence::class.java)) - assertEquals(Array::class.java, d.javaClass) - assertArrayEquals(arrayOf("x", "y", null), d) - } - - @Test - fun `Stream of Pairs toMap works`() { - val m: Map, Serializable> = Stream.of, Serializable>>("x" to "y", 0 to 1, "x" to '2').toMap() - assertEquals>(mapOf("x" to '2', 0 to 1), m) } } diff --git a/node/src/main/kotlin/net/corda/lazyhub/LazyHub.kt b/node/src/main/kotlin/net/corda/lazyhub/LazyHub.kt deleted file mode 100644 index 512fa1c74e..0000000000 --- a/node/src/main/kotlin/net/corda/lazyhub/LazyHub.kt +++ /dev/null @@ -1,109 +0,0 @@ -package net.corda.lazyhub - -import net.corda.core.serialization.CordaSerializable -import kotlin.reflect.KClass -import kotlin.reflect.KFunction - -/** Supertype of all exceptions thrown directly by [LazyHub]. */ -@CordaSerializable -abstract class LazyHubException(message: String) : RuntimeException(message) - -/** The type can't be instantiated because it is abstract, i.e. it's an interface or abstract class. */ -class AbstractTypeException(message: String) : LazyHubException(message) - -/** - * The class can't be instantiated because it has no public constructor. - * This is so that you can easily hide a constructor from LazyHub by making it non-public. - */ -class NoPublicConstructorsException(message: String) : LazyHubException(message) - -/** - * Nullable factory return types are not supported, as LazyHub has no concept of a provider that MAY supply an object. - * If you want an optional result, use logic to decide whether to add the factory to the lazyHub. - */ -class NullableReturnTypeException(message: String) : LazyHubException(message) - -/** The parameter can't be satisfied and doesn't have a default and isn't nullable. */ -abstract class UnsatisfiableParamException(message: String) : LazyHubException(message) - -/** No provider has been registered for the wanted type. */ -class NoSuchProviderException(message: String) : UnsatisfiableParamException(message) - -/** - * No provider has been registered for the component type of the wanted array. - * Note that LazyHub does not create empty arrays, make the array param type nullable to accept no elements. - * This allows you to express zero-or-more (nullable) or one-or-more via the parameter type. - */ -class UnsatisfiableArrayException(message: String) : UnsatisfiableParamException(message) - -/** More than one provider has been registered for the type but at most one object is wanted. */ -class TooManyProvidersException(message: String) : UnsatisfiableParamException(message) - -/** - * More than one public constructor is satisfiable and there is no clear winner. - * The winner is the constructor with the most params for which LazyHub actually supplies an arg. - */ -class NoUniqueGreediestSatisfiableConstructorException(message: String) : LazyHubException(message) - -/** The object being created depends on itself, i.e. it's already being instantiated/factoried. */ -class CircularDependencyException(message: String) : LazyHubException(message) - -/** Depend on this as a param (and add the [MutableLazyHub], which is a [LazyHubFactory], to itself) if you want to make child containers. */ -interface LazyHubFactory { - fun child(): MutableLazyHub -} - -/** - * Read-only interface to the lazyHub. - * Where possible, always obtain your object via a constructor/method param instead of directly from the [LazyHub]. - * This results in the greatest automatic benefits to the codebase e.g. separation of concerns and ease of testing. - * A notable exception to this rule is `getAll(Unit::class)` to (idempotently) run all side-effects. - */ -interface LazyHub : LazyHubFactory { - operator fun get(clazz: KClass) = get(clazz.java) - operator fun get(clazz: Class) = getOrNull(clazz) ?: throw NoSuchProviderException(clazz.toString()) - fun getAll(clazz: KClass) = getAll(clazz.java) - fun getAll(clazz: Class): List - fun getOrNull(clazz: KClass) = getOrNull(clazz.java) - fun getOrNull(clazz: Class): T? -} - -/** Fully-featured interface to the lazyHub. */ -interface MutableLazyHub : LazyHub { - /** Register the given object against its class and all supertypes. */ - fun obj(obj: Any) - - /** Like plain old [MutableLazyHub.obj] but removes all [service] providers first. */ - fun obj(service: KClass, obj: T) - - /** - * Register the given class as a provider for itself and all supertypes. - * The class is instantiated at most once, using the greediest public constructor satisfiable at the time. - */ - fun impl(impl: KClass<*>) - - /** - * Same as [MutableLazyHub.impl] if you don't have a static reference to the class. - * Note that Kotlin features such as nullable params and default args will not be available. - */ - fun impl(impl: Class<*>) - - /** Like plain old [MutableLazyHub.impl] but removes all [service] providers first. */ - fun impl(service: KClass, impl: KClass) - - /** Like the [KClass] variant if you don't have a static reference fo the class. */ - fun impl(service: KClass, impl: Class) - - /** - * Register the given function as a provider for its **declared** return type and all supertypes. - * The function is invoked at most once. Unlike constructors, the function may have any visibility. - * By convention the function should have side-effects iff its return type is [Unit]. - */ - fun factory(factory: KFunction<*>) - - /** Register a factory that provides the given type from the given hub. */ - fun factory(lh: LazyHub, type: KClass<*>) - - /** Like plain old [MutableLazyHub.factory] but removes all [service] providers first. */ - fun factory(service: KClass, factory: KFunction) -} diff --git a/node/src/main/kotlin/net/corda/lazyhub/LazyHubImpl.kt b/node/src/main/kotlin/net/corda/lazyhub/LazyHubImpl.kt deleted file mode 100644 index 5edbb4027b..0000000000 --- a/node/src/main/kotlin/net/corda/lazyhub/LazyHubImpl.kt +++ /dev/null @@ -1,200 +0,0 @@ -package net.corda.lazyhub - -import net.corda.core.internal.filterNotNull -import net.corda.core.internal.toTypedArray -import net.corda.core.internal.uncheckedCast -import net.corda.lazyhub.JConcrete.Companion.validate -import net.corda.lazyhub.KConcrete.Companion.validate -import net.corda.lazyhub.KConstructor.Companion.validate -import java.util.* -import java.util.concurrent.Callable -import java.util.stream.Stream -import kotlin.reflect.KClass -import kotlin.reflect.KFunction - -/** - * Create a new [MutableLazyHub] with no parent. - * - * Basic usage: - * * Add classes/factories/objects to the LazyHub using [MutableLazyHub.impl], [MutableLazyHub.factory] and [MutableLazyHub.obj] - * * Then ask it for a type using [LazyHub.get] and it will create (and cache) the object graph for you - * * You can use [LazyHub.getAll] to get all objects of a type, e.g. by convention pass in [Unit] to run side-effects - * - * How it works: - * * [LazyHub.get] finds the unique registered class/factory/object for the given type (or fails) - * * If it's an object, that object is returned - * * If it's a factory, it is executed with args obtained recursively from the same LazyHub - * * If it's a class, it is instantiated using a public constructor in the same way as a factory - * * Of the public constructors that can be satisfied, the one that consumes the most args is chosen - * - * Advanced usage: - * * Use an array parameter to get one-or-more args of the component type, make it nullable for zero-or-more - * * If a LazyHub can't satisfy a type (or array param) and has a parent, it asks the parent - * * Typically the root LazyHub in the hierarchy will manage all singletons of the process - */ -fun lazyHub(): MutableLazyHub = LazyHubImpl(null) - -private class SimpleProvider(override val obj: T) : Provider { - override val type get() = obj.javaClass -} - -private class LazyProvider(private val busyProviders: BusyProviders, private val underlying: Any?, override val type: Class, val chooseInvocation: () -> Callable) : Provider { - override val obj by lazy { busyProviders.runFactory(this) } - override fun toString() = underlying.toString() -} - -private class Invocation(val constructor: PublicConstructor, val argSuppliers: List>) : Callable { - fun providerCount() = argSuppliers.stream().filter { (_, supplier) -> supplier.provider != null }.count() // Allow repeated providers. - override fun call() = constructor(argSuppliers) - override fun toString() = constructor.toString() -} - -private class BusyProviders { - private val busyProviders = mutableMapOf, Callable<*>>() - fun runFactory(provider: LazyProvider): T { - if (busyProviders.contains(provider)) throw CircularDependencyException("Provider '$provider' is already busy: ${busyProviders.values}") - val invocation = provider.chooseInvocation() - busyProviders.put(provider, invocation) - try { - return invocation.call() - } finally { - busyProviders.remove(provider) - } - } -} - -private val autotypes: Map, Class<*>> = mutableMapOf, Class<*>>().apply { - Arrays::class.java.declaredMethods.filter { it.name == "hashCode" }.map { it.parameterTypes[0].componentType }.filter { it.isPrimitive }.forEach { - val boxed = java.lang.reflect.Array.get(java.lang.reflect.Array.newInstance(it, 1), 0).javaClass - put(it, boxed) - put(boxed, it) - } -} - -private infix fun Class<*>.isSatisfiedBy(clazz: Class<*>): Boolean { - return isAssignableFrom(clazz) || autotypes[this] == clazz -} - -private class LazyHubImpl(private val parent: LazyHubImpl?, private val busyProviders: BusyProviders = parent?.busyProviders ?: BusyProviders()) : MutableLazyHub { - private val providers = mutableMapOf, MutableList>>() - private fun add(provider: Provider<*>, type: Class<*> = provider.type, registered: MutableSet> = mutableSetOf()) { - if (!registered.add(type)) return - providers[type]?.add(provider) ?: providers.put(type, mutableListOf(provider)) - Stream.concat(Arrays.stream(type.interfaces), Stream.of(type.superclass, autotypes[type]).filterNotNull()).forEach { - add(provider, it, registered) - } - } - - /** The non-empty list of providers, or null. */ - private fun findProviders(clazz: Class): List>? = uncheckedCast(providers[clazz]) ?: parent?.findProviders(clazz) - - private fun dropAll(serviceClass: Class<*>) { - val removed = mutableSetOf>() - providers.iterator().run { - while (hasNext()) { - val entry = next() - if (serviceClass isSatisfiedBy entry.key) { - removed.addAll(entry.value) - remove() - } - } - } - providers.values.iterator().run { - while (hasNext()) { - val providers = next() - providers.removeAll(removed) - if (providers.isEmpty()) remove() - } - } - } - - override fun getOrNull(clazz: Class) = findProviders(clazz)?.run { (singleOrNull() ?: throw TooManyProvidersException(clazz.toString())).obj } - override fun getAll(clazz: Class) = findProviders(clazz)?.map { it.obj } ?: emptyList() - override fun child(): MutableLazyHub = LazyHubImpl(this) - override fun obj(obj: Any) = add(SimpleProvider(obj)) - override fun obj(service: KClass, obj: T) { - dropAll(service.java) - obj(obj) - } - - override fun factory(service: KClass, factory: KFunction) = factory.validate().let { - dropAll(service.java) - addFactory(it) - } - - override fun impl(service: KClass, impl: KClass) = impl.validate().let { - dropAll(service.java) - addConcrete(it) - } - - override fun impl(service: KClass, impl: Class) = impl.validate().let { - dropAll(service.java) - addConcrete(it) - } - - override fun factory(factory: KFunction<*>) = addFactory(factory.validate()) - private fun addFactory(factory: KConstructor) { - val type = factory.kFunction.returnType.toJavaType().let { if (it == Void.TYPE) Unit::class.java else it as Class<*> } - add(LazyProvider(busyProviders, factory, uncheckedCast(type)) { factory.toInvocation() }) - } - - override fun factory(lh: LazyHub, type: KClass<*>) = addFactory(lh, type) - private fun addFactory(lh: LazyHub, type: KClass) { - add(LazyProvider(busyProviders, lh, type.java) { Callable { lh[type] } }) - } - - override fun impl(impl: KClass<*>) = implGeneric(impl) - private fun implGeneric(type: KClass) = addConcrete(type.validate()) - override fun impl(impl: Class<*>) = implGeneric(impl) - private fun implGeneric(type: Class) = addConcrete(type.validate()) - private fun

> addConcrete(concrete: Concrete) { - add(LazyProvider(busyProviders, concrete, concrete.clazz) { - var fail: UnsatisfiableParamException? = null - val satisfiable = concrete.publicConstructors.mapNotNull { constructor -> - try { - constructor.toInvocation() - } catch (e: UnsatisfiableParamException) { - fail?.addSuppressed(e) ?: run { fail = e } - null - } - } - if (satisfiable.isEmpty()) throw fail!! - val greediest = mutableListOf(satisfiable[0]) - var providerCount = greediest[0].providerCount() - satisfiable.stream().skip(1).forEach next@ { - val pc = it.providerCount() - if (pc < providerCount) return@next - if (pc > providerCount) { - greediest.clear() - providerCount = pc - } - greediest += it - } - greediest.singleOrNull() ?: throw NoUniqueGreediestSatisfiableConstructorException(greediest.toString()) - }) - } - - private fun arrayProvider(arrayType: Class<*>, componentType: Class): LazyProvider>? { - val providers = findProviders(componentType) ?: return null - return LazyProvider(busyProviders, null, uncheckedCast(arrayType)) { - Callable { providers.stream().map { it.obj }.toTypedArray(componentType) } - } - } - - private fun

PublicConstructor.toInvocation() = Invocation(this, params.mapNotNull { param -> - if (param.type.isArray) { - val provider = arrayProvider(param.type, param.type.componentType) - when (provider) { - null -> param.supplierWhenUnsatisfiable()?.let { param to it } - else -> param to ArgSupplier(provider) - } - } else { - val providers = findProviders(param.type) - when (providers?.size) { - null -> param.supplierWhenUnsatisfiable()?.let { param to it } - 1 -> param to ArgSupplier(providers[0]) - else -> throw TooManyProvidersException(param.toString()) - } - } - }) -} diff --git a/node/src/main/kotlin/net/corda/lazyhub/LazyHubModel.kt b/node/src/main/kotlin/net/corda/lazyhub/LazyHubModel.kt deleted file mode 100644 index 7c3217f7d7..0000000000 --- a/node/src/main/kotlin/net/corda/lazyhub/LazyHubModel.kt +++ /dev/null @@ -1,130 +0,0 @@ -package net.corda.lazyhub - -import net.corda.core.internal.toMap -import net.corda.core.internal.toTypedArray -import net.corda.core.internal.uncheckedCast -import java.lang.reflect.* -import kotlin.reflect.KClass -import kotlin.reflect.KFunction -import kotlin.reflect.KParameter -import kotlin.reflect.KVisibility -import kotlin.reflect.jvm.internal.ReflectProperties -import kotlin.reflect.jvm.isAccessible - -private val javaTypeDelegateField = Class.forName("kotlin.reflect.jvm.internal.KTypeImpl").getDeclaredField("javaType\$delegate").apply { isAccessible = true } -internal fun kotlin.reflect.KType.toJavaType() = (javaTypeDelegateField.get(this) as ReflectProperties.Val<*>)() -internal interface Provider { - /** Most specific known type i.e. directly registered implementation class, or declared return type of factory method. */ - val type: Class - /** May be lazily computed. */ - val obj: T -} - -/** Like [Provider] but capable of supplying null. */ -internal class ArgSupplier(val provider: Provider<*>?) { - companion object { - val nullSupplier = ArgSupplier(null) - } - - operator fun invoke() = provider?.obj -} - -/** Common interface to Kotlin/Java params. */ -internal interface Param { - val type: Class<*> - /** The supplier, or null to supply nothing so the Kotlin default is used. */ - fun supplierWhenUnsatisfiable(): ArgSupplier? = throw (if (type.isArray) ::UnsatisfiableArrayException else ::NoSuchProviderException)(toString()) -} - -internal class KParam(val kParam: KParameter) : Param { - override val type = run { - var jType = kParam.type.toJavaType() - loop@ while (true) { - jType = when (jType) { - is ParameterizedType -> jType.rawType - is TypeVariable<*> -> jType.bounds.first() // Potentially surprising but most consistent behaviour, see unit tests. - else -> break@loop - } - } - jType as Class<*> - } - - override fun supplierWhenUnsatisfiable() = when { - kParam.isOptional -> null // Use default value, even if param is also nullable. - kParam.type.isMarkedNullable -> ArgSupplier.nullSupplier - else -> super.supplierWhenUnsatisfiable() - } - - override fun toString() = kParam.toString() -} - -internal class JParam(private val param: Parameter, private val index: Int, override val type: Class<*>) : Param { - override fun toString() = "parameter #$index ${param.name} of ${param.declaringExecutable}" -} - -internal interface PublicConstructor { - val params: List

- operator fun invoke(argSuppliers: List>): T -} - -internal class KConstructor(val kFunction: KFunction) : PublicConstructor { - companion object { - fun KFunction.validate() = run { - if (returnType.isMarkedNullable) throw NullableReturnTypeException(toString()) - isAccessible = true - KConstructor(this) - } - } - - override val params = kFunction.parameters.map(::KParam) - override fun invoke(argSuppliers: List>): T { - return kFunction.callBy(argSuppliers.stream().map { (param, supplier) -> param.kParam to supplier() }.toMap()) - } - - override fun toString() = kFunction.toString() -} - -internal class JConstructor(private val constructor: Constructor) : PublicConstructor { - // Much cheaper to get the types up-front than via the Parameter API: - override val params = constructor.parameters.zip(constructor.parameterTypes).mapIndexed { i, (p, t) -> JParam(p, i, t) } - - override fun invoke(argSuppliers: List>): T { - return constructor.newInstance(*argSuppliers.stream().map { (_, supplier) -> supplier() }.toTypedArray()) - } - - override fun toString() = constructor.toString() -} - -internal interface Concrete> { - val clazz: Class - val publicConstructors: List -} - -internal class KConcrete private constructor(private val kClass: KClass) : Concrete> { - companion object { - fun KClass.validate() = run { - if (isAbstract) throw AbstractTypeException(toString()) - KConcrete(this).apply { - if (publicConstructors.isEmpty()) throw NoPublicConstructorsException(toString()) - } - } - } - - override val clazz get() = kClass.java - override val publicConstructors = kClass.constructors.filter { it.visibility == KVisibility.PUBLIC }.map(::KConstructor) - override fun toString() = kClass.toString() -} - -internal class JConcrete private constructor(override val clazz: Class) : Concrete> { - companion object { - fun Class.validate() = run { - if (Modifier.isAbstract(modifiers)) throw AbstractTypeException(toString()) - JConcrete(this).apply { - if (publicConstructors.isEmpty()) throw NoPublicConstructorsException(toString()) - } - } - } - - override val publicConstructors = uncheckedCast>, Array>>(clazz.constructors).map(::JConstructor) - override fun toString() = clazz.toString() -} diff --git a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt index da9523a507..6280ed522a 100644 --- a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt +++ b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt @@ -26,14 +26,12 @@ import net.corda.core.transactions.SignedTransaction import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.debug import net.corda.core.utilities.getOrThrow -import net.corda.lazyhub.LazyHub -import net.corda.lazyhub.MutableLazyHub -import net.corda.lazyhub.lazyHub import net.corda.node.VersionInfo import net.corda.node.internal.classloading.requireAnnotation import net.corda.node.internal.cordapp.CordappLoader import net.corda.node.internal.cordapp.CordappProviderImpl import net.corda.node.internal.cordapp.CordappProviderInternal +import net.corda.node.internal.security.RPCSecurityManager import net.corda.node.services.ContractUpgradeHandler import net.corda.node.services.FinalityHandler import net.corda.node.services.NotaryChangeHandler @@ -56,6 +54,7 @@ import net.corda.node.services.transactions.* import net.corda.node.services.upgrade.ContractUpgradeServiceImpl import net.corda.node.services.vault.NodeVaultService import net.corda.node.services.vault.VaultSoftLockManager +import net.corda.node.shell.InteractiveShell import net.corda.node.utilities.AffinityExecutor import net.corda.nodeapi.internal.DevIdentityGenerator import net.corda.nodeapi.internal.SignedNodeInfo @@ -143,6 +142,9 @@ abstract class AbstractNode(val configuration: NodeConfiguration, protected val runOnStop = ArrayList<() -> Any?>() private val _nodeReadyFuture = openFuture() protected var networkMapClient: NetworkMapClient? = null + + lateinit var securityManager: RPCSecurityManager get + /** Completes once the node has successfully registered with the network map service * or has loaded network map data from local database */ val nodeReadyFuture: CordaFuture get() = _nodeReadyFuture @@ -197,31 +199,22 @@ abstract class AbstractNode(val configuration: NodeConfiguration, } } - protected open fun configure(lh: MutableLazyHub) { - // TODO: Migrate classes and factories from start method. - } - open fun start(): StartedNode { check(started == null) { "Node has already been started" } log.info("Node starting up ...") initCertificate() val schemaService = NodeSchemaService(cordappLoader.cordappSchemas, configuration.notary != null) val (identity, identityKeyPair) = obtainIdentity(notaryConfig = null) - val lh = lazyHub() - configure(lh) val identityService = makeIdentityService(identity.certificate) - lh.obj(identityService) networkMapClient = configuration.compatibilityZoneURL?.let { NetworkMapClient(it, identityService.trustRoot) } retrieveNetworkParameters(identityService.trustRoot) // Do all of this in a database transaction so anything that might need a connection has one. val (startedImpl, schedulerService) = initialiseDatabasePersistence(schemaService, identityService) { database -> - lh.obj(database) val networkMapCache = NetworkMapCacheImpl(PersistentNetworkMapCache(database, networkParameters.notaries).start(), identityService) val (keyPairs, info) = initNodeInfo(networkMapCache, identity, identityKeyPair) - lh.obj(info) identityService.loadIdentities(info.legalIdentitiesAndCerts) val transactionStorage = makeTransactionStorage(database, configuration.transactionCacheSizeBytes) - val nodeServices = makeServices(lh, keyPairs, schemaService, transactionStorage, database, info, identityService, networkMapCache) + val nodeServices = makeServices(keyPairs, schemaService, transactionStorage, database, info, identityService, networkMapCache) val notaryService = makeNotaryService(nodeServices, database) val smm = makeStateMachineManager(database) val flowLogicRefFactory = FlowLogicRefFactoryImpl(cordappLoader.appClassLoader) @@ -244,13 +237,13 @@ abstract class AbstractNode(val configuration: NodeConfiguration, } makeVaultObservers(schedulerService, database.hibernateConfig, smm, schemaService, flowLogicRefFactory) val rpcOps = makeRPCOps(flowStarter, database, smm) - lh.obj(rpcOps) - lh.getAll(Unit::class) // Run side-effects. + startMessagingService(rpcOps) installCoreFlows() val cordaServices = installCordaServices(flowStarter) tokenizableServices = nodeServices + cordaServices + schedulerService registerCordappFlows(smm) _services.rpcFlows += cordappLoader.cordapps.flatMap { it.rpcFlows } + startShell(rpcOps) Pair(StartedNodeImpl(this, _services, info, checkpointStorage, smm, attachments, network, database, rpcOps, flowStarter, notaryService), schedulerService) } val networkMapUpdater = NetworkMapUpdater(services.networkMapCache, @@ -286,6 +279,10 @@ abstract class AbstractNode(val configuration: NodeConfiguration, */ protected abstract fun getRxIoScheduler(): Scheduler + open fun startShell(rpcOps: CordaRPCOps) { + InteractiveShell.startShell(configuration, rpcOps, securityManager, _services.identityService, _services.database) + } + private fun initNodeInfo(networkMapCache: NetworkMapCacheBaseInternal, identity: PartyAndCertificate, identityKeyPair: KeyPair): Pair, NodeInfo> { @@ -536,7 +533,7 @@ abstract class AbstractNode(val configuration: NodeConfiguration, * Builds node internal, advertised, and plugin services. * Returns a list of tokenizable services to be added to the serialisation context. */ - private fun makeServices(lh: LazyHub, keyPairs: Set, schemaService: SchemaService, transactionStorage: WritableTransactionStorage, database: CordaPersistence, info: NodeInfo, identityService: IdentityServiceInternal, networkMapCache: NetworkMapCacheInternal): MutableList { + private fun makeServices(keyPairs: Set, schemaService: SchemaService, transactionStorage: WritableTransactionStorage, database: CordaPersistence, info: NodeInfo, identityService: IdentityServiceInternal, networkMapCache: NetworkMapCacheInternal): MutableList { checkpointStorage = DBCheckpointStorage() val metrics = MetricRegistry() attachments = NodeAttachmentService(metrics) @@ -552,7 +549,7 @@ abstract class AbstractNode(val configuration: NodeConfiguration, database, info, networkMapCache) - network = lh[MessagingService::class] // TODO: Retire the lateinit var. + network = makeMessagingService(database, info) val tokenizableServices = mutableListOf(attachments, network, services.vaultService, services.keyManagementService, services.identityService, platformClock, services.auditService, services.monitoringService, services.networkMapCache, services.schemaService, @@ -715,6 +712,9 @@ abstract class AbstractNode(val configuration: NodeConfiguration, _started = null } + protected abstract fun makeMessagingService(database: CordaPersistence, info: NodeInfo): MessagingService + protected abstract fun startMessagingService(rpcOps: RPCOps) + private fun obtainIdentity(notaryConfig: NotaryConfig?): Pair { val keyStore = KeyStoreWrapper(configuration.nodeKeystore, configuration.keyStorePassword) diff --git a/node/src/main/kotlin/net/corda/node/internal/Node.kt b/node/src/main/kotlin/net/corda/node/internal/Node.kt index a36a8f78e7..c4149f74ac 100644 --- a/node/src/main/kotlin/net/corda/node/internal/Node.kt +++ b/node/src/main/kotlin/net/corda/node/internal/Node.kt @@ -5,7 +5,6 @@ import net.corda.core.concurrent.CordaFuture import net.corda.core.internal.concurrent.openFuture import net.corda.core.internal.concurrent.thenMatch import net.corda.core.internal.uncheckedCast -import net.corda.core.messaging.CordaRPCOps import net.corda.core.messaging.RPCOps import net.corda.core.node.NodeInfo import net.corda.core.node.ServiceHub @@ -15,10 +14,8 @@ import net.corda.core.serialization.internal.SerializationEnvironmentImpl import net.corda.core.serialization.internal.nodeSerializationEnv import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.contextLogger -import net.corda.lazyhub.MutableLazyHub import net.corda.node.VersionInfo import net.corda.node.internal.cordapp.CordappLoader -import net.corda.node.internal.security.RPCSecurityManager import net.corda.node.internal.security.RPCSecurityManagerImpl import net.corda.node.serialization.KryoServerSerializationScheme import net.corda.node.services.api.SchemaService @@ -27,7 +24,6 @@ import net.corda.node.services.config.SecurityConfiguration import net.corda.node.services.config.VerifierType import net.corda.node.services.messaging.* import net.corda.node.services.transactions.InMemoryTransactionVerifierService -import net.corda.node.shell.InteractiveShell import net.corda.node.utilities.AddressUtils import net.corda.node.utilities.AffinityExecutor import net.corda.node.utilities.DemoClock @@ -138,27 +134,16 @@ open class Node(configuration: NodeConfiguration, private var messageBroker: ArtemisMessagingServer? = null private var shutdownHook: ShutdownHook? = null - override fun configure(lh: MutableLazyHub) { - super.configure(lh) + + override fun makeMessagingService(database: CordaPersistence, info: NodeInfo): MessagingService { // Construct security manager reading users data either from the 'security' config section // if present or from rpcUsers list if the former is missing from config. - lh.obj(configuration.security?.authService ?: SecurityConfiguration.AuthService.fromUsers(configuration.rpcUsers)) - lh.impl(RPCSecurityManagerImpl::class) - configuration.messagingServerAddress?.also { - lh.obj(MessagingServerAddress(it)) - } ?: run { - lh.factory(this::makeLocalMessageBroker) - } - lh.factory(this::makeMessagingService) - // Side-effects: - lh.factory(this::startMessagingService) - lh.factory(this::startShell) - } + val securityManagerConfig = configuration.security?.authService ?: + SecurityConfiguration.AuthService.fromUsers(configuration.rpcUsers) - class MessagingServerAddress(val address: NetworkHostAndPort) + securityManager = RPCSecurityManagerImpl(securityManagerConfig) - private fun makeMessagingService(database: CordaPersistence, info: NodeInfo, messagingServerAddress: MessagingServerAddress): MessagingService { - val serverAddress = messagingServerAddress.address + val serverAddress = configuration.messagingServerAddress ?: makeLocalMessageBroker() val advertisedAddress = info.addresses.single() printBasicNodeInfo("Incoming connection address", advertisedAddress.toString()) @@ -181,10 +166,10 @@ open class Node(configuration: NodeConfiguration, networkParameters.maxMessageSize) } - private fun makeLocalMessageBroker(securityManager: RPCSecurityManager): MessagingServerAddress { + private fun makeLocalMessageBroker(): NetworkHostAndPort { with(configuration) { messageBroker = ArtemisMessagingServer(this, p2pAddress.port, rpcAddress?.port, services.networkMapCache, securityManager, networkParameters.maxMessageSize) - return MessagingServerAddress(NetworkHostAndPort("localhost", p2pAddress.port)) + return NetworkHostAndPort("localhost", p2pAddress.port) } } @@ -232,7 +217,7 @@ open class Node(configuration: NodeConfiguration, } } - private fun startMessagingService(rpcOps: RPCOps, securityManager: RPCSecurityManager) { + override fun startMessagingService(rpcOps: RPCOps) { // Start up the embedded MQ server messageBroker?.apply { runOnStop += this::stop @@ -253,10 +238,6 @@ open class Node(configuration: NodeConfiguration, } } - private fun startShell(rpcOps: CordaRPCOps, securityManager: RPCSecurityManager, identityService: IdentityService, database: CordaPersistence) { - InteractiveShell.startShell(configuration, rpcOps, securityManager, identityService, database) - } - /** * If the node is persisting to an embedded H2 database, then expose this via TCP with a DB URL of the form: * jdbc:h2:tcp://:/node diff --git a/node/src/test/kotlin/net/corda/lazyhub/LazyHubTests.kt b/node/src/test/kotlin/net/corda/lazyhub/LazyHubTests.kt deleted file mode 100644 index 5d462585bc..0000000000 --- a/node/src/test/kotlin/net/corda/lazyhub/LazyHubTests.kt +++ /dev/null @@ -1,640 +0,0 @@ -package net.corda.lazyhub - -import net.corda.core.internal.uncheckedCast -import org.assertj.core.api.Assertions.catchThrowable -import org.hamcrest.CoreMatchers.* -import org.junit.Assert.* -import org.junit.Ignore -import org.junit.Test -import java.io.Closeable -import java.io.IOException -import java.io.Serializable -import kotlin.reflect.KFunction -import kotlin.reflect.jvm.javaConstructor -import kotlin.reflect.jvm.javaMethod -import kotlin.test.assertEquals -import kotlin.test.fail - -open class LazyHubTests { - private val lh = lazyHub() - - class Config(val info: String) - interface A - interface B { - val a: A - } - - class AImpl(val config: Config) : A - class BImpl(override val a: A) : B - class Spectator { - init { - fail("Should not be instantiated.") - } - } - - @Test - fun `basic functionality`() { - val config = Config("woo") - lh.obj(config) - lh.impl(AImpl::class) - lh.impl(BImpl::class) - lh.impl(Spectator::class) - val b = lh[B::class] - // An impl is instantiated at most once per LazyHub: - assertSame(b.a, lh[A::class]) - assertSame(b, lh[B::class]) - // More specific type to expose config without casting: - val a = lh[AImpl::class] - assertSame(b.a, a) - assertSame(config, a.config) - } - - private fun createA(config: Config): A = AImpl(config) // Declared return type is significant. - internal open fun createB(): B = fail("Should not be called.") - @Test - fun `factory works`() { - lh.obj(Config("x")) - lh.factory(this::createA) // Observe private is OK. - assertSame(AImpl::class.java, lh[A::class].javaClass) - // The factory declares A not AImpl as its return type, and lh doesn't try to be clever: - catchThrowable { lh[AImpl::class] }.run { - assertSame(NoSuchProviderException::class.java, javaClass) - assertEquals(AImpl::class.toString(), message) - } - } - - @Ignore - class Subclass : LazyHubTests() { // Should not run as tests. - @Suppress("unused") - private fun createA(@Suppress("UNUSED_PARAMETER") config: Config): A = fail("Should not be called.") - - override fun createB() = BImpl(AImpl(Config("Subclass"))) // More specific return type is OK. - } - - @Suppress("MemberVisibilityCanPrivate") - internal fun addCreateATo(lh: MutableLazyHub) { - lh.factory(this::createA) - } - - @Suppress("MemberVisibilityCanPrivate") - internal fun addCreateBTo(lh: MutableLazyHub) { - lh.factory(this::createB) - } - - @Test - fun `private factory is not virtual`() { - val baseMethod = this::createA.javaMethod!! - // Check the Subclass version would override if baseMethod wasn't private: - Subclass::class.java.getDeclaredMethod(baseMethod.name, *baseMethod.parameterTypes) - lh.obj(Config("x")) - Subclass().addCreateATo(lh) - lh[A::class] // Should not blow up. - } - - @Test - fun `non-private factory is virtual`() { - Subclass().addCreateBTo(lh) - assertEquals("Subclass", (lh[B::class].a as AImpl).config.info) // Check overridden function was called. - // The signature that was added declares B not BImpl as its return type: - catchThrowable { lh[BImpl::class] }.run { - assertSame(NoSuchProviderException::class.java, javaClass) - assertEquals(BImpl::class.toString(), message) - } - } - - private fun returnsYay() = "yay" - class TakesString(@Suppress("UNUSED_PARAMETER") text: String) - - @Test - fun `too many providers`() { - lh.obj("woo") - lh.factory(this::returnsYay) - lh.impl(TakesString::class) - catchThrowable { lh[TakesString::class] }.run { - assertSame(TooManyProvidersException::class.java, javaClass) - assertEquals(TakesString::class.constructors.single().parameters[0].toString(), message) - assertThat(message, containsString(" #0 ")) - assertThat(message, endsWith(TakesString::class.qualifiedName)) - } - } - - class TakesStringOrInt(val text: String) { - @Suppress("unused") - constructor(number: Int) : this(number.toString()) - } - - @Test - fun `too many providers with alternate constructor`() { - lh.obj("woo") - lh.factory(this::returnsYay) - lh.impl(TakesStringOrInt::class) - val constructors = TakesStringOrInt::class.constructors.toList() - catchThrowable { lh[TakesStringOrInt::class] }.run { - assertSame(NoSuchProviderException::class.java, javaClass) - assertEquals(constructors[0].parameters[0].toString(), message) - assertThat(message, containsString(" #0 ")) - assertThat(message, endsWith(TakesStringOrInt::class.qualifiedName)) - suppressed.single().run { - assertSame(TooManyProvidersException::class.java, javaClass) - assertEquals(constructors[1].parameters[0].toString(), message) - assertThat(message, containsString(" #0 ")) - assertThat(message, endsWith(TakesStringOrInt::class.qualifiedName)) - } - } - lh.obj(123) - assertEquals("123", lh[TakesStringOrInt::class].text) - } - - @Test - fun genericClass() { - class G(val arg: T) - lh.obj("arg") - lh.impl(G::class) - assertEquals("arg", lh[G::class].arg) // Can't inspect type arg T as no such thing exists. - } - - private fun ntv(a: Y) = a.toString() - @Test - fun `nested type variable`() { - // First check it's actually legal to pass any old Closeable into the function: - val arg = Closeable {} - assertEquals(arg.toString(), ntv(arg)) - // Good, now check LazyHub can do it: - val ntv: Function1 = this::ntv - lh.factory(uncheckedCast>(ntv)) - lh.obj(arg) - assertEquals(arg.toString(), lh[String::class]) - } - - class PTWMB(val arg: Y) where Y : Closeable, Y : Serializable - private class CloseableAndSerializable : Closeable, Serializable { - override fun close() {} - } - - @Test - fun `parameter type with multiple bounds in java`() { - // At compile time we must pass something Closeable and Serializable into the constructor: - CloseableAndSerializable().let { assertSame(it, PTWMB(it).arg) } - // But at runtime only Closeable is needed (and Serializable is not enough) due to the leftmost bound erasure rule: - lh.impl(PTWMB::class.java) - lh.obj(object : Serializable {}) - catchThrowable { lh[PTWMB::class] }.run { - assertSame(NoSuchProviderException::class.java, javaClass) - assertThat(message, containsString(" #0 ")) - assertThat(message, endsWith(PTWMB::class.constructors.single().javaConstructor.toString())) - } - val arg = Closeable {} - lh.obj(arg) - assertSame(arg, lh[PTWMB::class].arg) - } - - @Test - fun `parameter type with multiple bounds in kotlin`() { - lh.impl(PTWMB::class) - lh.obj(object : Serializable {}) - catchThrowable { lh[PTWMB::class] }.run { - assertSame(NoSuchProviderException::class.java, javaClass) - assertEquals(PTWMB::class.constructors.single().parameters[0].toString(), message) - assertThat(message, containsString(" #0 ")) - assertThat(message, containsString(PTWMB::class.qualifiedName)) - } - val arg = Closeable {} - lh.obj(arg) - assertSame(arg, lh[PTWMB::class].arg) - } - - private fun ptwmb(arg: Y) where Y : Closeable, Y : Serializable = arg.toString() - @Test - fun `factory parameter type with multiple bounds`() { - val ptwmb: Function1 = this::ptwmb - val kFunction = uncheckedCast>(ptwmb) - lh.factory(kFunction) - lh.obj(object : Serializable {}) - catchThrowable { lh[String::class] }.run { - assertSame(NoSuchProviderException::class.java, javaClass) - assertEquals(kFunction.parameters[0].toString(), message) - assertThat(message, containsString(" #0 ")) - assertThat(message, endsWith(ptwmb.toString())) - } - val arg = Closeable {} - lh.obj(arg) - assertEquals(arg.toString(), lh[String::class]) - } - - private fun upt(a: Y) = a.toString() - @Test - fun `unbounded parameter type`() { - val upt: Function1 = this::upt - val kFunction: KFunction = uncheckedCast(upt) - lh.factory(kFunction) - // The only provider for Any is the factory, which is busy: - catchThrowable { lh[String::class] }.run { - assertSame(CircularDependencyException::class.java, javaClass) - assertThat(message, containsString("'$upt'")) - assertThat(message, endsWith(listOf(upt).toString())) - } - lh.obj(Any()) - // This time the factory isn't attempted: - catchThrowable { lh[String::class] }.run { - assertSame(TooManyProvidersException::class.java, javaClass) - assertEquals(kFunction.parameters[0].toString(), message) - assertThat(message, containsString(" #0 ")) - assertThat(message, endsWith(upt.toString())) - } - } - - open class NoPublicConstructor protected constructor() - - @Test - fun `no public constructor`() { - catchThrowable { lh.impl(NoPublicConstructor::class) }.run { - assertSame(NoPublicConstructorsException::class.java, javaClass) - assertEquals(NoPublicConstructor::class.toString(), message) - } - catchThrowable { lh.impl(NoPublicConstructor::class.java) }.run { - assertSame(NoPublicConstructorsException::class.java, javaClass) - assertEquals(NoPublicConstructor::class.toString(), message) - } - } - - private fun primitiveInt() = 1 - class IntConsumer(@Suppress("UNUSED_PARAMETER") i: Int) - class IntegerConsumer(@Suppress("UNUSED_PARAMETER") i: Int?) - - @Test - fun `boxed satisfies primitive`() { - lh.obj(1) - lh.impl(IntConsumer::class) - lh[IntConsumer::class] - } - - @Test - fun `primitive satisfies boxed`() { - lh.factory(this::primitiveInt) - lh.impl(IntegerConsumer::class.java) - lh[IntegerConsumer::class] - } - - // The primary constructor takes two distinct providers: - class TakesTwoThings(@Suppress("UNUSED_PARAMETER") first: String, @Suppress("UNUSED_PARAMETER") second: Int) { - // This constructor takes one repeated provider but we count it both times so greediness is 2: - @Suppress("unused") - constructor(first: Int, second: Int) : this(first.toString(), second) - - // This constructor would be greediest but is not satisfiable: - @Suppress("unused") - constructor(first: Int, second: String, @Suppress("UNUSED_PARAMETER") third: Config) : this(second, first) - } - - @Test - fun `equally greedy constructors kotlin`() { - lh.obj("str") - lh.obj(123) - lh.impl(TakesTwoThings::class) - catchThrowable { lh[TakesTwoThings::class] }.run { - assertSame(NoUniqueGreediestSatisfiableConstructorException::class.java, javaClass) - val expected = TakesTwoThings::class.constructors.filter { it.parameters.size == 2 } - assertEquals(2, expected.size) - assertThat(message, endsWith(expected.toString())) - } - } - - @Test - fun `equally greedy constructors java`() { - lh.obj("str") - lh.obj(123) - lh.impl(TakesTwoThings::class.java) - catchThrowable { lh[TakesTwoThings::class] }.run { - assertSame(NoUniqueGreediestSatisfiableConstructorException::class.java, javaClass) - val expected = TakesTwoThings::class.java.constructors.filter { it.parameters.size == 2 } - assertEquals(2, expected.size) - assertEquals(expected.toString(), message) - } - } - - private fun nrt(): String? = fail("Should not be invoked.") - @Test - fun `nullable return type is banned`() { - catchThrowable { lh.factory(this::nrt) }.run { - assertSame(NullableReturnTypeException::class.java, javaClass) - assertThat(message, endsWith(this@LazyHubTests::nrt.toString())) - } - } - - @Test - fun unsatisfiableArrayParam() { - class Impl(@Suppress("UNUSED_PARAMETER") v: Array) - lh.impl(Impl::class) - catchThrowable { lh[Impl::class] }.run { - assertSame(UnsatisfiableArrayException::class.java, javaClass) - assertEquals(Impl::class.constructors.single().parameters[0].toString(), message) - } - // Arrays are only special in real params, you should use getAll to get all the Strings: - catchThrowable { lh[Array::class] }.run { - assertSame(NoSuchProviderException::class.java, javaClass) - assertEquals(Array::class.java.toString(), message) - } - assertEquals(emptyList(), lh.getAll(String::class)) - } - - @Test - fun arrayParam1() { - class Impl(val v: Array) - lh.impl(Impl::class) - lh.obj("a") - assertArrayEquals(arrayOf("a"), lh[Impl::class].v) - } - - @Test - fun arrayParam2() { - class Impl(val v: Array) - lh.impl(Impl::class) - lh.obj("y") - lh.obj("x") - assertArrayEquals(arrayOf("y", "x"), lh[Impl::class].v) - } - - @Test - fun nullableArrayParam() { - class Impl(val v: Array?) - lh.impl(Impl::class) - assertEquals(null, lh[Impl::class].v) - } - - @Test - fun arraysAreNotCached() { - class B(val v: Array) - class A(val v: Array, val b: B) - class C(val v: Array) - class D(val v: Array) - lh.obj("x") - lh.obj("y") - lh.impl(A::class) - lh.impl(B::class) - val a = lh[A::class] - a.run { - assertArrayEquals(arrayOf("x", "y"), v) - assertArrayEquals(arrayOf("x", "y"), b.v) - assertNotSame(v, b.v) - } - assertSame(lh[B::class].v, a.b.v) // Because it's the same (cached) instance of B. - lh.impl(C::class) - lh[C::class].run { - assertArrayEquals(arrayOf("x", "y"), v) - assertNotSame(v, a.v) - assertNotSame(v, a.b.v) - } - lh.obj("z") - lh.impl(D::class) - lh[D::class].run { - assertArrayEquals(arrayOf("x", "y", "z"), v) - } - } - - class C1(@Suppress("UNUSED_PARAMETER") c2: C2) - class C2(@Suppress("UNUSED_PARAMETER") c3: String) - - private fun c3(@Suppress("UNUSED_PARAMETER") c2: C2): String { - fail("Should not be called.") - } - - @Test - fun `circularity error kotlin`() { - lh.impl(C1::class) - lh.impl(C2::class) - lh.factory(this::c3) - catchThrowable { lh[C1::class] }.run { - assertSame(CircularDependencyException::class.java, javaClass) - assertThat(message, containsString("'${C2::class}'")) - assertThat(message, endsWith(listOf(C1::class.constructors.single(), C2::class.constructors.single(), this@LazyHubTests::c3).toString())) - } - } - - @Test - fun `circularity error java`() { - lh.impl(C1::class.java) - lh.impl(C2::class.java) - lh.factory(this::c3) - catchThrowable { lh[C1::class] }.run { - assertSame(CircularDependencyException::class.java, javaClass) - assertThat(message, containsString("'${C2::class}'")) - assertThat(message, endsWith(listOf(C1::class.constructors.single().javaConstructor, C2::class.constructors.single().javaConstructor, this@LazyHubTests::c3).toString())) - } - } - - @Test - fun `ancestor hub providers are visible`() { - val c = Config("over here") - lh.obj(c) - lh.child().also { - it.impl(AImpl::class) - assertSame(c, it[AImpl::class].config) - } - lh.child().child().also { - it.impl(AImpl::class) - assertSame(c, it[AImpl::class].config) - } - } - - @Test - fun `descendant hub providers are not visible`() { - val child = lh.child() - child.obj(Config("over here")) - lh.impl(AImpl::class) - catchThrowable { lh[AImpl::class] }.run { - assertSame(NoSuchProviderException::class.java, javaClass) - assertEquals(AImpl::class.constructors.single().parameters.single().toString(), message) - } - // Fails even though we go via the child, as the cached AImpl in lh shouldn't have collaborators from descendant hubs: - catchThrowable { child[AImpl::class] }.run { - assertSame(NoSuchProviderException::class.java, javaClass) - assertEquals(AImpl::class.constructors.single().parameters.single().toString(), message) - } - } - - class AllConfigs(val configs: Array) - - @Test - fun `nearest ancestor with at least one provider wins`() { - lh.obj(Config("deep")) - lh.child().also { - it.child().also { - it.impl(AllConfigs::class) - assertEquals(listOf("deep"), it[AllConfigs::class].configs.map { it.info }) - } - it.obj(Config("shallow1")) - it.obj(Config("shallow2")) - it.child().also { - it.impl(AllConfigs::class) - assertEquals(listOf("shallow1", "shallow2"), it[AllConfigs::class].configs.map { it.info }) - } - it.child().also { - it.obj(Config("local")) - it.impl(AllConfigs::class) - assertEquals(listOf("local"), it[AllConfigs::class].configs.map { it.info }) - } - } - } - - @Test - fun `abstract type`() { - catchThrowable { lh.impl(Runnable::class) }.run { - assertSame(AbstractTypeException::class.java, javaClass) - assertEquals(Runnable::class.toString(), message) - } - catchThrowable { lh.impl(Runnable::class.java) }.run { - assertSame(AbstractTypeException::class.java, javaClass) - assertEquals(Runnable::class.java.toString(), message) - } - } - - private interface Service - open class GoodService : Service - abstract class BadService1 : Service - class BadService2 private constructor() : Service - - private fun badService3(): Service? = fail("Should not be called.") - @Test - fun `existing providers not removed if new type is bad`() { - lh.impl(GoodService::class) - catchThrowable { lh.impl(Service::class, BadService1::class) }.run { - assertSame(AbstractTypeException::class.java, javaClass) - assertEquals(BadService1::class.toString(), message) - } - catchThrowable { lh.impl(Service::class, BadService2::class) }.run { - assertSame(NoPublicConstructorsException::class.java, javaClass) - assertEquals(BadService2::class.toString(), message) - } - catchThrowable { lh.impl(Service::class, BadService2::class.java) }.run { - assertSame(NoPublicConstructorsException::class.java, javaClass) - assertEquals(BadService2::class.toString(), message) - } - // Type system won't let you pass in badService3, but I still want validation up-front: - catchThrowable { lh.factory(Service::class, uncheckedCast(this::badService3)) }.run { - assertSame(NullableReturnTypeException::class.java, javaClass) - assertEquals(this@LazyHubTests::badService3.toString(), message) - } - assertSame(GoodService::class.java, lh[Service::class].javaClass) - } - - class GoodService2 : GoodService() - - @Test - fun `service providers are removed completely`() { - lh.impl(GoodService::class) - assertSame(GoodService::class.java, lh[Service::class].javaClass) - lh.impl(GoodService::class, GoodService2::class) - // In particular, GoodService is no longer registered against Service (or Any): - assertSame(GoodService2::class.java, lh[Service::class].javaClass) - assertSame(GoodService2::class.java, lh[Any::class].javaClass) - } - - class JParamExample(@Suppress("UNUSED_PARAMETER") str: String, @Suppress("UNUSED_PARAMETER") num: Int) - - @Test - fun `JParam has useful toString`() { - val c = JParamExample::class.java.constructors.single() - // Parameter doesn't expose its index, here we deliberately pass in the wrong one to see what happens: - val text = JParam(c.parameters[0], 1, IOException::class.java).toString() - assertThat(text, containsString(" #1 ")) - assertThat(text, anyOf(containsString(" str "), containsString(" arg0 "))) - assertThat(text, endsWith(c.toString())) - } - - private val sideEffects = mutableListOf() - private fun sideEffect1() { - sideEffects.add(1) - } - - private fun sideEffect2() { - sideEffects.add(2) - } - - @Test - fun `side-effects are idempotent as a consequence of caching of results`() { - lh.factory(this::sideEffect1) - assertEquals(listOf(Unit), lh.getAll(Unit::class)) - assertEquals(listOf(1), sideEffects) - lh.factory(this::sideEffect2) - assertEquals(listOf(Unit, Unit), lh.getAll(Unit::class)) // Get both results. - assertEquals(listOf(1, 2), sideEffects) // sideEffect1 didn't run again. - } - - @Test - fun `getAll returns empty list when there is nothing to return`() { - // This is in contrast to the exception thrown by an array param, which would not be useful to replicate here: - assertEquals(emptyList(), lh.getAll(IOException::class)) - } - - // Two params needed to make primary constructor the winner when both are satisfiable. - // It's probably true that the secondary will always trigger a CircularDependencyException, but LazyHub isn't clever enough to tell. - class InvocationSwitcher(@Suppress("UNUSED_PARAMETER") s: String, @Suppress("UNUSED_PARAMETER") t: String) { - @Suppress("unused") - constructor(same: InvocationSwitcher) : this(same.toString(), same.toString()) - } - - @Test - fun `chosen constructor is not set in stone`() { - lh.impl(InvocationSwitcher::class) - assertSame(CircularDependencyException::class.java, catchThrowable { lh[InvocationSwitcher::class] }.javaClass) - lh.obj("alt") - lh[InvocationSwitcher::class] // Succeeds via other constructor. - } - - class GreedinessUnits(@Suppress("UNUSED_PARAMETER") v: Array, @Suppress("UNUSED_PARAMETER") z: Int) { - // Two greediness units even though it's one provider repeated: - @Suppress("unused") - constructor(z1: Int, z2: Int) : this(emptyArray(), z1 + z2) - } - - @Test - fun `array param counts as one greediness unit`() { - lh.obj("x") - lh.obj("y") - lh.obj(100) - lh.impl(GreedinessUnits::class) - assertSame(NoUniqueGreediestSatisfiableConstructorException::class.java, catchThrowable { lh[GreedinessUnits::class] }.javaClass) - } - - interface TriangleBase - interface TriangleSide : TriangleBase - class TriangleImpl : TriangleBase, TriangleSide - - @Test - fun `provider registered exactly once against each supertype`() { - lh.impl(TriangleImpl::class) - lh[TriangleBase::class] // Don't throw TooManyProvidersException. - } - - interface Service1 - interface Service2 - class ServiceImpl1 : Service1, Service2 - class ServiceImpl2 : Service2 - - @Test - fun `do not leak empty provider list`() { - lh.impl(ServiceImpl1::class) - lh.impl(Service2::class, ServiceImpl2::class) - assertSame(NoSuchProviderException::class.java, catchThrowable { lh[Service1::class] }.javaClass) - } - - class Global - class Session(val global: Global, val local: Int) - - @Test - fun `child can be used to create a scope`() { - lh.impl(Global::class) - lh.factory(lh.child().also { - it.obj(1) - it.impl(Session::class) - }, Session::class) - lh.factory(lh.child().also { - it.obj(2) - it.impl(Session::class) - }, Session::class) - val sessions = lh.getAll(Session::class) - val g = lh[Global::class] - sessions.forEach { assertSame(g, it.global) } - assertEquals(listOf(1, 2), sessions.map { it.local }) - } -} diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNode.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNode.kt index b9901a881c..f6a47a79f8 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNode.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNode.kt @@ -5,6 +5,7 @@ import com.google.common.jimfs.Jimfs import com.nhaarman.mockito_kotlin.doReturn import com.nhaarman.mockito_kotlin.whenever import net.corda.core.DoNotImplement +import net.corda.core.crypto.entropyToKeyPair import net.corda.core.crypto.Crypto import net.corda.core.crypto.random63BitValue import net.corda.core.identity.CordaX500Name @@ -14,15 +15,17 @@ import net.corda.core.internal.VisibleForTesting import net.corda.core.internal.createDirectories import net.corda.core.internal.createDirectory import net.corda.core.internal.uncheckedCast +import net.corda.core.messaging.CordaRPCOps import net.corda.core.messaging.MessageRecipients +import net.corda.core.messaging.RPCOps import net.corda.core.messaging.SingleMessageRecipient +import net.corda.core.node.NodeInfo import net.corda.core.node.services.IdentityService import net.corda.core.node.services.KeyManagementService import net.corda.core.serialization.SerializationWhitelist import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.contextLogger import net.corda.core.utilities.seconds -import net.corda.lazyhub.MutableLazyHub import net.corda.node.VersionInfo import net.corda.node.internal.AbstractNode import net.corda.node.internal.StartedNode @@ -294,14 +297,9 @@ open class MockNetwork(private val cordappPackages: List, } } - override fun configure(lh: MutableLazyHub) { - super.configure(lh) - lh.factory(this::makeMessagingService) - } - // We only need to override the messaging service here, as currently everything that hits disk does so // through the java.nio API which we are already mocking via Jimfs. - private fun makeMessagingService(database: CordaPersistence): MessagingService { + override fun makeMessagingService(database: CordaPersistence, info: NodeInfo): MessagingService { require(id >= 0) { "Node ID must be zero or positive, was passed: " + id } return mockNet.messagingNetwork.createNodeWithID( !mockNet.threadPerNode, @@ -320,6 +318,14 @@ open class MockNetwork(private val cordappPackages: List, return E2ETestKeyManagementService(identityService, keyPairs) } + override fun startShell(rpcOps: CordaRPCOps) { + //No mock shell + } + + override fun startMessagingService(rpcOps: RPCOps) { + // Nothing to do + } + // This is not thread safe, but node construction is done on a single thread, so that should always be fine override fun generateKeyPair(): KeyPair { counter = counter.add(BigInteger.ONE) From f59560bb0647c70aa439ca255b316849811fcbf5 Mon Sep 17 00:00:00 2001 From: szymonsztuka Date: Fri, 19 Jan 2018 17:09:02 +0000 Subject: [PATCH 11/36] Database schema changes. (#2389) Changes compatible with R3.Corda (ENT-794): 1) Added Hibernate corda-wrapper-binary two to to columns. 2) Shorten names of tables in dummy schemas used in tests. 3) Undo removal of compound index of VaultTxnNote (b423fea). 4) Assertions for 2 vault tests don't rely on order of rows. --- .../schemas/CommercialPaperSchemaV1.kt | 1 + .../schemas/SampleCommercialPaperSchemaV1.kt | 1 + .../PersistentUniquenessProvider.kt | 2 + .../corda/node/services/vault/VaultSchema.kt | 3 +- .../node/services/vault/VaultQueryTests.kt | 44 +++++++++---------- .../internal/vault/DummyDealStateSchemaV1.kt | 2 +- .../vault/DummyLinearStateSchemaV2.kt | 2 +- 7 files changed, 28 insertions(+), 27 deletions(-) diff --git a/finance/src/main/kotlin/net/corda/finance/schemas/CommercialPaperSchemaV1.kt b/finance/src/main/kotlin/net/corda/finance/schemas/CommercialPaperSchemaV1.kt index c3a9743115..701630514c 100644 --- a/finance/src/main/kotlin/net/corda/finance/schemas/CommercialPaperSchemaV1.kt +++ b/finance/src/main/kotlin/net/corda/finance/schemas/CommercialPaperSchemaV1.kt @@ -33,6 +33,7 @@ object CommercialPaperSchemaV1 : MappedSchema(schemaFamily = CommercialPaperSche var issuancePartyHash: String, @Column(name = "issuance_ref") + @Type(type = "corda-wrapper-binary") var issuanceRef: ByteArray, @Column(name = "owner_key_hash", length = MAX_HASH_HEX_SIZE) diff --git a/finance/src/test/kotlin/net/corda/finance/schemas/SampleCommercialPaperSchemaV1.kt b/finance/src/test/kotlin/net/corda/finance/schemas/SampleCommercialPaperSchemaV1.kt index f407bf7fc2..e3080952f4 100644 --- a/finance/src/test/kotlin/net/corda/finance/schemas/SampleCommercialPaperSchemaV1.kt +++ b/finance/src/test/kotlin/net/corda/finance/schemas/SampleCommercialPaperSchemaV1.kt @@ -31,6 +31,7 @@ object SampleCommercialPaperSchemaV1 : MappedSchema(schemaFamily = CommercialPap var issuancePartyHash: String, @Column(name = "issuance_ref") + @Type(type = "corda-wrapper-binary") var issuanceRef: ByteArray, @Column(name = "owner_key_hash", length = MAX_HASH_HEX_SIZE) diff --git a/node/src/main/kotlin/net/corda/node/services/transactions/PersistentUniquenessProvider.kt b/node/src/main/kotlin/net/corda/node/services/transactions/PersistentUniquenessProvider.kt index a38e6bd446..c007475d26 100644 --- a/node/src/main/kotlin/net/corda/node/services/transactions/PersistentUniquenessProvider.kt +++ b/node/src/main/kotlin/net/corda/node/services/transactions/PersistentUniquenessProvider.kt @@ -13,6 +13,7 @@ import net.corda.core.serialization.SingletonSerializeAsToken import net.corda.core.utilities.contextLogger import net.corda.node.utilities.AppendOnlyPersistentMap import net.corda.nodeapi.internal.persistence.NODE_DATABASE_PREFIX +import org.hibernate.annotations.Type import java.io.Serializable import java.util.* import javax.annotation.concurrent.ThreadSafe @@ -43,6 +44,7 @@ class PersistentUniquenessProvider : UniquenessProvider, SingletonSerializeAsTok var name: String = "", @Column(name = "requesting_party_key", length = 255) + @Type(type = "corda-wrapper-binary") var owningKey: ByteArray = ByteArray(0) ) : Serializable diff --git a/node/src/main/kotlin/net/corda/node/services/vault/VaultSchema.kt b/node/src/main/kotlin/net/corda/node/services/vault/VaultSchema.kt index a86585bc87..5dceb42bab 100644 --- a/node/src/main/kotlin/net/corda/node/services/vault/VaultSchema.kt +++ b/node/src/main/kotlin/net/corda/node/services/vault/VaultSchema.kt @@ -151,7 +151,8 @@ object VaultSchemaV1 : MappedSchema(schemaFamily = VaultSchema.javaClass, versio @Entity @Table(name = "vault_transaction_notes", - indexes = arrayOf(Index(name = "transaction_id_index", columnList = "transaction_id"))) + indexes = arrayOf(Index(name = "seq_no_index", columnList = "seq_no"), + Index(name = "transaction_id_index", columnList = "transaction_id"))) class VaultTxnNote( @Id @GeneratedValue diff --git a/node/src/test/kotlin/net/corda/node/services/vault/VaultQueryTests.kt b/node/src/test/kotlin/net/corda/node/services/vault/VaultQueryTests.kt index 836939050e..d29c3e3ca0 100644 --- a/node/src/test/kotlin/net/corda/node/services/vault/VaultQueryTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/vault/VaultQueryTests.kt @@ -745,24 +745,18 @@ class VaultQueryTests { // DOCEND VaultQueryExample22 assertThat(results.otherResults).hasSize(15) - /** CHF */ - assertThat(results.otherResults[0]).isEqualTo(50000L) - assertThat(results.otherResults[1]).isEqualTo(10274L) - assertThat(results.otherResults[2]).isEqualTo(9481L) - assertThat(results.otherResults[3]).isEqualTo(10000.0) - assertThat(results.otherResults[4]).isEqualTo("CHF") - /** GBP */ - assertThat(results.otherResults[5]).isEqualTo(40000L) - assertThat(results.otherResults[6]).isEqualTo(10343L) - assertThat(results.otherResults[7]).isEqualTo(9351L) - assertThat(results.otherResults[8]).isEqualTo(10000.0) - assertThat(results.otherResults[9]).isEqualTo("GBP") - /** USD */ - assertThat(results.otherResults[10]).isEqualTo(60000L) - assertThat(results.otherResults[11]).isEqualTo(11298L) - assertThat(results.otherResults[12]).isEqualTo(8702L) - assertThat(results.otherResults[13]).isEqualTo(10000.0) - assertThat(results.otherResults[14]).isEqualTo("USD") + // the order of rows not guaranteed, a row has format 'NUM, NUM, NUM, CURRENCY_CODE' + val actualRows = mapOf(results.otherResults[4] as String to results.otherResults.subList(0,4), + results.otherResults[9] as String to results.otherResults.subList(5,9), + results.otherResults[14] as String to results.otherResults.subList(10,14)) + + val expectedRows = mapOf("CHF" to listOf(50000L, 10274L, 9481L, 10000.0), + "GBP" to listOf(40000L, 10343L, 9351L, 10000.0), + "USD" to listOf(60000L, 11298L, 8702L, 10000.0)) + + assertThat(expectedRows["CHF"]).isEqualTo(actualRows["CHF"]) + assertThat(expectedRows["GBP"]).isEqualTo(actualRows["GBP"]) + assertThat(expectedRows["USD"]).isEqualTo(actualRows["USD"]) } } @@ -1454,12 +1448,14 @@ class VaultQueryTests { val results = vaultService.queryBy>(criteria) assertThat(results.otherResults).hasSize(6) - assertThat(results.otherResults[0]).isEqualTo(110000L) - assertThat(results.otherResults[1]).isEqualTo("CHF") - assertThat(results.otherResults[2]).isEqualTo(70000L) - assertThat(results.otherResults[3]).isEqualTo("GBP") - assertThat(results.otherResults[4]).isEqualTo(30000L) - assertThat(results.otherResults[5]).isEqualTo("USD") + // the order of rows not guaranteed + val actualTotals = mapOf(results.otherResults[1] as String to results.otherResults[0] as Long, + results.otherResults[3] as String to results.otherResults[2] as Long, + results.otherResults[5] as String to results.otherResults[4] as Long) + val expectedTotals = mapOf("CHF" to 110000L, "GBP" to 70000L, "USD" to 30000L) + assertThat(expectedTotals["CHF"]).isEqualTo(actualTotals["CHF"]) + assertThat(expectedTotals["GBP"]).isEqualTo(actualTotals["GBP"]) + assertThat(expectedTotals["USD"]).isEqualTo(actualTotals["USD"]) } } diff --git a/testing/test-utils/src/main/kotlin/net/corda/testing/internal/vault/DummyDealStateSchemaV1.kt b/testing/test-utils/src/main/kotlin/net/corda/testing/internal/vault/DummyDealStateSchemaV1.kt index 9ed95d29b7..f562ec4e71 100644 --- a/testing/test-utils/src/main/kotlin/net/corda/testing/internal/vault/DummyDealStateSchemaV1.kt +++ b/testing/test-utils/src/main/kotlin/net/corda/testing/internal/vault/DummyDealStateSchemaV1.kt @@ -22,7 +22,7 @@ object DummyDealStateSchemaV1 : MappedSchema(schemaFamily = DummyDealStateSchema /** parent attributes */ @ElementCollection @Column(name = "participants") - @CollectionTable(name = "dummy_deal_states_participants", joinColumns = arrayOf( + @CollectionTable(name = "dummy_deal_states_parts", joinColumns = arrayOf( JoinColumn(name = "output_index", referencedColumnName = "output_index"), JoinColumn(name = "transaction_id", referencedColumnName = "transaction_id"))) override var participants: MutableSet? = null, diff --git a/testing/test-utils/src/main/kotlin/net/corda/testing/internal/vault/DummyLinearStateSchemaV2.kt b/testing/test-utils/src/main/kotlin/net/corda/testing/internal/vault/DummyLinearStateSchemaV2.kt index 6a25167c71..ea0feb4f63 100644 --- a/testing/test-utils/src/main/kotlin/net/corda/testing/internal/vault/DummyLinearStateSchemaV2.kt +++ b/testing/test-utils/src/main/kotlin/net/corda/testing/internal/vault/DummyLinearStateSchemaV2.kt @@ -18,7 +18,7 @@ object DummyLinearStateSchemaV2 : MappedSchema(schemaFamily = DummyLinearStateSc @ElementCollection @Column(name = "participants") - @CollectionTable(name = "dummy_linear_states_v2_participants", joinColumns = arrayOf( + @CollectionTable(name = "dummy_linear_states_v2_parts", joinColumns = arrayOf( JoinColumn(name = "output_index", referencedColumnName = "output_index"), JoinColumn(name = "transaction_id", referencedColumnName = "transaction_id"))) override var participants: MutableSet? = null, From d00438b86f6ead59321056d864ea7ad820487dab Mon Sep 17 00:00:00 2001 From: Andrzej Cichocki Date: Mon, 22 Jan 2018 11:00:40 +0000 Subject: [PATCH 12/36] Fix MockNetwork non-daemon thread leak. (#2397) --- .../node/MockNetworkIntegrationTests.kt | 26 +++++++++++++++++++ .../testing/node/InMemoryMessagingNetwork.kt | 1 + .../testing/node/internal/ProcessUtilities.kt | 5 ++-- 3 files changed, 30 insertions(+), 2 deletions(-) create mode 100644 testing/node-driver/src/integration-test/kotlin/net/corda/testing/node/MockNetworkIntegrationTests.kt diff --git a/testing/node-driver/src/integration-test/kotlin/net/corda/testing/node/MockNetworkIntegrationTests.kt b/testing/node-driver/src/integration-test/kotlin/net/corda/testing/node/MockNetworkIntegrationTests.kt new file mode 100644 index 0000000000..cf82aec871 --- /dev/null +++ b/testing/node-driver/src/integration-test/kotlin/net/corda/testing/node/MockNetworkIntegrationTests.kt @@ -0,0 +1,26 @@ +package net.corda.testing.node + +import net.corda.core.internal.div +import net.corda.testing.common.internal.ProjectStructure.projectRootDir +import net.corda.testing.node.internal.ProcessUtilities.startJavaProcess +import org.junit.Test +import kotlin.test.assertEquals + +class MockNetworkIntegrationTests { + companion object { + @JvmStatic + fun main(args: Array) { + MockNetwork(emptyList()).run { + repeat(2) { createNode() } + runNetwork() + stopNodes() + } + } + } + + @Test + fun `does not leak non-daemon threads`() { + val quasar = projectRootDir / "lib" / "quasar.jar" + assertEquals(0, startJavaProcess(emptyList(), extraJvmArguments = listOf("-javaagent:$quasar")).waitFor()) + } +} diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/InMemoryMessagingNetwork.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/InMemoryMessagingNetwork.kt index 3bfa44fe5f..fbfc3cf4d9 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/InMemoryMessagingNetwork.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/InMemoryMessagingNetwork.kt @@ -161,6 +161,7 @@ class InMemoryMessagingNetwork internal constructor( handleEndpointMap.clear() messageReceiveQueues.clear() + timer.cancel() } @CordaSerializable diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/ProcessUtilities.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/ProcessUtilities.kt index f8d4a01f98..8d2a8d5486 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/ProcessUtilities.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/ProcessUtilities.kt @@ -8,9 +8,10 @@ import java.nio.file.Path object ProcessUtilities { inline fun startJavaProcess( arguments: List, - jdwpPort: Int? = null + jdwpPort: Int? = null, + extraJvmArguments: List = emptyList() ): Process { - return startJavaProcessImpl(C::class.java.name, arguments, defaultClassPath, jdwpPort, emptyList(), null, null, null) + return startJavaProcessImpl(C::class.java.name, arguments, defaultClassPath, jdwpPort, extraJvmArguments, null, null, null) } fun startCordaProcess( From 1fc646cfa8df3b1f3fc76d7175176515f1361bfe Mon Sep 17 00:00:00 2001 From: Andrzej Cichocki Date: Mon, 22 Jan 2018 11:28:41 +0000 Subject: [PATCH 13/36] CORDA-716 Move test-utils and node-driver to stable API section in docs (#2335) * Move test-utils and node-driver to stable section. * Move FlowStackSnapshotFactory to testing.services package & update docs * Move SerializationTestHelpers to testing.core package * Move TestConstants.kt to core namespace * Move Expect.kt to core namespace * Move CoreTestUtils to core.TestUtils - rename class and update imports * Added some clarification to documents after re-reading them * Added static imports * Removing unused import * Fix merge conflict * Fixing merge conflict --- .../net/corda/client/jackson/JacksonSupportTest.kt | 5 ++++- .../net/corda/client/jfx/NodeMonitorModelTest.kt | 2 +- .../net/corda/client/rpc/CordaRPCJavaClientTest.java | 6 +++--- .../net/corda/client/rpc/BlacklistKotlinClosureTest.kt | 2 +- .../kotlin/net/corda/client/rpc/CordaRPCClientTest.kt | 2 +- .../kotlin/net/corda/client/rpc/RPCStabilityTests.kt | 2 +- .../kotlin/net/corda/client/rpc/AbstractRPCTest.kt | 2 +- .../kotlin/net/corda/client/rpc/RPCFailureTests.kt | 2 +- .../net/corda/confidential/IdentitySyncFlowTests.kt | 5 ++++- .../net/corda/confidential/SwapIdentitiesFlowTests.kt | 2 +- .../java/net/corda/core/flows/FlowsInJavaTest.java | 4 ++-- .../corda/core/flows/SerializationApiInJavaTest.java | 2 +- .../net/corda/core/contracts/ContractsDSLTests.kt | 2 +- .../net/corda/core/contracts/DummyContractV2Tests.kt | 5 ++++- .../kotlin/net/corda/core/crypto/CompositeKeyTests.kt | 2 +- .../net/corda/core/crypto/PartialMerkleTreeTest.kt | 5 ++++- .../kotlin/net/corda/core/crypto/SignedDataTest.kt | 2 +- .../net/corda/core/crypto/TransactionSignatureTest.kt | 2 +- .../kotlin/net/corda/core/flows/AttachmentTests.kt | 4 +++- .../net/corda/core/flows/CollectSignaturesFlowTests.kt | 2 +- .../net/corda/core/flows/ContractUpgradeFlowTest.kt | 6 +++--- .../kotlin/net/corda/core/flows/FinalityFlowTests.kt | 2 +- .../kotlin/net/corda/core/flows/ReceiveAllFlowTests.kt | 2 +- .../net/corda/core/identity/PartyAndCertificateTest.kt | 6 +++--- .../test/kotlin/net/corda/core/identity/PartyTest.kt | 2 +- .../net/corda/core/internal/AbstractAttachmentTest.kt | 4 ++-- .../corda/core/internal/ResolveTransactionsFlowTest.kt | 2 +- .../kotlin/net/corda/core/node/VaultUpdateTests.kt | 4 ++-- .../core/serialization/AttachmentSerializationTest.kt | 6 +++--- .../core/serialization/CommandsSerializationTests.kt | 2 +- .../serialization/TransactionSerializationTests.kt | 2 +- .../UniquenessExceptionSerializationTest.kt | 2 +- .../core/transactions/CompatibleTransactionTests.kt | 6 +++--- .../core/transactions/LedgerTransactionQueryTests.kt | 2 +- .../core/transactions/TransactionEncumbranceTests.kt | 4 +++- .../net/corda/core/transactions/TransactionTests.kt | 2 +- .../kotlin/net/corda/core/utilities/KotlinUtilsTest.kt | 2 +- .../kotlin/net/corda/core/utilities/NonEmptySetTest.kt | 2 +- docs/source/corda-api.rst | 8 ++++++-- .../net/corda/docs/IntegrationTestingTutorial.kt | 2 +- .../main/kotlin/net/corda/docs/ClientRpcTutorial.kt | 2 +- .../net/corda/docs/FxTransactionBuildTutorial.kt | 2 +- .../corda/docs/tutorial/contract/TutorialContract.kt | 2 +- .../java/tutorial/testdsl/CommercialPaperTest.java | 6 ++++-- .../test/kotlin/net/corda/docs/CustomVaultQueryTest.kt | 2 +- .../net/corda/docs/FxTransactionBuildTutorialTest.kt | 2 +- .../corda/docs/WorkflowTransactionBuildTutorialTest.kt | 3 ++- .../net/corda/docs/tutorial/testdsl/TutorialTestDSL.kt | 2 +- .../net/corda/finance/contracts/universal/Cap.kt | 4 +++- .../net/corda/finance/contracts/universal/Caplet.kt | 2 +- .../finance/contracts/universal/ContractDefinition.kt | 2 +- .../finance/contracts/universal/FXFwdTimeOption.kt | 2 +- .../net/corda/finance/contracts/universal/FXSwap.kt | 2 +- .../net/corda/finance/contracts/universal/IRS.kt | 2 +- .../corda/finance/contracts/universal/RollOutTests.kt | 2 +- .../net/corda/finance/contracts/universal/Swaption.kt | 2 +- .../finance/contracts/universal/ZeroCouponBond.kt | 2 +- .../corda/finance/contracts/asset/CashTestsJava.java | 8 ++++---- .../corda/finance/contracts/CommercialPaperTests.kt | 2 +- .../net/corda/finance/contracts/asset/CashTests.kt | 2 +- .../corda/finance/contracts/asset/ObligationTests.kt | 2 +- .../net/corda/finance/flows/CashExitFlowTests.kt | 2 +- .../net/corda/finance/flows/CashIssueFlowTests.kt | 2 +- .../net/corda/finance/flows/CashPaymentFlowTests.kt | 2 +- .../ForbiddenLambdaSerializationTests.java | 2 +- .../LambdaCheckpointSerializationTest.java | 2 +- .../AttachmentsClassLoaderStaticContractTests.kt | 4 +++- .../nodeapi/internal/AttachmentsClassLoaderTests.kt | 4 +++- .../net/corda/nodeapi/internal/SignedNodeInfoTest.kt | 6 +++--- .../corda/nodeapi/internal/crypto/X509UtilitiesTest.kt | 6 +++--- .../serialization/ContractAttachmentSerializerTest.kt | 2 +- .../corda/nodeapi/internal/serialization/KryoTests.kt | 6 +++--- .../internal/serialization/ListsSerializationTest.kt | 2 +- .../internal/serialization/MapsSerializationTest.kt | 2 +- .../serialization/PrivateKeySerializationTest.kt | 2 +- .../internal/serialization/SerializationTokenTest.kt | 2 +- .../internal/serialization/SetsSerializationTest.kt | 2 +- .../serialization/amqp/SerializationOutputTests.kt | 4 +++- .../kotlin/net/corda/node/AuthDBTests.kt | 2 +- .../kotlin/net/corda/node/BootTests.kt | 2 +- .../kotlin/net/corda/node/CordappScanningDriverTest.kt | 4 +++- .../kotlin/net/corda/node/NodeKeystoreCheckTest.kt | 5 +---- .../kotlin/net/corda/node/NodePerformanceTests.kt | 2 +- .../kotlin/net/corda/node/SSHServerTest.kt | 3 +-- .../kotlin/net/corda/node/amqp/AMQPBridgeTest.kt | 2 +- .../kotlin/net/corda/node/amqp/ProtonWrapperTests.kt | 2 +- .../net/corda/node/services/AttachmentLoadingTests.kt | 8 ++++---- .../net/corda/node/services/BFTNotaryServiceTests.kt | 4 ++-- .../net/corda/node/services/DistributedServiceTests.kt | 2 +- .../net/corda/node/services/RaftNotaryServiceTests.kt | 6 +++--- .../net/corda/node/services/network/NetworkMapTest.kt | 6 +++--- .../corda/node/services/network/NodeInfoWatcherTest.kt | 4 ++-- .../services/network/PersistentNetworkMapCacheTest.kt | 2 +- .../node/services/statemachine/FlowVersioningTest.kt | 5 +++-- .../services/statemachine/LargeTransactionsTest.kt | 2 +- .../utilities/registration/NodeRegistrationTest.kt | 6 +++--- .../net/corda/services/messaging/MQSecurityTest.kt | 6 +++--- .../net/corda/services/messaging/P2PMessagingTest.kt | 4 ++-- .../kotlin/net/corda/test/node/NodeStartAndStopTest.kt | 2 +- .../net/corda/test/node/NodeStatePersistenceTests.kt | 2 +- .../corda/node/services/vault/VaultQueryJavaTests.java | 10 +++++----- .../test/kotlin/net/corda/node/CordaRPCOpsImplTest.kt | 5 ++++- .../test/kotlin/net/corda/node/InteractiveShellTest.kt | 2 +- .../net/corda/node/internal/NetworkParametersTest.kt | 8 ++++---- .../net/corda/node/messaging/TwoPartyTradeFlowTests.kt | 2 +- .../net/corda/node/services/NotaryChangeTests.kt | 2 +- .../node/services/config/NodeConfigurationImplTest.kt | 2 +- .../corda/node/services/events/ScheduledFlowTests.kt | 5 ++++- .../services/identity/InMemoryIdentityServiceTests.kt | 2 +- .../identity/PersistentIdentityServiceTests.kt | 2 +- .../net/corda/node/services/keys/KMSUtilsTests.kt | 4 +++- .../node/services/messaging/ArtemisMessagingTests.kt | 2 +- .../corda/node/services/network/NetworkMapCacheTest.kt | 9 ++++----- .../node/services/network/NetworkMapClientTest.kt | 8 ++++---- .../node/services/network/NetworkMapUpdaterTest.kt | 4 ++-- .../services/persistence/DBCheckpointStorageTests.kt | 2 +- .../services/persistence/DBTransactionStorageTests.kt | 2 +- .../services/persistence/HibernateConfigurationTest.kt | 2 +- .../node/services/schema/HibernateObserverTests.kt | 2 +- .../node/services/statemachine/FlowFrameworkTests.kt | 2 +- .../transactions/DistributedImmutableMapTests.kt | 4 ++-- .../node/services/transactions/NotaryServiceTests.kt | 6 +++--- .../transactions/PersistentUniquenessProviderTests.kt | 4 +++- .../transactions/ValidatingNotaryServiceTests.kt | 4 +++- .../corda/node/services/vault/NodeVaultServiceTest.kt | 2 +- .../net/corda/node/services/vault/VaultQueryTests.kt | 2 +- .../node/services/vault/VaultSoftLockManagerTest.kt | 2 +- .../net/corda/node/services/vault/VaultWithCashTest.kt | 2 +- .../registration/NetworkRegistrationHelperTest.kt | 2 +- .../net/corda/attachmentdemo/AttachmentDemoTest.kt | 4 ++-- .../kotlin/net/corda/attachmentdemo/AttachmentDemo.kt | 4 ++-- .../src/main/kotlin/net/corda/attachmentdemo/Main.kt | 4 ++-- .../kotlin/net/corda/bank/BankOfCordaRPCClientTest.kt | 5 ++++- .../main/kotlin/net/corda/bank/BankOfCordaCordform.kt | 2 +- .../cordapp/src/test/kotlin/net/corda/irs/Main.kt | 4 ++-- .../kotlin/net/corda/irs/api/NodeInterestRatesTest.kt | 2 +- .../src/test/kotlin/net/corda/irs/contract/IRSTests.kt | 5 ++++- .../kotlin/net/corda/irs/IRSDemoTest.kt | 5 ++++- .../kotlin/net/corda/netmap/NetworkMapVisualiser.kt | 2 +- .../kotlin/net/corda/netmap/VisualiserViewModel.kt | 2 +- .../net/corda/netmap/simulation/IRSSimulation.kt | 2 +- .../kotlin/net/corda/netmap/simulation/Simulation.kt | 2 +- .../kotlin/net/corda/notarydemo/BFTNotaryCordform.kt | 4 ++-- .../net/corda/notarydemo/CustomNotaryCordform.kt | 4 +++- .../src/main/kotlin/net/corda/notarydemo/Notarise.kt | 2 +- .../kotlin/net/corda/notarydemo/RaftNotaryCordform.kt | 4 ++-- .../net/corda/notarydemo/SingleNotaryCordform.kt | 6 +++--- .../kotlin/net/corda/vega/SimmValuationTest.kt | 4 ++-- .../src/test/kotlin/net/corda/vega/Main.kt | 4 +++- .../kotlin/net/corda/traderdemo/TraderDemoTest.kt | 5 ++++- .../src/main/kotlin/net/corda/traderdemo/TraderDemo.kt | 4 ++-- .../src/test/kotlin/net/corda/traderdemo/Main.kt | 6 +++--- .../corda/traderdemo/TransactionGraphSearchTests.kt | 5 ++++- .../kotlin/net/corda/testing/driver/DriverTests.kt | 4 ++-- .../src/main/kotlin/net/corda/testing/driver/Driver.kt | 4 ++-- .../src/main/kotlin/net/corda/testing/node/MockNode.kt | 4 ++-- .../main/kotlin/net/corda/testing/node/MockServices.kt | 4 ++-- .../kotlin/net/corda/testing/node/NodeTestUtils.kt | 4 +++- .../net/corda/testing/node/internal/DriverDSLImpl.kt | 8 ++++---- .../net/corda/testing/node/internal/NodeBasedTest.kt | 4 ++-- .../net/corda/testing/node/internal/RPCDriver.kt | 2 +- .../main/kotlin/net/corda/testing/{ => core}/Expect.kt | 2 +- .../testing/{ => core}/SerializationTestHelpers.kt | 2 +- .../net/corda/testing/{ => core}/TestConstants.kt | 2 +- .../testing/{CoreTestUtils.kt => core/TestUtils.kt} | 7 ++----- .../src/main/kotlin/net/corda/testing/dsl/TestDSL.kt | 2 +- .../internal/InternalSerializationTestHelpers.kt | 2 +- .../net/corda/testing/internal/TestNodeInfoBuilder.kt | 2 +- .../net/corda/testing/internal/vault/VaultFiller.kt | 2 +- .../corda/testing/{ => services}/FlowStackSnapshot.kt | 2 +- ...node.services.statemachine.FlowStackSnapshotFactory | 2 +- .../test/kotlin/net/corda/testing/TestIdentityTests.kt | 3 +++ .../kotlin/net/corda/explorer/ExplorerSimulation.kt | 4 ++-- .../explorer/views/cordapps/cash/NewTransaction.kt | 2 +- .../main/kotlin/net/corda/loadtest/tests/NotaryTest.kt | 3 ++- .../kotlin/net/corda/verifier/VerifierTests.kt | 4 +++- .../kotlin/net/corda/webserver/WebserverDriverTests.kt | 2 +- 177 files changed, 327 insertions(+), 262 deletions(-) rename testing/test-utils/src/main/kotlin/net/corda/testing/{ => core}/Expect.kt (99%) rename testing/test-utils/src/main/kotlin/net/corda/testing/{ => core}/SerializationTestHelpers.kt (99%) rename testing/test-utils/src/main/kotlin/net/corda/testing/{ => core}/TestConstants.kt (98%) rename testing/test-utils/src/main/kotlin/net/corda/testing/{CoreTestUtils.kt => core/TestUtils.kt} (96%) rename testing/test-utils/src/main/kotlin/net/corda/testing/{ => services}/FlowStackSnapshot.kt (99%) diff --git a/client/jackson/src/test/kotlin/net/corda/client/jackson/JacksonSupportTest.kt b/client/jackson/src/test/kotlin/net/corda/client/jackson/JacksonSupportTest.kt index 65800b7a8f..41a41ed327 100644 --- a/client/jackson/src/test/kotlin/net/corda/client/jackson/JacksonSupportTest.kt +++ b/client/jackson/src/test/kotlin/net/corda/client/jackson/JacksonSupportTest.kt @@ -10,8 +10,11 @@ import net.corda.core.identity.CordaX500Name import net.corda.core.node.ServiceHub import net.corda.core.transactions.SignedTransaction import net.corda.finance.USD -import net.corda.testing.* import net.corda.testing.contracts.DummyContract +import net.corda.testing.core.ALICE_NAME +import net.corda.testing.core.DUMMY_NOTARY_NAME +import net.corda.testing.core.SerializationEnvironmentRule +import net.corda.testing.core.TestIdentity import net.corda.testing.internal.rigorousMock import org.junit.Before import org.junit.Rule diff --git a/client/jfx/src/integration-test/kotlin/net/corda/client/jfx/NodeMonitorModelTest.kt b/client/jfx/src/integration-test/kotlin/net/corda/client/jfx/NodeMonitorModelTest.kt index 860ab8d4d2..4050d10497 100644 --- a/client/jfx/src/integration-test/kotlin/net/corda/client/jfx/NodeMonitorModelTest.kt +++ b/client/jfx/src/integration-test/kotlin/net/corda/client/jfx/NodeMonitorModelTest.kt @@ -28,7 +28,7 @@ import net.corda.finance.flows.CashIssueFlow import net.corda.finance.flows.CashPaymentFlow import net.corda.node.services.Permissions.Companion.invokeRpc import net.corda.node.services.Permissions.Companion.startFlow -import net.corda.testing.* +import net.corda.testing.core.* import net.corda.testing.driver.driver import net.corda.testing.node.User import org.junit.Test diff --git a/client/rpc/src/integration-test/java/net/corda/client/rpc/CordaRPCJavaClientTest.java b/client/rpc/src/integration-test/java/net/corda/client/rpc/CordaRPCJavaClientTest.java index a1c93caac5..e313ac8d10 100644 --- a/client/rpc/src/integration-test/java/net/corda/client/rpc/CordaRPCJavaClientTest.java +++ b/client/rpc/src/integration-test/java/net/corda/client/rpc/CordaRPCJavaClientTest.java @@ -10,7 +10,7 @@ import net.corda.finance.flows.CashPaymentFlow; import net.corda.finance.schemas.CashSchemaV1; import net.corda.node.internal.Node; import net.corda.node.internal.StartedNode; -import net.corda.testing.CoreTestUtils; +import net.corda.testing.core.TestUtils; import net.corda.testing.node.User; import net.corda.testing.node.internal.NodeBasedTest; import org.junit.After; @@ -28,7 +28,7 @@ import static net.corda.finance.Currencies.DOLLARS; import static net.corda.finance.contracts.GetBalances.getCashBalance; import static net.corda.node.services.Permissions.invokeRpc; import static net.corda.node.services.Permissions.startFlow; -import static net.corda.testing.TestConstants.ALICE_NAME; +import static net.corda.testing.core.TestConstants.ALICE_NAME; public class CordaRPCJavaClientTest extends NodeBasedTest { public CordaRPCJavaClientTest() { @@ -76,7 +76,7 @@ public class CordaRPCJavaClientTest extends NodeBasedTest { FlowHandle flowHandle = rpcProxy.startFlowDynamic(CashIssueFlow.class, DOLLARS(123), OpaqueBytes.of((byte)0), - CoreTestUtils.chooseIdentity(node.getInfo())); + TestUtils.chooseIdentity(node.getInfo())); System.out.println("Started issuing cash, waiting on result"); flowHandle.getReturnValue().get(); diff --git a/client/rpc/src/integration-test/kotlin/net/corda/client/rpc/BlacklistKotlinClosureTest.kt b/client/rpc/src/integration-test/kotlin/net/corda/client/rpc/BlacklistKotlinClosureTest.kt index de89672195..f8c2e3fcc5 100644 --- a/client/rpc/src/integration-test/kotlin/net/corda/client/rpc/BlacklistKotlinClosureTest.kt +++ b/client/rpc/src/integration-test/kotlin/net/corda/client/rpc/BlacklistKotlinClosureTest.kt @@ -7,7 +7,7 @@ import net.corda.core.flows.StartableByRPC import net.corda.core.messaging.startFlow import net.corda.core.serialization.CordaSerializable import net.corda.core.utilities.getOrThrow -import net.corda.testing.ALICE_NAME +import net.corda.testing.core.ALICE_NAME import net.corda.testing.driver.driver import org.assertj.core.api.Assertions.assertThatExceptionOfType import org.junit.Test diff --git a/client/rpc/src/integration-test/kotlin/net/corda/client/rpc/CordaRPCClientTest.kt b/client/rpc/src/integration-test/kotlin/net/corda/client/rpc/CordaRPCClientTest.kt index e15b9dfc04..d3d3d4f3ca 100644 --- a/client/rpc/src/integration-test/kotlin/net/corda/client/rpc/CordaRPCClientTest.kt +++ b/client/rpc/src/integration-test/kotlin/net/corda/client/rpc/CordaRPCClientTest.kt @@ -20,7 +20,7 @@ import net.corda.node.internal.Node import net.corda.node.internal.StartedNode import net.corda.node.services.Permissions.Companion.invokeRpc import net.corda.node.services.Permissions.Companion.startFlow -import net.corda.testing.* +import net.corda.testing.core.* import net.corda.testing.node.User import net.corda.testing.node.internal.NodeBasedTest import org.apache.activemq.artemis.api.core.ActiveMQSecurityException diff --git a/client/rpc/src/integration-test/kotlin/net/corda/client/rpc/RPCStabilityTests.kt b/client/rpc/src/integration-test/kotlin/net/corda/client/rpc/RPCStabilityTests.kt index b6f0e5d0cf..45b5bd3d0b 100644 --- a/client/rpc/src/integration-test/kotlin/net/corda/client/rpc/RPCStabilityTests.kt +++ b/client/rpc/src/integration-test/kotlin/net/corda/client/rpc/RPCStabilityTests.kt @@ -12,7 +12,7 @@ import net.corda.core.serialization.serialize import net.corda.core.utilities.* import net.corda.node.services.messaging.RPCServerConfiguration import net.corda.nodeapi.RPCApi -import net.corda.testing.SerializationEnvironmentRule +import net.corda.testing.core.SerializationEnvironmentRule import net.corda.testing.internal.* import net.corda.testing.node.internal.* import org.apache.activemq.artemis.api.core.SimpleString diff --git a/client/rpc/src/test/kotlin/net/corda/client/rpc/AbstractRPCTest.kt b/client/rpc/src/test/kotlin/net/corda/client/rpc/AbstractRPCTest.kt index 4bcf3ec51f..9f7ddefa0e 100644 --- a/client/rpc/src/test/kotlin/net/corda/client/rpc/AbstractRPCTest.kt +++ b/client/rpc/src/test/kotlin/net/corda/client/rpc/AbstractRPCTest.kt @@ -5,7 +5,7 @@ import net.corda.core.internal.concurrent.flatMap import net.corda.core.internal.concurrent.map import net.corda.core.messaging.RPCOps import net.corda.node.services.messaging.RPCServerConfiguration -import net.corda.testing.SerializationEnvironmentRule +import net.corda.testing.core.SerializationEnvironmentRule import net.corda.testing.node.User import net.corda.testing.node.internal.RPCDriverDSL import net.corda.testing.node.internal.rpcTestUser diff --git a/client/rpc/src/test/kotlin/net/corda/client/rpc/RPCFailureTests.kt b/client/rpc/src/test/kotlin/net/corda/client/rpc/RPCFailureTests.kt index 96ba3ece6b..398e2dc261 100644 --- a/client/rpc/src/test/kotlin/net/corda/client/rpc/RPCFailureTests.kt +++ b/client/rpc/src/test/kotlin/net/corda/client/rpc/RPCFailureTests.kt @@ -5,7 +5,7 @@ import net.corda.core.concurrent.CordaFuture import net.corda.core.internal.concurrent.openFuture import net.corda.core.messaging.* import net.corda.core.utilities.getOrThrow -import net.corda.testing.SerializationEnvironmentRule +import net.corda.testing.core.SerializationEnvironmentRule import net.corda.testing.node.internal.rpcDriver import net.corda.testing.node.internal.startRpcClient import org.assertj.core.api.Assertions.assertThatThrownBy diff --git a/confidential-identities/src/test/kotlin/net/corda/confidential/IdentitySyncFlowTests.kt b/confidential-identities/src/test/kotlin/net/corda/confidential/IdentitySyncFlowTests.kt index 585cb8b6bf..11c8e45532 100644 --- a/confidential-identities/src/test/kotlin/net/corda/confidential/IdentitySyncFlowTests.kt +++ b/confidential-identities/src/test/kotlin/net/corda/confidential/IdentitySyncFlowTests.kt @@ -14,7 +14,10 @@ import net.corda.finance.DOLLARS import net.corda.finance.contracts.asset.Cash import net.corda.finance.flows.CashIssueAndPaymentFlow import net.corda.finance.flows.CashPaymentFlow -import net.corda.testing.* +import net.corda.testing.core.ALICE_NAME +import net.corda.testing.core.BOB_NAME +import net.corda.testing.core.CHARLIE_NAME +import net.corda.testing.core.singleIdentity import net.corda.testing.node.MockNetwork import net.corda.testing.node.startFlow import org.junit.After diff --git a/confidential-identities/src/test/kotlin/net/corda/confidential/SwapIdentitiesFlowTests.kt b/confidential-identities/src/test/kotlin/net/corda/confidential/SwapIdentitiesFlowTests.kt index 7d72ec56da..f1e2efe9ae 100644 --- a/confidential-identities/src/test/kotlin/net/corda/confidential/SwapIdentitiesFlowTests.kt +++ b/confidential-identities/src/test/kotlin/net/corda/confidential/SwapIdentitiesFlowTests.kt @@ -2,7 +2,7 @@ package net.corda.confidential import net.corda.core.identity.* import net.corda.core.utilities.getOrThrow -import net.corda.testing.* +import net.corda.testing.core.* import net.corda.testing.node.MockNetwork import org.junit.Before import net.corda.testing.node.startFlow diff --git a/core/src/test/java/net/corda/core/flows/FlowsInJavaTest.java b/core/src/test/java/net/corda/core/flows/FlowsInJavaTest.java index ae6431bec2..848b74aed7 100644 --- a/core/src/test/java/net/corda/core/flows/FlowsInJavaTest.java +++ b/core/src/test/java/net/corda/core/flows/FlowsInJavaTest.java @@ -4,7 +4,7 @@ import co.paralleluniverse.fibers.Suspendable; import com.google.common.primitives.Primitives; import net.corda.core.identity.Party; import net.corda.node.internal.StartedNode; -import net.corda.testing.TestConstants; +import net.corda.testing.core.TestConstants; import net.corda.testing.node.MockNetwork; import org.junit.After; import org.junit.Before; @@ -14,7 +14,7 @@ import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import static java.util.Collections.emptyList; -import static net.corda.testing.CoreTestUtils.singleIdentity; +import static net.corda.testing.core.TestUtils.singleIdentity; import static org.assertj.core.api.AssertionsForClassTypes.assertThat; import static org.junit.Assert.fail; import static net.corda.testing.node.NodeTestUtils.startFlow; diff --git a/core/src/test/java/net/corda/core/flows/SerializationApiInJavaTest.java b/core/src/test/java/net/corda/core/flows/SerializationApiInJavaTest.java index 07bec4bb79..297adeff8f 100644 --- a/core/src/test/java/net/corda/core/flows/SerializationApiInJavaTest.java +++ b/core/src/test/java/net/corda/core/flows/SerializationApiInJavaTest.java @@ -2,7 +2,7 @@ package net.corda.core.flows; import net.corda.core.serialization.SerializationDefaults; import net.corda.core.serialization.SerializationFactory; -import net.corda.testing.SerializationEnvironmentRule; +import net.corda.testing.core.SerializationEnvironmentRule; import org.junit.Rule; import org.junit.Test; diff --git a/core/src/test/kotlin/net/corda/core/contracts/ContractsDSLTests.kt b/core/src/test/kotlin/net/corda/core/contracts/ContractsDSLTests.kt index c4e75a3b05..bfb54e5e78 100644 --- a/core/src/test/kotlin/net/corda/core/contracts/ContractsDSLTests.kt +++ b/core/src/test/kotlin/net/corda/core/contracts/ContractsDSLTests.kt @@ -3,7 +3,7 @@ package net.corda.core.contracts import net.corda.core.identity.AbstractParty import net.corda.core.identity.CordaX500Name import net.corda.core.identity.Party -import net.corda.testing.TestIdentity +import net.corda.testing.core.TestIdentity import org.assertj.core.api.Assertions import org.junit.Test import org.junit.runner.RunWith diff --git a/core/src/test/kotlin/net/corda/core/contracts/DummyContractV2Tests.kt b/core/src/test/kotlin/net/corda/core/contracts/DummyContractV2Tests.kt index f7f1abf245..be483b9e0d 100644 --- a/core/src/test/kotlin/net/corda/core/contracts/DummyContractV2Tests.kt +++ b/core/src/test/kotlin/net/corda/core/contracts/DummyContractV2Tests.kt @@ -6,9 +6,12 @@ import net.corda.core.cordapp.CordappProvider import net.corda.core.crypto.SecureHash import net.corda.core.internal.UpgradeCommand import net.corda.core.node.ServicesForResolution -import net.corda.testing.* import net.corda.testing.contracts.DummyContract import net.corda.testing.contracts.DummyContractV2 +import net.corda.testing.core.ALICE_NAME +import net.corda.testing.core.DUMMY_NOTARY_NAME +import net.corda.testing.core.SerializationEnvironmentRule +import net.corda.testing.core.TestIdentity import net.corda.testing.internal.rigorousMock import org.junit.Rule import org.junit.Test diff --git a/core/src/test/kotlin/net/corda/core/crypto/CompositeKeyTests.kt b/core/src/test/kotlin/net/corda/core/crypto/CompositeKeyTests.kt index 61d0c3d2f6..9e749e3451 100644 --- a/core/src/test/kotlin/net/corda/core/crypto/CompositeKeyTests.kt +++ b/core/src/test/kotlin/net/corda/core/crypto/CompositeKeyTests.kt @@ -7,7 +7,7 @@ import net.corda.core.serialization.serialize import net.corda.core.utilities.OpaqueBytes import net.corda.core.utilities.toBase58String import net.corda.nodeapi.internal.crypto.* -import net.corda.testing.SerializationEnvironmentRule +import net.corda.testing.core.SerializationEnvironmentRule import net.corda.testing.internal.kryoSpecific import org.junit.Rule import org.junit.Test diff --git a/core/src/test/kotlin/net/corda/core/crypto/PartialMerkleTreeTest.kt b/core/src/test/kotlin/net/corda/core/crypto/PartialMerkleTreeTest.kt index 18ecbcfd24..725dd34c2b 100644 --- a/core/src/test/kotlin/net/corda/core/crypto/PartialMerkleTreeTest.kt +++ b/core/src/test/kotlin/net/corda/core/crypto/PartialMerkleTreeTest.kt @@ -13,7 +13,10 @@ import net.corda.finance.DOLLARS import net.corda.finance.`issued by` import net.corda.finance.contracts.asset.Cash import net.corda.node.services.api.IdentityServiceInternal -import net.corda.testing.* +import net.corda.testing.core.DUMMY_NOTARY_NAME +import net.corda.testing.core.SerializationEnvironmentRule +import net.corda.testing.core.TEST_TX_TIME +import net.corda.testing.core.TestIdentity import net.corda.testing.dsl.LedgerDSL import net.corda.testing.dsl.TestLedgerDSLInterpreter import net.corda.testing.dsl.TestTransactionDSLInterpreter diff --git a/core/src/test/kotlin/net/corda/core/crypto/SignedDataTest.kt b/core/src/test/kotlin/net/corda/core/crypto/SignedDataTest.kt index 77ce4e0ba8..b7310756d4 100644 --- a/core/src/test/kotlin/net/corda/core/crypto/SignedDataTest.kt +++ b/core/src/test/kotlin/net/corda/core/crypto/SignedDataTest.kt @@ -2,7 +2,7 @@ package net.corda.core.crypto import net.corda.core.serialization.SerializedBytes import net.corda.core.serialization.serialize -import net.corda.testing.SerializationEnvironmentRule +import net.corda.testing.core.SerializationEnvironmentRule import org.junit.Before import org.junit.Rule import org.junit.Test diff --git a/core/src/test/kotlin/net/corda/core/crypto/TransactionSignatureTest.kt b/core/src/test/kotlin/net/corda/core/crypto/TransactionSignatureTest.kt index 5f35ff8de4..dbb0027a7c 100644 --- a/core/src/test/kotlin/net/corda/core/crypto/TransactionSignatureTest.kt +++ b/core/src/test/kotlin/net/corda/core/crypto/TransactionSignatureTest.kt @@ -1,6 +1,6 @@ package net.corda.core.crypto -import net.corda.testing.SerializationEnvironmentRule +import net.corda.testing.core.SerializationEnvironmentRule import org.junit.Rule import org.junit.Test import java.security.SignatureException diff --git a/core/src/test/kotlin/net/corda/core/flows/AttachmentTests.kt b/core/src/test/kotlin/net/corda/core/flows/AttachmentTests.kt index 527fd33a49..6de955e66d 100644 --- a/core/src/test/kotlin/net/corda/core/flows/AttachmentTests.kt +++ b/core/src/test/kotlin/net/corda/core/flows/AttachmentTests.kt @@ -10,7 +10,9 @@ import net.corda.core.internal.FetchDataFlow import net.corda.core.utilities.getOrThrow import net.corda.node.internal.StartedNode import net.corda.node.services.persistence.NodeAttachmentService -import net.corda.testing.* +import net.corda.testing.core.ALICE_NAME +import net.corda.testing.core.BOB_NAME +import net.corda.testing.core.singleIdentity import net.corda.testing.node.MockNetwork import net.corda.testing.node.MockNodeParameters import net.corda.testing.node.startFlow diff --git a/core/src/test/kotlin/net/corda/core/flows/CollectSignaturesFlowTests.kt b/core/src/test/kotlin/net/corda/core/flows/CollectSignaturesFlowTests.kt index 6da0ce7f18..353c20034f 100644 --- a/core/src/test/kotlin/net/corda/core/flows/CollectSignaturesFlowTests.kt +++ b/core/src/test/kotlin/net/corda/core/flows/CollectSignaturesFlowTests.kt @@ -12,8 +12,8 @@ import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.TransactionBuilder import net.corda.core.utilities.getOrThrow import net.corda.node.internal.StartedNode -import net.corda.testing.* import net.corda.testing.contracts.DummyContract +import net.corda.testing.core.* import net.corda.testing.internal.rigorousMock import net.corda.testing.node.MockNetwork import net.corda.testing.node.MockServices diff --git a/core/src/test/kotlin/net/corda/core/flows/ContractUpgradeFlowTest.kt b/core/src/test/kotlin/net/corda/core/flows/ContractUpgradeFlowTest.kt index 818c58ebf2..aae132071a 100644 --- a/core/src/test/kotlin/net/corda/core/flows/ContractUpgradeFlowTest.kt +++ b/core/src/test/kotlin/net/corda/core/flows/ContractUpgradeFlowTest.kt @@ -19,8 +19,8 @@ import net.corda.finance.flows.CashIssueFlow import net.corda.node.internal.SecureCordaRPCOps import net.corda.node.internal.StartedNode import net.corda.node.services.Permissions.Companion.startFlow -import net.corda.testing.ALICE_NAME -import net.corda.testing.BOB_NAME +import net.corda.testing.core.ALICE_NAME +import net.corda.testing.core.BOB_NAME import net.corda.testing.node.User import net.corda.testing.contracts.DummyContract import net.corda.testing.contracts.DummyContractV2 @@ -29,7 +29,7 @@ import net.corda.testing.node.internal.rpcDriver import net.corda.testing.node.internal.rpcTestUser import net.corda.testing.node.internal.startRpcClient import net.corda.testing.node.MockNetwork -import net.corda.testing.singleIdentity +import net.corda.testing.core.singleIdentity import net.corda.testing.node.startFlow import org.junit.After import org.junit.Before diff --git a/core/src/test/kotlin/net/corda/core/flows/FinalityFlowTests.kt b/core/src/test/kotlin/net/corda/core/flows/FinalityFlowTests.kt index bd4be11c16..0263c3f234 100644 --- a/core/src/test/kotlin/net/corda/core/flows/FinalityFlowTests.kt +++ b/core/src/test/kotlin/net/corda/core/flows/FinalityFlowTests.kt @@ -7,7 +7,7 @@ import net.corda.finance.POUNDS import net.corda.finance.contracts.asset.Cash import net.corda.finance.issuedBy import net.corda.node.services.api.StartedNodeServices -import net.corda.testing.* +import net.corda.testing.core.* import net.corda.testing.node.MockNetwork import net.corda.testing.node.startFlow import org.junit.After diff --git a/core/src/test/kotlin/net/corda/core/flows/ReceiveAllFlowTests.kt b/core/src/test/kotlin/net/corda/core/flows/ReceiveAllFlowTests.kt index 237474c902..6dccd55c9f 100644 --- a/core/src/test/kotlin/net/corda/core/flows/ReceiveAllFlowTests.kt +++ b/core/src/test/kotlin/net/corda/core/flows/ReceiveAllFlowTests.kt @@ -6,7 +6,7 @@ import net.corda.core.utilities.UntrustworthyData import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.unwrap import net.corda.testing.node.MockNetwork -import net.corda.testing.singleIdentity +import net.corda.testing.core.singleIdentity import net.corda.testing.node.startFlow import org.assertj.core.api.Assertions.assertThat import org.junit.After diff --git a/core/src/test/kotlin/net/corda/core/identity/PartyAndCertificateTest.kt b/core/src/test/kotlin/net/corda/core/identity/PartyAndCertificateTest.kt index ff8207db8d..faca71eeba 100644 --- a/core/src/test/kotlin/net/corda/core/identity/PartyAndCertificateTest.kt +++ b/core/src/test/kotlin/net/corda/core/identity/PartyAndCertificateTest.kt @@ -7,9 +7,9 @@ import net.corda.core.serialization.serialize import net.corda.nodeapi.internal.crypto.KEYSTORE_TYPE import net.corda.nodeapi.internal.crypto.X509CertificateFactory import net.corda.nodeapi.internal.crypto.save -import net.corda.testing.DEV_ROOT_CA -import net.corda.testing.SerializationEnvironmentRule -import net.corda.testing.getTestPartyAndCertificate +import net.corda.testing.core.DEV_ROOT_CA +import net.corda.testing.core.SerializationEnvironmentRule +import net.corda.testing.core.getTestPartyAndCertificate import org.assertj.core.api.Assertions.assertThat import org.junit.Rule import org.junit.Test diff --git a/core/src/test/kotlin/net/corda/core/identity/PartyTest.kt b/core/src/test/kotlin/net/corda/core/identity/PartyTest.kt index 28d6fb1fa3..ff80c8b33b 100644 --- a/core/src/test/kotlin/net/corda/core/identity/PartyTest.kt +++ b/core/src/test/kotlin/net/corda/core/identity/PartyTest.kt @@ -1,7 +1,7 @@ package net.corda.core.identity import net.corda.core.crypto.entropyToKeyPair -import net.corda.testing.ALICE_NAME +import net.corda.testing.core.ALICE_NAME import org.junit.Test import java.math.BigInteger import kotlin.test.assertEquals diff --git a/core/src/test/kotlin/net/corda/core/internal/AbstractAttachmentTest.kt b/core/src/test/kotlin/net/corda/core/internal/AbstractAttachmentTest.kt index 955ff7d2b2..71b7fa8d37 100644 --- a/core/src/test/kotlin/net/corda/core/internal/AbstractAttachmentTest.kt +++ b/core/src/test/kotlin/net/corda/core/internal/AbstractAttachmentTest.kt @@ -1,7 +1,7 @@ package net.corda.core.internal -import net.corda.testing.ALICE_NAME -import net.corda.testing.BOB_NAME +import net.corda.testing.core.ALICE_NAME +import net.corda.testing.core.BOB_NAME import org.assertj.core.api.Assertions.assertThatThrownBy import org.junit.After import org.junit.AfterClass diff --git a/core/src/test/kotlin/net/corda/core/internal/ResolveTransactionsFlowTest.kt b/core/src/test/kotlin/net/corda/core/internal/ResolveTransactionsFlowTest.kt index 5121a9f6e5..b61374a40a 100644 --- a/core/src/test/kotlin/net/corda/core/internal/ResolveTransactionsFlowTest.kt +++ b/core/src/test/kotlin/net/corda/core/internal/ResolveTransactionsFlowTest.kt @@ -11,7 +11,7 @@ import net.corda.core.utilities.sequence import net.corda.node.internal.StartedNode import net.corda.testing.contracts.DummyContract import net.corda.testing.node.MockNetwork -import net.corda.testing.singleIdentity +import net.corda.testing.core.singleIdentity import net.corda.testing.node.startFlow import org.junit.After import org.junit.Before diff --git a/core/src/test/kotlin/net/corda/core/node/VaultUpdateTests.kt b/core/src/test/kotlin/net/corda/core/node/VaultUpdateTests.kt index dd4c993837..3805a703bd 100644 --- a/core/src/test/kotlin/net/corda/core/node/VaultUpdateTests.kt +++ b/core/src/test/kotlin/net/corda/core/node/VaultUpdateTests.kt @@ -5,8 +5,8 @@ import net.corda.core.crypto.SecureHash import net.corda.core.identity.AbstractParty import net.corda.core.node.services.Vault import net.corda.core.transactions.LedgerTransaction -import net.corda.testing.DUMMY_NOTARY_NAME -import net.corda.testing.TestIdentity +import net.corda.testing.core.DUMMY_NOTARY_NAME +import net.corda.testing.core.TestIdentity import org.junit.Test import kotlin.test.assertEquals import kotlin.test.assertFailsWith diff --git a/core/src/test/kotlin/net/corda/core/serialization/AttachmentSerializationTest.kt b/core/src/test/kotlin/net/corda/core/serialization/AttachmentSerializationTest.kt index d72d7068d4..81b70a6c2b 100644 --- a/core/src/test/kotlin/net/corda/core/serialization/AttachmentSerializationTest.kt +++ b/core/src/test/kotlin/net/corda/core/serialization/AttachmentSerializationTest.kt @@ -16,11 +16,11 @@ import net.corda.node.internal.InitiatedFlowFactory import net.corda.node.internal.StartedNode import net.corda.node.services.persistence.NodeAttachmentService import net.corda.nodeapi.internal.persistence.currentDBSession -import net.corda.testing.ALICE_NAME -import net.corda.testing.BOB_NAME +import net.corda.testing.core.ALICE_NAME +import net.corda.testing.core.BOB_NAME import net.corda.testing.node.MockNetwork import net.corda.testing.node.MockNodeParameters -import net.corda.testing.singleIdentity +import net.corda.testing.core.singleIdentity import net.corda.testing.node.startFlow import org.junit.After import org.junit.Before diff --git a/core/src/test/kotlin/net/corda/core/serialization/CommandsSerializationTests.kt b/core/src/test/kotlin/net/corda/core/serialization/CommandsSerializationTests.kt index 836157e2c3..64c30bdf8c 100644 --- a/core/src/test/kotlin/net/corda/core/serialization/CommandsSerializationTests.kt +++ b/core/src/test/kotlin/net/corda/core/serialization/CommandsSerializationTests.kt @@ -2,7 +2,7 @@ package net.corda.core.serialization import net.corda.finance.contracts.CommercialPaper import net.corda.finance.contracts.asset.Cash -import net.corda.testing.SerializationEnvironmentRule +import net.corda.testing.core.SerializationEnvironmentRule import org.junit.Rule import org.junit.Test import kotlin.test.assertEquals diff --git a/core/src/test/kotlin/net/corda/core/serialization/TransactionSerializationTests.kt b/core/src/test/kotlin/net/corda/core/serialization/TransactionSerializationTests.kt index 8f70d9610d..1d326554e3 100644 --- a/core/src/test/kotlin/net/corda/core/serialization/TransactionSerializationTests.kt +++ b/core/src/test/kotlin/net/corda/core/serialization/TransactionSerializationTests.kt @@ -8,7 +8,7 @@ import net.corda.core.transactions.LedgerTransaction import net.corda.core.transactions.TransactionBuilder import net.corda.core.utilities.seconds import net.corda.finance.POUNDS -import net.corda.testing.* +import net.corda.testing.core.* import net.corda.testing.internal.rigorousMock import net.corda.testing.node.MockServices import org.junit.Before diff --git a/core/src/test/kotlin/net/corda/core/serialization/UniquenessExceptionSerializationTest.kt b/core/src/test/kotlin/net/corda/core/serialization/UniquenessExceptionSerializationTest.kt index d0da8a2bcb..f1ac6956a3 100644 --- a/core/src/test/kotlin/net/corda/core/serialization/UniquenessExceptionSerializationTest.kt +++ b/core/src/test/kotlin/net/corda/core/serialization/UniquenessExceptionSerializationTest.kt @@ -7,7 +7,7 @@ import net.corda.core.identity.CordaX500Name import net.corda.core.identity.Party import net.corda.core.node.services.UniquenessException import net.corda.core.node.services.UniquenessProvider -import net.corda.testing.SerializationEnvironmentRule +import net.corda.testing.core.SerializationEnvironmentRule import org.junit.Rule import org.junit.Test import kotlin.test.assertEquals diff --git a/core/src/test/kotlin/net/corda/core/transactions/CompatibleTransactionTests.kt b/core/src/test/kotlin/net/corda/core/transactions/CompatibleTransactionTests.kt index e83b05e8f3..2cb630e01b 100644 --- a/core/src/test/kotlin/net/corda/core/transactions/CompatibleTransactionTests.kt +++ b/core/src/test/kotlin/net/corda/core/transactions/CompatibleTransactionTests.kt @@ -5,9 +5,9 @@ import net.corda.core.contracts.ComponentGroupEnum.* import net.corda.core.crypto.* import net.corda.core.serialization.serialize import net.corda.core.utilities.OpaqueBytes -import net.corda.testing.* import net.corda.testing.contracts.DummyContract import net.corda.testing.contracts.DummyState +import net.corda.testing.core.* import org.junit.Rule import org.junit.Test import java.time.Instant @@ -287,7 +287,7 @@ class CompatibleTransactionTests { @Test fun `Command visibility tests`() { // 1st and 3rd commands require a signature from KEY_1. - val twoCommandsforKey1 = listOf(dummyCommand(DUMMY_KEY_1.public, DUMMY_KEY_2.public), dummyCommand(DUMMY_KEY_2.public), dummyCommand(DUMMY_KEY_1.public)) + val twoCommandsforKey1 = listOf(dummyCommand(DUMMY_KEY_1.public, DUMMY_KEY_2.public), dummyCommand(DUMMY_KEY_2.public), dummyCommand(DUMMY_KEY_1.public)) val componentGroups = listOf( inputGroup, outputGroup, @@ -409,7 +409,7 @@ class CompatibleTransactionTests { val ftxConstructor = ::FilteredTransaction // 1st and 3rd commands require a signature from KEY_1. - val twoCommandsforKey1 = listOf(dummyCommand(DUMMY_KEY_1.public, DUMMY_KEY_2.public), dummyCommand(DUMMY_KEY_2.public), dummyCommand(DUMMY_KEY_1.public)) + val twoCommandsforKey1 = listOf(dummyCommand(DUMMY_KEY_1.public, DUMMY_KEY_2.public), dummyCommand(DUMMY_KEY_2.public), dummyCommand(DUMMY_KEY_1.public)) val componentGroups = listOf( inputGroup, outputGroup, diff --git a/core/src/test/kotlin/net/corda/core/transactions/LedgerTransactionQueryTests.kt b/core/src/test/kotlin/net/corda/core/transactions/LedgerTransactionQueryTests.kt index fe7e977962..973752a197 100644 --- a/core/src/test/kotlin/net/corda/core/transactions/LedgerTransactionQueryTests.kt +++ b/core/src/test/kotlin/net/corda/core/transactions/LedgerTransactionQueryTests.kt @@ -8,8 +8,8 @@ import net.corda.core.identity.AbstractParty import net.corda.core.identity.CordaX500Name import net.corda.core.identity.Party import net.corda.node.services.api.IdentityServiceInternal -import net.corda.testing.* import net.corda.testing.contracts.DummyContract +import net.corda.testing.core.* import net.corda.testing.internal.rigorousMock import net.corda.testing.node.MockServices import org.junit.Before diff --git a/core/src/test/kotlin/net/corda/core/transactions/TransactionEncumbranceTests.kt b/core/src/test/kotlin/net/corda/core/transactions/TransactionEncumbranceTests.kt index 3e923c5a7d..1a3878b586 100644 --- a/core/src/test/kotlin/net/corda/core/transactions/TransactionEncumbranceTests.kt +++ b/core/src/test/kotlin/net/corda/core/transactions/TransactionEncumbranceTests.kt @@ -11,7 +11,9 @@ import net.corda.finance.DOLLARS import net.corda.finance.`issued by` import net.corda.finance.contracts.asset.Cash import net.corda.node.services.api.IdentityServiceInternal -import net.corda.testing.* +import net.corda.testing.core.DUMMY_NOTARY_NAME +import net.corda.testing.core.SerializationEnvironmentRule +import net.corda.testing.core.TestIdentity import net.corda.testing.internal.rigorousMock import net.corda.testing.node.MockServices import net.corda.testing.node.ledger diff --git a/core/src/test/kotlin/net/corda/core/transactions/TransactionTests.kt b/core/src/test/kotlin/net/corda/core/transactions/TransactionTests.kt index 8191dce1ae..cd234f1138 100644 --- a/core/src/test/kotlin/net/corda/core/transactions/TransactionTests.kt +++ b/core/src/test/kotlin/net/corda/core/transactions/TransactionTests.kt @@ -4,8 +4,8 @@ import net.corda.core.contracts.* import net.corda.core.crypto.* import net.corda.core.crypto.CompositeKey import net.corda.core.identity.Party -import net.corda.testing.* import net.corda.testing.contracts.DummyContract +import net.corda.testing.core.* import net.corda.testing.internal.rigorousMock import org.junit.Rule import org.junit.Test diff --git a/core/src/test/kotlin/net/corda/core/utilities/KotlinUtilsTest.kt b/core/src/test/kotlin/net/corda/core/utilities/KotlinUtilsTest.kt index ec5f5c8fc1..89c4ee9092 100644 --- a/core/src/test/kotlin/net/corda/core/utilities/KotlinUtilsTest.kt +++ b/core/src/test/kotlin/net/corda/core/utilities/KotlinUtilsTest.kt @@ -7,7 +7,7 @@ import net.corda.core.serialization.deserialize import net.corda.core.serialization.serialize import net.corda.nodeapi.internal.serialization.KRYO_CHECKPOINT_CONTEXT import net.corda.nodeapi.internal.serialization.KRYO_P2P_CONTEXT -import net.corda.testing.SerializationEnvironmentRule +import net.corda.testing.core.SerializationEnvironmentRule import org.assertj.core.api.Assertions.assertThat import org.junit.Rule import org.junit.Test diff --git a/core/src/test/kotlin/net/corda/core/utilities/NonEmptySetTest.kt b/core/src/test/kotlin/net/corda/core/utilities/NonEmptySetTest.kt index 0a26639ae4..b404ded1fc 100644 --- a/core/src/test/kotlin/net/corda/core/utilities/NonEmptySetTest.kt +++ b/core/src/test/kotlin/net/corda/core/utilities/NonEmptySetTest.kt @@ -7,7 +7,7 @@ import com.google.common.collect.testing.features.CollectionSize import junit.framework.TestSuite import net.corda.core.serialization.deserialize import net.corda.core.serialization.serialize -import net.corda.testing.SerializationEnvironmentRule +import net.corda.testing.core.SerializationEnvironmentRule import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThatThrownBy import org.junit.Rule diff --git a/docs/source/corda-api.rst b/docs/source/corda-api.rst index 21040ea968..aa55bfefcf 100644 --- a/docs/source/corda-api.rst +++ b/docs/source/corda-api.rst @@ -47,6 +47,12 @@ The following modules have a stable API we commit not to break in following rele * **Core (net.corda.core)**: core Corda libraries such as crypto functions, types for Corda's building blocks: states, contracts, transactions, attachments, etc. and some interfaces for nodes and protocols * **Client RPC (net.corda.client.rpc)**: client RPC * **Client Jackson (net.corda.client.jackson)**: JSON support for client applications +* **Test Utils (net.corda.testing.core)**: generic test utilities +* **Test Node Driver (net.corda.testing.node, net.corda.testing.driver)**: test utilities to run nodes programmatically +* **Http Test Utils (net.corda.testing.http)**: a small set of utilities for making HttpCalls, aimed at demos and tests. +* **DSL Test Utils (net.corda.testing.dsl)**: a simple DSL for building pseudo-transactions (not the same as the wire protocol) for testing purposes. +* **Dummy Contracts (net.corda.testing.contracts)**: dummy state and contracts for testing purposes +* **Mock Services (net.corda.testing.services)**: mock service implementations for testing purposes Corda incubating modules ------------------------ @@ -54,9 +60,7 @@ Corda incubating modules The following modules don't yet have a completely stable API, but we will do our best to minimise disruption to developers using them until we are able to graduate them into the public API: -* **net.corda.node.driver**: test utilities to run nodes programmatically * **net.corda.confidential.identities**: experimental support for confidential identities on the ledger -* **net.corda.node.test.utils**: generic test utilities * **net.corda.finance**: a range of elementary contracts (and associated schemas) and protocols, such as abstract fungible assets, cash, obligation and commercial paper * **net.corda.client.jfx**: support for Java FX UI * **net.corda.client.mock**: client mock utilities diff --git a/docs/source/example-code/src/integration-test/kotlin/net/corda/docs/IntegrationTestingTutorial.kt b/docs/source/example-code/src/integration-test/kotlin/net/corda/docs/IntegrationTestingTutorial.kt index 47f6b74ea1..99eb5df37f 100644 --- a/docs/source/example-code/src/integration-test/kotlin/net/corda/docs/IntegrationTestingTutorial.kt +++ b/docs/source/example-code/src/integration-test/kotlin/net/corda/docs/IntegrationTestingTutorial.kt @@ -13,7 +13,7 @@ import net.corda.finance.flows.CashIssueFlow import net.corda.finance.flows.CashPaymentFlow import net.corda.node.services.Permissions.Companion.invokeRpc import net.corda.node.services.Permissions.Companion.startFlow -import net.corda.testing.* +import net.corda.testing.core.* import net.corda.testing.driver.driver import net.corda.testing.node.User import org.junit.Test diff --git a/docs/source/example-code/src/main/kotlin/net/corda/docs/ClientRpcTutorial.kt b/docs/source/example-code/src/main/kotlin/net/corda/docs/ClientRpcTutorial.kt index 5e61c128b1..896152398d 100644 --- a/docs/source/example-code/src/main/kotlin/net/corda/docs/ClientRpcTutorial.kt +++ b/docs/source/example-code/src/main/kotlin/net/corda/docs/ClientRpcTutorial.kt @@ -17,7 +17,7 @@ import net.corda.finance.flows.CashIssueFlow import net.corda.finance.flows.CashPaymentFlow import net.corda.node.services.Permissions.Companion.invokeRpc import net.corda.node.services.Permissions.Companion.startFlow -import net.corda.testing.ALICE_NAME +import net.corda.testing.core.ALICE_NAME import net.corda.testing.node.User import net.corda.testing.driver.driver import org.graphstream.graph.Edge diff --git a/docs/source/example-code/src/main/kotlin/net/corda/docs/FxTransactionBuildTutorial.kt b/docs/source/example-code/src/main/kotlin/net/corda/docs/FxTransactionBuildTutorial.kt index 98d1e279f8..73893bae56 100644 --- a/docs/source/example-code/src/main/kotlin/net/corda/docs/FxTransactionBuildTutorial.kt +++ b/docs/source/example-code/src/main/kotlin/net/corda/docs/FxTransactionBuildTutorial.kt @@ -16,7 +16,7 @@ import net.corda.core.transactions.TransactionBuilder import net.corda.core.utilities.unwrap import net.corda.finance.contracts.asset.Cash import net.corda.finance.schemas.CashSchemaV1 -import net.corda.testing.chooseIdentity +import net.corda.testing.core.chooseIdentity import java.util.* @CordaSerializable diff --git a/docs/source/example-code/src/main/kotlin/net/corda/docs/tutorial/contract/TutorialContract.kt b/docs/source/example-code/src/main/kotlin/net/corda/docs/tutorial/contract/TutorialContract.kt index 8389e73a99..a3cd0a656f 100644 --- a/docs/source/example-code/src/main/kotlin/net/corda/docs/tutorial/contract/TutorialContract.kt +++ b/docs/source/example-code/src/main/kotlin/net/corda/docs/tutorial/contract/TutorialContract.kt @@ -10,7 +10,7 @@ import net.corda.core.transactions.LedgerTransaction import net.corda.core.transactions.TransactionBuilder import net.corda.finance.contracts.asset.Cash import net.corda.finance.utils.sumCashBy -import net.corda.testing.chooseIdentityAndCert +import net.corda.testing.core.chooseIdentityAndCert import java.time.Instant import java.util.* diff --git a/docs/source/example-code/src/test/java/net/corda/docs/java/tutorial/testdsl/CommercialPaperTest.java b/docs/source/example-code/src/test/java/net/corda/docs/java/tutorial/testdsl/CommercialPaperTest.java index 087d632e83..91516ee751 100644 --- a/docs/source/example-code/src/test/java/net/corda/docs/java/tutorial/testdsl/CommercialPaperTest.java +++ b/docs/source/example-code/src/test/java/net/corda/docs/java/tutorial/testdsl/CommercialPaperTest.java @@ -8,7 +8,7 @@ import net.corda.finance.contracts.ICommercialPaperState; import net.corda.finance.contracts.JavaCommercialPaper; import net.corda.finance.contracts.asset.Cash; import net.corda.testing.node.MockServices; -import net.corda.testing.TestIdentity; +import net.corda.testing.core.TestIdentity; import org.junit.Test; import java.security.PublicKey; @@ -22,7 +22,9 @@ import static net.corda.finance.contracts.JavaCommercialPaper.JCP_PROGRAM_ID; import static net.corda.testing.node.MockServicesKt.makeTestIdentityService; import static net.corda.testing.node.NodeTestUtils.ledger; import static net.corda.testing.node.NodeTestUtils.transaction; -import static net.corda.testing.TestConstants.*; +import static net.corda.testing.core.TestConstants.ALICE_NAME; +import static net.corda.testing.core.TestConstants.BOB_NAME; +import static net.corda.testing.core.TestConstants.TEST_TX_TIME; public class CommercialPaperTest { private static final TestIdentity ALICE = new TestIdentity(ALICE_NAME, 70L); diff --git a/docs/source/example-code/src/test/kotlin/net/corda/docs/CustomVaultQueryTest.kt b/docs/source/example-code/src/test/kotlin/net/corda/docs/CustomVaultQueryTest.kt index f2ecb42a3f..372259ddb6 100644 --- a/docs/source/example-code/src/test/kotlin/net/corda/docs/CustomVaultQueryTest.kt +++ b/docs/source/example-code/src/test/kotlin/net/corda/docs/CustomVaultQueryTest.kt @@ -8,7 +8,7 @@ import net.corda.finance.* import net.corda.finance.contracts.getCashBalances import net.corda.finance.flows.CashIssueFlow import net.corda.node.internal.StartedNode -import net.corda.testing.chooseIdentity +import net.corda.testing.core.chooseIdentity import net.corda.testing.node.MockNetwork import net.corda.testing.node.startFlow import org.junit.After diff --git a/docs/source/example-code/src/test/kotlin/net/corda/docs/FxTransactionBuildTutorialTest.kt b/docs/source/example-code/src/test/kotlin/net/corda/docs/FxTransactionBuildTutorialTest.kt index 5009779529..eaabd01a40 100644 --- a/docs/source/example-code/src/test/kotlin/net/corda/docs/FxTransactionBuildTutorialTest.kt +++ b/docs/source/example-code/src/test/kotlin/net/corda/docs/FxTransactionBuildTutorialTest.kt @@ -8,7 +8,7 @@ import net.corda.finance.* import net.corda.finance.contracts.getCashBalances import net.corda.finance.flows.CashIssueFlow import net.corda.node.internal.StartedNode -import net.corda.testing.chooseIdentity +import net.corda.testing.core.chooseIdentity import net.corda.testing.node.MockNetwork import net.corda.testing.node.startFlow import org.junit.After diff --git a/docs/source/example-code/src/test/kotlin/net/corda/docs/WorkflowTransactionBuildTutorialTest.kt b/docs/source/example-code/src/test/kotlin/net/corda/docs/WorkflowTransactionBuildTutorialTest.kt index dca397eb98..c74c5c7d45 100644 --- a/docs/source/example-code/src/test/kotlin/net/corda/docs/WorkflowTransactionBuildTutorialTest.kt +++ b/docs/source/example-code/src/test/kotlin/net/corda/docs/WorkflowTransactionBuildTutorialTest.kt @@ -10,7 +10,8 @@ import net.corda.core.node.services.vault.QueryCriteria import net.corda.core.toFuture import net.corda.core.utilities.getOrThrow import net.corda.node.services.api.StartedNodeServices -import net.corda.testing.* +import net.corda.testing.core.ALICE_NAME +import net.corda.testing.core.BOB_NAME import net.corda.testing.node.MockNetwork import net.corda.testing.node.startFlow import org.junit.After diff --git a/docs/source/example-code/src/test/kotlin/net/corda/docs/tutorial/testdsl/TutorialTestDSL.kt b/docs/source/example-code/src/test/kotlin/net/corda/docs/tutorial/testdsl/TutorialTestDSL.kt index 340cd1d9d9..b0c910e45d 100644 --- a/docs/source/example-code/src/test/kotlin/net/corda/docs/tutorial/testdsl/TutorialTestDSL.kt +++ b/docs/source/example-code/src/test/kotlin/net/corda/docs/tutorial/testdsl/TutorialTestDSL.kt @@ -14,7 +14,7 @@ import net.corda.finance.contracts.ICommercialPaperState import net.corda.finance.contracts.asset.CASH import net.corda.finance.contracts.asset.Cash import net.corda.node.services.api.IdentityServiceInternal -import net.corda.testing.* +import net.corda.testing.core.* import net.corda.testing.internal.rigorousMock import net.corda.testing.node.MockServices import net.corda.testing.node.ledger diff --git a/experimental/src/test/kotlin/net/corda/finance/contracts/universal/Cap.kt b/experimental/src/test/kotlin/net/corda/finance/contracts/universal/Cap.kt index ed12e6190c..55e5b7fa3a 100644 --- a/experimental/src/test/kotlin/net/corda/finance/contracts/universal/Cap.kt +++ b/experimental/src/test/kotlin/net/corda/finance/contracts/universal/Cap.kt @@ -8,7 +8,9 @@ import net.corda.finance.contracts.FixOf import net.corda.finance.contracts.Frequency import net.corda.finance.contracts.Tenor import net.corda.node.services.api.IdentityServiceInternal -import net.corda.testing.* +import net.corda.testing.core.DUMMY_NOTARY_NAME +import net.corda.testing.core.SerializationEnvironmentRule +import net.corda.testing.core.TestIdentity import net.corda.testing.dsl.EnforceVerifyOrFail import net.corda.testing.dsl.TransactionDSL import net.corda.testing.dsl.TransactionDSLInterpreter diff --git a/experimental/src/test/kotlin/net/corda/finance/contracts/universal/Caplet.kt b/experimental/src/test/kotlin/net/corda/finance/contracts/universal/Caplet.kt index c9535fd109..599be9f0cb 100644 --- a/experimental/src/test/kotlin/net/corda/finance/contracts/universal/Caplet.kt +++ b/experimental/src/test/kotlin/net/corda/finance/contracts/universal/Caplet.kt @@ -2,7 +2,7 @@ package net.corda.finance.contracts.universal import net.corda.finance.contracts.FixOf import net.corda.finance.contracts.Tenor -import net.corda.testing.SerializationEnvironmentRule +import net.corda.testing.core.SerializationEnvironmentRule import org.junit.Ignore import org.junit.Rule import org.junit.Test diff --git a/experimental/src/test/kotlin/net/corda/finance/contracts/universal/ContractDefinition.kt b/experimental/src/test/kotlin/net/corda/finance/contracts/universal/ContractDefinition.kt index 112093b4b4..b173ace1a4 100644 --- a/experimental/src/test/kotlin/net/corda/finance/contracts/universal/ContractDefinition.kt +++ b/experimental/src/test/kotlin/net/corda/finance/contracts/universal/ContractDefinition.kt @@ -2,7 +2,7 @@ package net.corda.finance.contracts.universal import net.corda.core.crypto.generateKeyPair import net.corda.core.identity.CordaX500Name -import net.corda.testing.TestIdentity +import net.corda.testing.core.TestIdentity import org.junit.Test import java.util.* import kotlin.test.assertEquals diff --git a/experimental/src/test/kotlin/net/corda/finance/contracts/universal/FXFwdTimeOption.kt b/experimental/src/test/kotlin/net/corda/finance/contracts/universal/FXFwdTimeOption.kt index 380dd564d2..f79734a8ad 100644 --- a/experimental/src/test/kotlin/net/corda/finance/contracts/universal/FXFwdTimeOption.kt +++ b/experimental/src/test/kotlin/net/corda/finance/contracts/universal/FXFwdTimeOption.kt @@ -1,6 +1,6 @@ package net.corda.finance.contracts.universal -import net.corda.testing.SerializationEnvironmentRule +import net.corda.testing.core.SerializationEnvironmentRule import org.junit.Ignore import org.junit.Rule import org.junit.Test diff --git a/experimental/src/test/kotlin/net/corda/finance/contracts/universal/FXSwap.kt b/experimental/src/test/kotlin/net/corda/finance/contracts/universal/FXSwap.kt index 4dadb338c2..f5fa59ddf1 100644 --- a/experimental/src/test/kotlin/net/corda/finance/contracts/universal/FXSwap.kt +++ b/experimental/src/test/kotlin/net/corda/finance/contracts/universal/FXSwap.kt @@ -1,6 +1,6 @@ package net.corda.finance.contracts.universal -import net.corda.testing.SerializationEnvironmentRule +import net.corda.testing.core.SerializationEnvironmentRule import org.junit.Ignore import org.junit.Rule import org.junit.Test diff --git a/experimental/src/test/kotlin/net/corda/finance/contracts/universal/IRS.kt b/experimental/src/test/kotlin/net/corda/finance/contracts/universal/IRS.kt index ae88022c78..1205a64947 100644 --- a/experimental/src/test/kotlin/net/corda/finance/contracts/universal/IRS.kt +++ b/experimental/src/test/kotlin/net/corda/finance/contracts/universal/IRS.kt @@ -3,7 +3,7 @@ package net.corda.finance.contracts.universal import net.corda.finance.contracts.FixOf import net.corda.finance.contracts.Frequency import net.corda.finance.contracts.Tenor -import net.corda.testing.SerializationEnvironmentRule +import net.corda.testing.core.SerializationEnvironmentRule import org.junit.Ignore import org.junit.Rule import org.junit.Test diff --git a/experimental/src/test/kotlin/net/corda/finance/contracts/universal/RollOutTests.kt b/experimental/src/test/kotlin/net/corda/finance/contracts/universal/RollOutTests.kt index 185cebd57a..12d71721ff 100644 --- a/experimental/src/test/kotlin/net/corda/finance/contracts/universal/RollOutTests.kt +++ b/experimental/src/test/kotlin/net/corda/finance/contracts/universal/RollOutTests.kt @@ -1,7 +1,7 @@ package net.corda.finance.contracts.universal import net.corda.finance.contracts.Frequency -import net.corda.testing.SerializationEnvironmentRule +import net.corda.testing.core.SerializationEnvironmentRule import org.junit.Rule import org.junit.Test import java.time.Instant diff --git a/experimental/src/test/kotlin/net/corda/finance/contracts/universal/Swaption.kt b/experimental/src/test/kotlin/net/corda/finance/contracts/universal/Swaption.kt index aa82468c30..5dfbdf28ec 100644 --- a/experimental/src/test/kotlin/net/corda/finance/contracts/universal/Swaption.kt +++ b/experimental/src/test/kotlin/net/corda/finance/contracts/universal/Swaption.kt @@ -2,7 +2,7 @@ package net.corda.finance.contracts.universal import net.corda.finance.contracts.Frequency import net.corda.finance.contracts.Tenor -import net.corda.testing.SerializationEnvironmentRule +import net.corda.testing.core.SerializationEnvironmentRule import org.junit.Ignore import org.junit.Rule import org.junit.Test diff --git a/experimental/src/test/kotlin/net/corda/finance/contracts/universal/ZeroCouponBond.kt b/experimental/src/test/kotlin/net/corda/finance/contracts/universal/ZeroCouponBond.kt index 10aee2e760..e8eb2cc71f 100644 --- a/experimental/src/test/kotlin/net/corda/finance/contracts/universal/ZeroCouponBond.kt +++ b/experimental/src/test/kotlin/net/corda/finance/contracts/universal/ZeroCouponBond.kt @@ -1,6 +1,6 @@ package net.corda.finance.contracts.universal -import net.corda.testing.SerializationEnvironmentRule +import net.corda.testing.core.SerializationEnvironmentRule import org.junit.Rule import org.junit.Test import java.time.Instant diff --git a/finance/src/test/java/net/corda/finance/contracts/asset/CashTestsJava.java b/finance/src/test/java/net/corda/finance/contracts/asset/CashTestsJava.java index 5d3f27286a..034404e184 100644 --- a/finance/src/test/java/net/corda/finance/contracts/asset/CashTestsJava.java +++ b/finance/src/test/java/net/corda/finance/contracts/asset/CashTestsJava.java @@ -5,9 +5,9 @@ import net.corda.core.identity.AnonymousParty; import net.corda.core.identity.CordaX500Name; import net.corda.core.identity.Party; import net.corda.node.services.api.IdentityServiceInternal; -import net.corda.testing.DummyCommandData; -import net.corda.testing.SerializationEnvironmentRule; -import net.corda.testing.TestIdentity; +import net.corda.testing.core.DummyCommandData; +import net.corda.testing.core.SerializationEnvironmentRule; +import net.corda.testing.core.TestIdentity; import net.corda.testing.node.MockServices; import org.junit.Rule; import org.junit.Test; @@ -17,7 +17,7 @@ import static net.corda.finance.Currencies.DOLLARS; import static net.corda.finance.Currencies.issuedBy; import static net.corda.testing.node.NodeTestUtils.transaction; import static net.corda.testing.internal.InternalTestUtilsKt.rigorousMock; -import static net.corda.testing.TestConstants.DUMMY_NOTARY_NAME; +import static net.corda.testing.core.TestConstants.DUMMY_NOTARY_NAME; import static org.mockito.Mockito.doReturn; /** diff --git a/finance/src/test/kotlin/net/corda/finance/contracts/CommercialPaperTests.kt b/finance/src/test/kotlin/net/corda/finance/contracts/CommercialPaperTests.kt index dc011b5fa7..12aa01f5f1 100644 --- a/finance/src/test/kotlin/net/corda/finance/contracts/CommercialPaperTests.kt +++ b/finance/src/test/kotlin/net/corda/finance/contracts/CommercialPaperTests.kt @@ -17,7 +17,7 @@ import net.corda.finance.DOLLARS import net.corda.finance.`issued by` import net.corda.finance.contracts.asset.* import net.corda.node.services.api.IdentityServiceInternal -import net.corda.testing.* +import net.corda.testing.core.* import net.corda.testing.dsl.EnforceVerifyOrFail import net.corda.testing.dsl.TransactionDSL import net.corda.testing.dsl.TransactionDSLInterpreter diff --git a/finance/src/test/kotlin/net/corda/finance/contracts/asset/CashTests.kt b/finance/src/test/kotlin/net/corda/finance/contracts/asset/CashTests.kt index af3900486e..aed8d302f8 100644 --- a/finance/src/test/kotlin/net/corda/finance/contracts/asset/CashTests.kt +++ b/finance/src/test/kotlin/net/corda/finance/contracts/asset/CashTests.kt @@ -18,8 +18,8 @@ import net.corda.finance.utils.sumCashOrZero import net.corda.node.services.api.IdentityServiceInternal import net.corda.node.services.vault.NodeVaultService import net.corda.nodeapi.internal.persistence.CordaPersistence -import net.corda.testing.* import net.corda.testing.contracts.DummyState +import net.corda.testing.core.* import net.corda.testing.internal.LogHelper import net.corda.testing.dsl.EnforceVerifyOrFail import net.corda.testing.dsl.TransactionDSL diff --git a/finance/src/test/kotlin/net/corda/finance/contracts/asset/ObligationTests.kt b/finance/src/test/kotlin/net/corda/finance/contracts/asset/ObligationTests.kt index 73f1363d36..35a0db24c3 100644 --- a/finance/src/test/kotlin/net/corda/finance/contracts/asset/ObligationTests.kt +++ b/finance/src/test/kotlin/net/corda/finance/contracts/asset/ObligationTests.kt @@ -19,9 +19,9 @@ import net.corda.finance.contracts.Commodity import net.corda.finance.contracts.NetType import net.corda.finance.contracts.asset.Obligation.Lifecycle import net.corda.node.services.api.IdentityServiceInternal -import net.corda.testing.* import net.corda.testing.contracts.DummyContract import net.corda.testing.contracts.DummyState +import net.corda.testing.core.* import net.corda.testing.dsl.* import net.corda.testing.internal.rigorousMock import net.corda.testing.node.MockServices diff --git a/finance/src/test/kotlin/net/corda/finance/flows/CashExitFlowTests.kt b/finance/src/test/kotlin/net/corda/finance/flows/CashExitFlowTests.kt index 5bb058c5ac..fc32245551 100644 --- a/finance/src/test/kotlin/net/corda/finance/flows/CashExitFlowTests.kt +++ b/finance/src/test/kotlin/net/corda/finance/flows/CashExitFlowTests.kt @@ -7,7 +7,7 @@ import net.corda.finance.DOLLARS import net.corda.finance.`issued by` import net.corda.finance.contracts.asset.Cash import net.corda.node.internal.StartedNode -import net.corda.testing.BOC_NAME +import net.corda.testing.core.BOC_NAME import net.corda.testing.node.InMemoryMessagingNetwork.ServicePeerAllocationStrategy.RoundRobin import net.corda.testing.node.MockNetwork import net.corda.testing.node.MockNetwork.MockNode diff --git a/finance/src/test/kotlin/net/corda/finance/flows/CashIssueFlowTests.kt b/finance/src/test/kotlin/net/corda/finance/flows/CashIssueFlowTests.kt index 98df506c31..9979ec31b0 100644 --- a/finance/src/test/kotlin/net/corda/finance/flows/CashIssueFlowTests.kt +++ b/finance/src/test/kotlin/net/corda/finance/flows/CashIssueFlowTests.kt @@ -7,7 +7,7 @@ import net.corda.finance.DOLLARS import net.corda.finance.`issued by` import net.corda.finance.contracts.asset.Cash import net.corda.node.internal.StartedNode -import net.corda.testing.BOC_NAME +import net.corda.testing.core.BOC_NAME import net.corda.testing.node.InMemoryMessagingNetwork.ServicePeerAllocationStrategy.RoundRobin import net.corda.testing.node.MockNetwork import net.corda.testing.node.MockNetwork.MockNode diff --git a/finance/src/test/kotlin/net/corda/finance/flows/CashPaymentFlowTests.kt b/finance/src/test/kotlin/net/corda/finance/flows/CashPaymentFlowTests.kt index 1c046752a2..de51e3b702 100644 --- a/finance/src/test/kotlin/net/corda/finance/flows/CashPaymentFlowTests.kt +++ b/finance/src/test/kotlin/net/corda/finance/flows/CashPaymentFlowTests.kt @@ -10,7 +10,7 @@ import net.corda.finance.DOLLARS import net.corda.finance.`issued by` import net.corda.finance.contracts.asset.Cash import net.corda.node.internal.StartedNode -import net.corda.testing.* +import net.corda.testing.core.* import net.corda.testing.node.InMemoryMessagingNetwork.ServicePeerAllocationStrategy.RoundRobin import net.corda.testing.node.MockNetwork import net.corda.testing.node.MockNetwork.MockNode diff --git a/node-api/src/test/java/net/corda/nodeapi/internal/serialization/ForbiddenLambdaSerializationTests.java b/node-api/src/test/java/net/corda/nodeapi/internal/serialization/ForbiddenLambdaSerializationTests.java index 5e7dffce8d..34bcefbc0d 100644 --- a/node-api/src/test/java/net/corda/nodeapi/internal/serialization/ForbiddenLambdaSerializationTests.java +++ b/node-api/src/test/java/net/corda/nodeapi/internal/serialization/ForbiddenLambdaSerializationTests.java @@ -4,7 +4,7 @@ import com.google.common.collect.Maps; import net.corda.core.serialization.SerializationContext; import net.corda.core.serialization.SerializationFactory; import net.corda.core.serialization.SerializedBytes; -import net.corda.testing.SerializationEnvironmentRule; +import net.corda.testing.core.SerializationEnvironmentRule; import net.corda.nodeapi.internal.serialization.kryo.CordaClosureBlacklistSerializer; import net.corda.nodeapi.internal.serialization.kryo.KryoSerializationSchemeKt; import org.junit.Before; diff --git a/node-api/src/test/java/net/corda/nodeapi/internal/serialization/LambdaCheckpointSerializationTest.java b/node-api/src/test/java/net/corda/nodeapi/internal/serialization/LambdaCheckpointSerializationTest.java index 6e25a3f53d..f8f9b4451b 100644 --- a/node-api/src/test/java/net/corda/nodeapi/internal/serialization/LambdaCheckpointSerializationTest.java +++ b/node-api/src/test/java/net/corda/nodeapi/internal/serialization/LambdaCheckpointSerializationTest.java @@ -4,7 +4,7 @@ import com.google.common.collect.Maps; import net.corda.core.serialization.SerializationContext; import net.corda.core.serialization.SerializationFactory; import net.corda.core.serialization.SerializedBytes; -import net.corda.testing.SerializationEnvironmentRule; +import net.corda.testing.core.SerializationEnvironmentRule; import net.corda.nodeapi.internal.serialization.kryo.CordaClosureSerializer; import net.corda.nodeapi.internal.serialization.kryo.KryoSerializationSchemeKt; import org.junit.Before; diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/AttachmentsClassLoaderStaticContractTests.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/AttachmentsClassLoaderStaticContractTests.kt index d0dd03ad6d..519c1632fc 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/AttachmentsClassLoaderStaticContractTests.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/AttachmentsClassLoaderStaticContractTests.kt @@ -13,7 +13,9 @@ import net.corda.core.transactions.LedgerTransaction import net.corda.core.transactions.TransactionBuilder import net.corda.node.internal.cordapp.CordappLoader import net.corda.node.internal.cordapp.CordappProviderImpl -import net.corda.testing.* +import net.corda.testing.core.DUMMY_NOTARY_NAME +import net.corda.testing.core.SerializationEnvironmentRule +import net.corda.testing.core.TestIdentity import net.corda.testing.internal.rigorousMock import net.corda.testing.services.MockAttachmentStorage import org.junit.Assert.* diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/AttachmentsClassLoaderTests.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/AttachmentsClassLoaderTests.kt index bbf8e5ca21..af8e806874 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/AttachmentsClassLoaderTests.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/AttachmentsClassLoaderTests.kt @@ -18,7 +18,9 @@ import net.corda.nodeapi.DummyContractBackdoor import net.corda.nodeapi.internal.serialization.SerializeAsTokenContextImpl import net.corda.nodeapi.internal.serialization.attachmentsClassLoaderEnabledPropertyName import net.corda.nodeapi.internal.serialization.withTokenContext -import net.corda.testing.* +import net.corda.testing.core.DUMMY_NOTARY_NAME +import net.corda.testing.core.SerializationEnvironmentRule +import net.corda.testing.core.TestIdentity import net.corda.testing.internal.kryoSpecific import net.corda.testing.internal.rigorousMock import net.corda.testing.services.MockAttachmentStorage diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/SignedNodeInfoTest.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/SignedNodeInfoTest.kt index 0ad99ae579..933dd46f41 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/SignedNodeInfoTest.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/SignedNodeInfoTest.kt @@ -1,9 +1,9 @@ package net.corda.nodeapi.internal import net.corda.core.crypto.Crypto -import net.corda.testing.ALICE_NAME -import net.corda.testing.BOB_NAME -import net.corda.testing.SerializationEnvironmentRule +import net.corda.testing.core.ALICE_NAME +import net.corda.testing.core.BOB_NAME +import net.corda.testing.core.SerializationEnvironmentRule import net.corda.testing.internal.TestNodeInfoBuilder import net.corda.testing.internal.signWith import org.assertj.core.api.Assertions.assertThat diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/crypto/X509UtilitiesTest.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/crypto/X509UtilitiesTest.kt index 05ec9db2e5..512e0a096d 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/crypto/X509UtilitiesTest.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/crypto/X509UtilitiesTest.kt @@ -15,9 +15,9 @@ import net.corda.nodeapi.internal.serialization.AllWhitelist import net.corda.nodeapi.internal.serialization.SerializationContextImpl import net.corda.nodeapi.internal.serialization.SerializationFactoryImpl import net.corda.nodeapi.internal.serialization.kryo.KryoHeaderV0_1 -import net.corda.testing.ALICE_NAME -import net.corda.testing.BOB_NAME -import net.corda.testing.TestIdentity +import net.corda.testing.core.ALICE_NAME +import net.corda.testing.core.BOB_NAME +import net.corda.testing.core.TestIdentity import net.corda.testing.internal.createDevIntermediateCaCertPath import org.assertj.core.api.Assertions.assertThat import org.bouncycastle.asn1.x509.BasicConstraints diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/ContractAttachmentSerializerTest.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/ContractAttachmentSerializerTest.kt index e24dc671dd..7c009212dd 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/ContractAttachmentSerializerTest.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/ContractAttachmentSerializerTest.kt @@ -3,8 +3,8 @@ package net.corda.nodeapi.internal.serialization import net.corda.core.contracts.ContractAttachment import net.corda.core.identity.CordaX500Name import net.corda.core.serialization.* -import net.corda.testing.* import net.corda.testing.contracts.DummyContract +import net.corda.testing.core.SerializationEnvironmentRule import net.corda.testing.internal.rigorousMock import net.corda.testing.node.MockServices import org.assertj.core.api.Assertions.assertThat diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/KryoTests.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/KryoTests.kt index d48558a6fe..7cc408b4ea 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/KryoTests.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/KryoTests.kt @@ -14,9 +14,9 @@ import net.corda.core.utilities.sequence import net.corda.node.serialization.KryoServerSerializationScheme import net.corda.node.services.persistence.NodeAttachmentService import net.corda.nodeapi.internal.serialization.kryo.KryoHeaderV0_1 -import net.corda.testing.ALICE_NAME -import net.corda.testing.SerializationEnvironmentRule -import net.corda.testing.TestIdentity +import net.corda.testing.core.ALICE_NAME +import net.corda.testing.core.SerializationEnvironmentRule +import net.corda.testing.core.TestIdentity import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThatThrownBy import org.junit.Before diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/ListsSerializationTest.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/ListsSerializationTest.kt index d732033477..e19fe31a3c 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/ListsSerializationTest.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/ListsSerializationTest.kt @@ -10,7 +10,7 @@ import net.corda.nodeapi.internal.serialization.amqp.SerializerFactory import net.corda.nodeapi.internal.serialization.kryo.KryoHeaderV0_1 import net.corda.testing.internal.amqpSpecific import net.corda.testing.internal.kryoSpecific -import net.corda.testing.SerializationEnvironmentRule +import net.corda.testing.core.SerializationEnvironmentRule import org.assertj.core.api.Assertions import org.junit.Assert.assertArrayEquals import org.junit.Assert.assertEquals diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/MapsSerializationTest.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/MapsSerializationTest.kt index d2ba294d2b..577ca6b5f9 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/MapsSerializationTest.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/MapsSerializationTest.kt @@ -8,7 +8,7 @@ import net.corda.core.serialization.deserialize import net.corda.core.serialization.serialize import net.corda.node.services.statemachine.SessionData import net.corda.nodeapi.internal.serialization.kryo.KryoHeaderV0_1 -import net.corda.testing.SerializationEnvironmentRule +import net.corda.testing.core.SerializationEnvironmentRule import net.corda.testing.internal.amqpSpecific import net.corda.testing.internal.kryoSpecific import org.assertj.core.api.Assertions.assertThatThrownBy diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/PrivateKeySerializationTest.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/PrivateKeySerializationTest.kt index adc8097a97..c2d25b4ff0 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/PrivateKeySerializationTest.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/PrivateKeySerializationTest.kt @@ -4,7 +4,7 @@ import net.corda.core.crypto.Crypto import net.corda.core.serialization.SerializationContext.UseCase.* import net.corda.core.serialization.SerializationDefaults import net.corda.core.serialization.serialize -import net.corda.testing.SerializationEnvironmentRule +import net.corda.testing.core.SerializationEnvironmentRule import org.assertj.core.api.Assertions.assertThatThrownBy import org.junit.Rule import org.junit.Test diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/SerializationTokenTest.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/SerializationTokenTest.kt index 70f165d9a4..1373f363c1 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/SerializationTokenTest.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/SerializationTokenTest.kt @@ -9,7 +9,7 @@ import net.corda.nodeapi.internal.serialization.kryo.CordaKryo import net.corda.nodeapi.internal.serialization.kryo.DefaultKryoCustomizer import net.corda.nodeapi.internal.serialization.kryo.KryoHeaderV0_1 import net.corda.testing.internal.rigorousMock -import net.corda.testing.SerializationEnvironmentRule +import net.corda.testing.core.SerializationEnvironmentRule import org.assertj.core.api.Assertions.assertThat import org.junit.Before import org.junit.Rule diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/SetsSerializationTest.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/SetsSerializationTest.kt index 9a910aeb3a..65828eeebf 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/SetsSerializationTest.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/SetsSerializationTest.kt @@ -7,7 +7,7 @@ import net.corda.core.serialization.serialize import net.corda.node.services.statemachine.SessionData import net.corda.nodeapi.internal.serialization.kryo.KryoHeaderV0_1 import net.corda.testing.internal.kryoSpecific -import net.corda.testing.SerializationEnvironmentRule +import net.corda.testing.core.SerializationEnvironmentRule import org.junit.Assert.assertArrayEquals import org.junit.Assert.assertEquals import org.junit.Rule diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializationOutputTests.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializationOutputTests.kt index c6b1eeff4d..d8c1db9cf9 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializationOutputTests.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializationOutputTests.kt @@ -20,8 +20,10 @@ import net.corda.nodeapi.internal.serialization.AllWhitelist import net.corda.nodeapi.internal.serialization.EmptyWhitelist import net.corda.nodeapi.internal.serialization.GeneratedAttachment import net.corda.nodeapi.internal.serialization.amqp.SerializerFactory.Companion.isPrimitive -import net.corda.testing.* import net.corda.testing.contracts.DummyContract +import net.corda.testing.core.BOB_NAME +import net.corda.testing.core.SerializationEnvironmentRule +import net.corda.testing.core.TestIdentity import org.apache.activemq.artemis.api.core.SimpleString import org.apache.qpid.proton.amqp.* import org.apache.qpid.proton.codec.DecoderImpl diff --git a/node/src/integration-test/kotlin/net/corda/node/AuthDBTests.kt b/node/src/integration-test/kotlin/net/corda/node/AuthDBTests.kt index 1c61915e40..db4ec0b049 100644 --- a/node/src/integration-test/kotlin/net/corda/node/AuthDBTests.kt +++ b/node/src/integration-test/kotlin/net/corda/node/AuthDBTests.kt @@ -15,7 +15,7 @@ import net.corda.node.internal.StartedNode import net.corda.node.services.Permissions import net.corda.node.services.config.PasswordEncryption import net.corda.testing.node.internal.NodeBasedTest -import net.corda.testing.* +import net.corda.testing.core.ALICE_NAME import org.apache.activemq.artemis.api.core.ActiveMQSecurityException import org.apache.shiro.authc.credential.DefaultPasswordService import org.junit.After diff --git a/node/src/integration-test/kotlin/net/corda/node/BootTests.kt b/node/src/integration-test/kotlin/net/corda/node/BootTests.kt index 6f1b74b446..7bb503d744 100644 --- a/node/src/integration-test/kotlin/net/corda/node/BootTests.kt +++ b/node/src/integration-test/kotlin/net/corda/node/BootTests.kt @@ -8,7 +8,7 @@ import net.corda.core.messaging.startFlow import net.corda.core.utilities.getOrThrow import net.corda.node.internal.NodeStartup import net.corda.node.services.Permissions.Companion.startFlow -import net.corda.testing.ALICE_NAME +import net.corda.testing.core.ALICE_NAME import net.corda.testing.node.User import net.corda.testing.common.internal.ProjectStructure.projectRootDir import net.corda.testing.driver.driver diff --git a/node/src/integration-test/kotlin/net/corda/node/CordappScanningDriverTest.kt b/node/src/integration-test/kotlin/net/corda/node/CordappScanningDriverTest.kt index f420896945..94f0034278 100644 --- a/node/src/integration-test/kotlin/net/corda/node/CordappScanningDriverTest.kt +++ b/node/src/integration-test/kotlin/net/corda/node/CordappScanningDriverTest.kt @@ -8,7 +8,9 @@ import net.corda.core.messaging.startFlow import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.unwrap import net.corda.node.services.Permissions.Companion.startFlow -import net.corda.testing.* +import net.corda.testing.core.ALICE_NAME +import net.corda.testing.core.BOB_NAME +import net.corda.testing.core.chooseIdentity import net.corda.testing.driver.driver import net.corda.testing.node.User import org.assertj.core.api.Assertions.assertThat diff --git a/node/src/integration-test/kotlin/net/corda/node/NodeKeystoreCheckTest.kt b/node/src/integration-test/kotlin/net/corda/node/NodeKeystoreCheckTest.kt index d2268941d3..f36d543cc2 100644 --- a/node/src/integration-test/kotlin/net/corda/node/NodeKeystoreCheckTest.kt +++ b/node/src/integration-test/kotlin/net/corda/node/NodeKeystoreCheckTest.kt @@ -6,15 +6,12 @@ import net.corda.core.utilities.getOrThrow import net.corda.node.services.config.configureDevKeyAndTrustStores import net.corda.nodeapi.internal.config.SSLConfiguration import net.corda.nodeapi.internal.crypto.* -import net.corda.testing.ALICE_NAME +import net.corda.testing.core.ALICE_NAME import net.corda.testing.driver.driver import org.assertj.core.api.Assertions.assertThatThrownBy import org.junit.Test import java.nio.file.Path import javax.security.auth.x500.X500Principal -import kotlin.test.assertEquals -import kotlin.test.assertFailsWith -import kotlin.test.assertTrue class NodeKeystoreCheckTest { @Test diff --git a/node/src/integration-test/kotlin/net/corda/node/NodePerformanceTests.kt b/node/src/integration-test/kotlin/net/corda/node/NodePerformanceTests.kt index cf62847193..b0868815c8 100644 --- a/node/src/integration-test/kotlin/net/corda/node/NodePerformanceTests.kt +++ b/node/src/integration-test/kotlin/net/corda/node/NodePerformanceTests.kt @@ -13,7 +13,7 @@ import net.corda.finance.DOLLARS import net.corda.finance.flows.CashIssueFlow import net.corda.finance.flows.CashPaymentFlow import net.corda.node.services.Permissions.Companion.startFlow -import net.corda.testing.DUMMY_NOTARY_NAME +import net.corda.testing.core.DUMMY_NOTARY_NAME import net.corda.testing.node.User import net.corda.testing.driver.NodeHandle import net.corda.testing.driver.driver diff --git a/node/src/integration-test/kotlin/net/corda/node/SSHServerTest.kt b/node/src/integration-test/kotlin/net/corda/node/SSHServerTest.kt index 78ac121dac..8a0e2e3727 100644 --- a/node/src/integration-test/kotlin/net/corda/node/SSHServerTest.kt +++ b/node/src/integration-test/kotlin/net/corda/node/SSHServerTest.kt @@ -12,12 +12,11 @@ import net.corda.core.utilities.ProgressTracker import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.unwrap import net.corda.node.services.Permissions.Companion.startFlow -import net.corda.testing.ALICE_NAME +import net.corda.testing.core.ALICE_NAME import net.corda.testing.node.User import net.corda.testing.driver.driver import org.assertj.core.api.Assertions.assertThat import org.bouncycastle.util.io.Streams -import org.junit.Ignore import org.junit.Test import java.net.ConnectException import java.util.regex.Pattern diff --git a/node/src/integration-test/kotlin/net/corda/node/amqp/AMQPBridgeTest.kt b/node/src/integration-test/kotlin/net/corda/node/amqp/AMQPBridgeTest.kt index 127eb0718b..33555018bb 100644 --- a/node/src/integration-test/kotlin/net/corda/node/amqp/AMQPBridgeTest.kt +++ b/node/src/integration-test/kotlin/net/corda/node/amqp/AMQPBridgeTest.kt @@ -16,7 +16,7 @@ import net.corda.node.services.messaging.ArtemisMessagingClient import net.corda.node.services.messaging.ArtemisMessagingServer import net.corda.nodeapi.internal.ArtemisMessagingComponent import net.corda.nodeapi.internal.crypto.loadKeyStore -import net.corda.testing.* +import net.corda.testing.core.* import net.corda.testing.internal.rigorousMock import org.apache.activemq.artemis.api.core.Message.HDR_DUPLICATE_DETECTION_ID import org.apache.activemq.artemis.api.core.RoutingType diff --git a/node/src/integration-test/kotlin/net/corda/node/amqp/ProtonWrapperTests.kt b/node/src/integration-test/kotlin/net/corda/node/amqp/ProtonWrapperTests.kt index 6c06fa607c..6c46f6f407 100644 --- a/node/src/integration-test/kotlin/net/corda/node/amqp/ProtonWrapperTests.kt +++ b/node/src/integration-test/kotlin/net/corda/node/amqp/ProtonWrapperTests.kt @@ -22,7 +22,7 @@ import net.corda.node.services.messaging.ArtemisMessagingServer import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.P2P_PREFIX import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.PEER_USER import net.corda.nodeapi.internal.crypto.loadKeyStore -import net.corda.testing.* +import net.corda.testing.core.* import net.corda.testing.internal.rigorousMock import org.apache.activemq.artemis.api.core.RoutingType import org.junit.Assert.assertArrayEquals diff --git a/node/src/integration-test/kotlin/net/corda/node/services/AttachmentLoadingTests.kt b/node/src/integration-test/kotlin/net/corda/node/services/AttachmentLoadingTests.kt index 7a7406cb76..5fa206af81 100644 --- a/node/src/integration-test/kotlin/net/corda/node/services/AttachmentLoadingTests.kt +++ b/node/src/integration-test/kotlin/net/corda/node/services/AttachmentLoadingTests.kt @@ -20,10 +20,10 @@ import net.corda.core.utilities.contextLogger import net.corda.core.utilities.getOrThrow import net.corda.node.internal.cordapp.CordappLoader import net.corda.node.internal.cordapp.CordappProviderImpl -import net.corda.testing.DUMMY_BANK_A_NAME -import net.corda.testing.DUMMY_NOTARY_NAME -import net.corda.testing.SerializationEnvironmentRule -import net.corda.testing.TestIdentity +import net.corda.testing.core.DUMMY_BANK_A_NAME +import net.corda.testing.core.DUMMY_NOTARY_NAME +import net.corda.testing.core.SerializationEnvironmentRule +import net.corda.testing.core.TestIdentity import net.corda.testing.driver.DriverDSL import net.corda.testing.driver.NodeHandle import net.corda.testing.driver.driver diff --git a/node/src/integration-test/kotlin/net/corda/node/services/BFTNotaryServiceTests.kt b/node/src/integration-test/kotlin/net/corda/node/services/BFTNotaryServiceTests.kt index bdaec3ec16..5da28ec519 100644 --- a/node/src/integration-test/kotlin/net/corda/node/services/BFTNotaryServiceTests.kt +++ b/node/src/integration-test/kotlin/net/corda/node/services/BFTNotaryServiceTests.kt @@ -26,10 +26,10 @@ import net.corda.node.services.transactions.minCorrectReplicas import net.corda.nodeapi.internal.DevIdentityGenerator import net.corda.nodeapi.internal.network.NetworkParametersCopier import net.corda.nodeapi.internal.network.NotaryInfo -import net.corda.testing.chooseIdentity +import net.corda.testing.core.chooseIdentity import net.corda.testing.common.internal.testNetworkParameters import net.corda.testing.contracts.DummyContract -import net.corda.testing.dummyCommand +import net.corda.testing.core.dummyCommand import net.corda.testing.node.MockNetwork import net.corda.testing.node.MockNetwork.MockNode import net.corda.testing.node.MockNodeParameters diff --git a/node/src/integration-test/kotlin/net/corda/node/services/DistributedServiceTests.kt b/node/src/integration-test/kotlin/net/corda/node/services/DistributedServiceTests.kt index 710b5ee97a..aca792c913 100644 --- a/node/src/integration-test/kotlin/net/corda/node/services/DistributedServiceTests.kt +++ b/node/src/integration-test/kotlin/net/corda/node/services/DistributedServiceTests.kt @@ -13,7 +13,7 @@ import net.corda.finance.flows.CashIssueFlow import net.corda.finance.flows.CashPaymentFlow import net.corda.node.services.Permissions.Companion.invokeRpc import net.corda.node.services.Permissions.Companion.startFlow -import net.corda.testing.* +import net.corda.testing.core.* import net.corda.testing.driver.NodeHandle import net.corda.testing.driver.driver import net.corda.testing.node.NotarySpec diff --git a/node/src/integration-test/kotlin/net/corda/node/services/RaftNotaryServiceTests.kt b/node/src/integration-test/kotlin/net/corda/node/services/RaftNotaryServiceTests.kt index 4f70f23f5d..825c24b273 100644 --- a/node/src/integration-test/kotlin/net/corda/node/services/RaftNotaryServiceTests.kt +++ b/node/src/integration-test/kotlin/net/corda/node/services/RaftNotaryServiceTests.kt @@ -11,12 +11,12 @@ import net.corda.core.internal.concurrent.map import net.corda.core.transactions.TransactionBuilder import net.corda.core.utilities.getOrThrow import net.corda.node.internal.StartedNode -import net.corda.testing.DUMMY_BANK_A_NAME -import net.corda.testing.chooseIdentity +import net.corda.testing.core.DUMMY_BANK_A_NAME +import net.corda.testing.core.chooseIdentity import net.corda.testing.contracts.DummyContract import net.corda.testing.driver.NodeHandle import net.corda.testing.driver.driver -import net.corda.testing.dummyCommand +import net.corda.testing.core.dummyCommand import net.corda.testing.node.ClusterSpec import net.corda.testing.node.NotarySpec import net.corda.testing.node.startFlow diff --git a/node/src/integration-test/kotlin/net/corda/node/services/network/NetworkMapTest.kt b/node/src/integration-test/kotlin/net/corda/node/services/network/NetworkMapTest.kt index 67b5d35a76..558fa62ae1 100644 --- a/node/src/integration-test/kotlin/net/corda/node/services/network/NetworkMapTest.kt +++ b/node/src/integration-test/kotlin/net/corda/node/services/network/NetworkMapTest.kt @@ -10,9 +10,9 @@ import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.seconds import net.corda.nodeapi.internal.network.NETWORK_PARAMS_FILE_NAME import net.corda.nodeapi.internal.network.NetworkParameters -import net.corda.testing.ALICE_NAME -import net.corda.testing.BOB_NAME -import net.corda.testing.SerializationEnvironmentRule +import net.corda.testing.core.ALICE_NAME +import net.corda.testing.core.BOB_NAME +import net.corda.testing.core.SerializationEnvironmentRule import net.corda.testing.common.internal.testNetworkParameters import net.corda.testing.driver.NodeHandle import net.corda.testing.driver.PortAllocation diff --git a/node/src/integration-test/kotlin/net/corda/node/services/network/NodeInfoWatcherTest.kt b/node/src/integration-test/kotlin/net/corda/node/services/network/NodeInfoWatcherTest.kt index b813558eca..2d54eca863 100644 --- a/node/src/integration-test/kotlin/net/corda/node/services/network/NodeInfoWatcherTest.kt +++ b/node/src/integration-test/kotlin/net/corda/node/services/network/NodeInfoWatcherTest.kt @@ -9,8 +9,8 @@ import net.corda.core.node.NodeInfo import net.corda.core.node.services.KeyManagementService import net.corda.nodeapi.internal.SignedNodeInfo import net.corda.nodeapi.internal.network.NodeInfoFilesCopier -import net.corda.testing.ALICE_NAME -import net.corda.testing.SerializationEnvironmentRule +import net.corda.testing.core.ALICE_NAME +import net.corda.testing.core.SerializationEnvironmentRule import net.corda.testing.internal.createNodeInfoAndSigned import net.corda.testing.node.MockKeyManagementService import net.corda.testing.node.makeTestIdentityService diff --git a/node/src/integration-test/kotlin/net/corda/node/services/network/PersistentNetworkMapCacheTest.kt b/node/src/integration-test/kotlin/net/corda/node/services/network/PersistentNetworkMapCacheTest.kt index 67ec3d592c..607852e7a8 100644 --- a/node/src/integration-test/kotlin/net/corda/node/services/network/PersistentNetworkMapCacheTest.kt +++ b/node/src/integration-test/kotlin/net/corda/node/services/network/PersistentNetworkMapCacheTest.kt @@ -7,7 +7,7 @@ import net.corda.core.node.NodeInfo import net.corda.core.utilities.NetworkHostAndPort import net.corda.node.internal.Node import net.corda.node.internal.StartedNode -import net.corda.testing.* +import net.corda.testing.core.* import net.corda.testing.node.internal.NodeBasedTest import org.junit.Before import org.junit.Test diff --git a/node/src/integration-test/kotlin/net/corda/node/services/statemachine/FlowVersioningTest.kt b/node/src/integration-test/kotlin/net/corda/node/services/statemachine/FlowVersioningTest.kt index 178f470d6c..04702e02bd 100644 --- a/node/src/integration-test/kotlin/net/corda/node/services/statemachine/FlowVersioningTest.kt +++ b/node/src/integration-test/kotlin/net/corda/node/services/statemachine/FlowVersioningTest.kt @@ -7,10 +7,11 @@ import net.corda.core.flows.InitiatingFlow import net.corda.core.identity.Party import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.unwrap -import net.corda.testing.chooseIdentity +import net.corda.testing.core.chooseIdentity import net.corda.testing.node.internal.NodeBasedTest import net.corda.testing.node.startFlow -import net.corda.testing.* +import net.corda.testing.core.ALICE_NAME +import net.corda.testing.core.BOB_NAME import org.assertj.core.api.Assertions.assertThat import org.junit.Test diff --git a/node/src/integration-test/kotlin/net/corda/node/services/statemachine/LargeTransactionsTest.kt b/node/src/integration-test/kotlin/net/corda/node/services/statemachine/LargeTransactionsTest.kt index 77f6e0c560..c49f63aaef 100644 --- a/node/src/integration-test/kotlin/net/corda/node/services/statemachine/LargeTransactionsTest.kt +++ b/node/src/integration-test/kotlin/net/corda/node/services/statemachine/LargeTransactionsTest.kt @@ -8,9 +8,9 @@ import net.corda.core.internal.concurrent.transpose import net.corda.core.messaging.startFlow import net.corda.core.transactions.TransactionBuilder import net.corda.core.utilities.getOrThrow -import net.corda.testing.* import net.corda.testing.contracts.DummyContract import net.corda.testing.contracts.DummyState +import net.corda.testing.core.* import net.corda.testing.driver.driver import net.corda.testing.node.User import org.junit.Test diff --git a/node/src/integration-test/kotlin/net/corda/node/utilities/registration/NodeRegistrationTest.kt b/node/src/integration-test/kotlin/net/corda/node/utilities/registration/NodeRegistrationTest.kt index 7b77464ed9..b9af17dcb9 100644 --- a/node/src/integration-test/kotlin/net/corda/node/utilities/registration/NodeRegistrationTest.kt +++ b/node/src/integration-test/kotlin/net/corda/node/utilities/registration/NodeRegistrationTest.kt @@ -17,15 +17,15 @@ import net.corda.nodeapi.internal.crypto.X509Utilities import net.corda.nodeapi.internal.crypto.X509Utilities.CORDA_CLIENT_CA import net.corda.nodeapi.internal.crypto.X509Utilities.CORDA_INTERMEDIATE_CA import net.corda.nodeapi.internal.crypto.X509Utilities.CORDA_ROOT_CA -import net.corda.testing.DEV_ROOT_CA -import net.corda.testing.SerializationEnvironmentRule +import net.corda.testing.core.DEV_ROOT_CA +import net.corda.testing.core.SerializationEnvironmentRule import net.corda.testing.common.internal.testNetworkParameters import net.corda.testing.driver.PortAllocation import net.corda.testing.node.NotarySpec import net.corda.testing.node.internal.CompatibilityZoneParams import net.corda.testing.node.internal.internalDriver import net.corda.testing.node.internal.network.NetworkMapServer -import net.corda.testing.singleIdentity +import net.corda.testing.core.singleIdentity import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThatThrownBy import org.bouncycastle.pkcs.PKCS10CertificationRequest diff --git a/node/src/integration-test/kotlin/net/corda/services/messaging/MQSecurityTest.kt b/node/src/integration-test/kotlin/net/corda/services/messaging/MQSecurityTest.kt index 1ac529b462..86b71bebfd 100644 --- a/node/src/integration-test/kotlin/net/corda/services/messaging/MQSecurityTest.kt +++ b/node/src/integration-test/kotlin/net/corda/services/messaging/MQSecurityTest.kt @@ -24,10 +24,10 @@ import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.NOTIFICATI import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.P2P_PREFIX import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.PEERS_PREFIX import net.corda.nodeapi.internal.config.SSLConfiguration -import net.corda.testing.ALICE_NAME -import net.corda.testing.BOB_NAME +import net.corda.testing.core.ALICE_NAME +import net.corda.testing.core.BOB_NAME import net.corda.testing.node.User -import net.corda.testing.chooseIdentity +import net.corda.testing.core.chooseIdentity import net.corda.testing.internal.configureTestSSL import net.corda.testing.node.internal.NodeBasedTest import net.corda.testing.node.startFlow diff --git a/node/src/integration-test/kotlin/net/corda/services/messaging/P2PMessagingTest.kt b/node/src/integration-test/kotlin/net/corda/services/messaging/P2PMessagingTest.kt index 4e7035957d..987277597d 100644 --- a/node/src/integration-test/kotlin/net/corda/services/messaging/P2PMessagingTest.kt +++ b/node/src/integration-test/kotlin/net/corda/services/messaging/P2PMessagingTest.kt @@ -15,8 +15,8 @@ import net.corda.core.utilities.seconds import net.corda.node.internal.Node import net.corda.node.internal.StartedNode import net.corda.node.services.messaging.* -import net.corda.testing.ALICE_NAME -import net.corda.testing.chooseIdentity +import net.corda.testing.core.ALICE_NAME +import net.corda.testing.core.chooseIdentity import net.corda.testing.driver.DriverDSL import net.corda.testing.driver.NodeHandle import net.corda.testing.driver.driver diff --git a/node/src/integration-test/kotlin/net/corda/test/node/NodeStartAndStopTest.kt b/node/src/integration-test/kotlin/net/corda/test/node/NodeStartAndStopTest.kt index 73e041485c..310d653926 100644 --- a/node/src/integration-test/kotlin/net/corda/test/node/NodeStartAndStopTest.kt +++ b/node/src/integration-test/kotlin/net/corda/test/node/NodeStartAndStopTest.kt @@ -1,7 +1,7 @@ package net.corda.test.node import net.corda.core.utilities.getOrThrow -import net.corda.testing.ALICE_NAME +import net.corda.testing.core.ALICE_NAME import net.corda.testing.node.internal.NodeBasedTest import org.junit.Test diff --git a/node/src/integration-test/kotlin/net/corda/test/node/NodeStatePersistenceTests.kt b/node/src/integration-test/kotlin/net/corda/test/node/NodeStatePersistenceTests.kt index bbd0d7c209..f9d84eee28 100644 --- a/node/src/integration-test/kotlin/net/corda/test/node/NodeStatePersistenceTests.kt +++ b/node/src/integration-test/kotlin/net/corda/test/node/NodeStatePersistenceTests.kt @@ -20,7 +20,7 @@ import net.corda.core.utilities.getOrThrow import net.corda.node.services.Permissions.Companion.invokeRpc import net.corda.node.services.Permissions.Companion.startFlow import net.corda.testing.node.User -import net.corda.testing.chooseIdentity +import net.corda.testing.core.chooseIdentity import net.corda.testing.driver.driver import org.junit.Assume.assumeFalse import org.junit.Test diff --git a/node/src/test/java/net/corda/node/services/vault/VaultQueryJavaTests.java b/node/src/test/java/net/corda/node/services/vault/VaultQueryJavaTests.java index 9ab2c4a2b4..2729af12f7 100644 --- a/node/src/test/java/net/corda/node/services/vault/VaultQueryJavaTests.java +++ b/node/src/test/java/net/corda/node/services/vault/VaultQueryJavaTests.java @@ -22,8 +22,8 @@ import net.corda.finance.schemas.CashSchemaV1; import net.corda.node.services.api.IdentityServiceInternal; import net.corda.nodeapi.internal.persistence.CordaPersistence; import net.corda.nodeapi.internal.persistence.DatabaseTransaction; -import net.corda.testing.SerializationEnvironmentRule; -import net.corda.testing.TestIdentity; +import net.corda.testing.core.SerializationEnvironmentRule; +import net.corda.testing.core.TestIdentity; import net.corda.testing.internal.vault.DummyLinearContract; import net.corda.testing.internal.vault.VaultFiller; import net.corda.testing.node.MockServices; @@ -46,9 +46,9 @@ import static net.corda.core.node.services.vault.QueryCriteriaUtils.DEFAULT_PAGE import static net.corda.core.node.services.vault.QueryCriteriaUtils.MAX_PAGE_SIZE; import static net.corda.core.utilities.ByteArrays.toHexString; import static net.corda.testing.internal.InternalTestUtilsKt.rigorousMock; -import static net.corda.testing.TestConstants.BOC_NAME; -import static net.corda.testing.TestConstants.CHARLIE_NAME; -import static net.corda.testing.TestConstants.DUMMY_NOTARY_NAME; +import static net.corda.testing.core.TestConstants.BOC_NAME; +import static net.corda.testing.core.TestConstants.CHARLIE_NAME; +import static net.corda.testing.core.TestConstants.DUMMY_NOTARY_NAME; import static net.corda.testing.node.MockServices.makeTestDatabaseAndMockServices; import static net.corda.testing.node.MockServicesKt.makeTestIdentityService; import static org.assertj.core.api.Assertions.assertThat; diff --git a/node/src/test/kotlin/net/corda/node/CordaRPCOpsImplTest.kt b/node/src/test/kotlin/net/corda/node/CordaRPCOpsImplTest.kt index ec6086fb7e..0214d551f9 100644 --- a/node/src/test/kotlin/net/corda/node/CordaRPCOpsImplTest.kt +++ b/node/src/test/kotlin/net/corda/node/CordaRPCOpsImplTest.kt @@ -33,7 +33,10 @@ import net.corda.node.services.Permissions.Companion.startFlow import net.corda.node.services.messaging.CURRENT_RPC_CONTEXT import net.corda.node.services.messaging.RpcAuthContext import net.corda.nodeapi.internal.config.User -import net.corda.testing.* +import net.corda.testing.core.ALICE_NAME +import net.corda.testing.core.expect +import net.corda.testing.core.expectEvents +import net.corda.testing.core.sequence import net.corda.testing.node.MockNetwork import net.corda.testing.node.MockNetwork.MockNode import net.corda.testing.node.MockNodeParameters diff --git a/node/src/test/kotlin/net/corda/node/InteractiveShellTest.kt b/node/src/test/kotlin/net/corda/node/InteractiveShellTest.kt index eb3e5ba532..79a6769d40 100644 --- a/node/src/test/kotlin/net/corda/node/InteractiveShellTest.kt +++ b/node/src/test/kotlin/net/corda/node/InteractiveShellTest.kt @@ -14,7 +14,7 @@ import net.corda.core.utilities.ProgressTracker import net.corda.nodeapi.internal.persistence.DatabaseConfig import net.corda.node.shell.InteractiveShell import net.corda.node.internal.configureDatabase -import net.corda.testing.TestIdentity +import net.corda.testing.core.TestIdentity import net.corda.testing.node.MockServices import net.corda.testing.node.makeTestIdentityService import net.corda.testing.internal.rigorousMock diff --git a/node/src/test/kotlin/net/corda/node/internal/NetworkParametersTest.kt b/node/src/test/kotlin/net/corda/node/internal/NetworkParametersTest.kt index 0b9e42640c..42bdc9e0c8 100644 --- a/node/src/test/kotlin/net/corda/node/internal/NetworkParametersTest.kt +++ b/node/src/test/kotlin/net/corda/node/internal/NetworkParametersTest.kt @@ -10,10 +10,10 @@ import net.corda.node.services.config.NotaryConfig import net.corda.nodeapi.internal.network.NetworkParameters import net.corda.nodeapi.internal.network.NetworkParametersCopier import net.corda.nodeapi.internal.network.NotaryInfo -import net.corda.testing.ALICE_NAME -import net.corda.testing.BOB_NAME -import net.corda.testing.DUMMY_NOTARY_NAME -import net.corda.testing.chooseIdentity +import net.corda.testing.core.ALICE_NAME +import net.corda.testing.core.BOB_NAME +import net.corda.testing.core.DUMMY_NOTARY_NAME +import net.corda.testing.core.chooseIdentity import net.corda.testing.common.internal.testNetworkParameters import net.corda.testing.node.* import org.assertj.core.api.Assertions.assertThat diff --git a/node/src/test/kotlin/net/corda/node/messaging/TwoPartyTradeFlowTests.kt b/node/src/test/kotlin/net/corda/node/messaging/TwoPartyTradeFlowTests.kt index f61f34f0ef..c5d1922a29 100644 --- a/node/src/test/kotlin/net/corda/node/messaging/TwoPartyTradeFlowTests.kt +++ b/node/src/test/kotlin/net/corda/node/messaging/TwoPartyTradeFlowTests.kt @@ -40,7 +40,7 @@ import net.corda.node.services.api.IdentityServiceInternal import net.corda.node.services.persistence.DBTransactionStorage import net.corda.node.services.persistence.checkpoints import net.corda.nodeapi.internal.persistence.CordaPersistence -import net.corda.testing.* +import net.corda.testing.core.* import net.corda.testing.internal.LogHelper import net.corda.testing.dsl.LedgerDSL import net.corda.testing.dsl.TestLedgerDSLInterpreter diff --git a/node/src/test/kotlin/net/corda/node/services/NotaryChangeTests.kt b/node/src/test/kotlin/net/corda/node/services/NotaryChangeTests.kt index a2b321391d..4f1292cf80 100644 --- a/node/src/test/kotlin/net/corda/node/services/NotaryChangeTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/NotaryChangeTests.kt @@ -13,8 +13,8 @@ import net.corda.core.transactions.WireTransaction import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.seconds import net.corda.node.internal.StartedNode -import net.corda.testing.* import net.corda.testing.contracts.DummyContract +import net.corda.testing.core.* import net.corda.testing.node.MockNetwork import net.corda.testing.node.MockNetwork.NotarySpec import net.corda.testing.node.MockNodeParameters diff --git a/node/src/test/kotlin/net/corda/node/services/config/NodeConfigurationImplTest.kt b/node/src/test/kotlin/net/corda/node/services/config/NodeConfigurationImplTest.kt index 1b6f20fa81..73067bc769 100644 --- a/node/src/test/kotlin/net/corda/node/services/config/NodeConfigurationImplTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/config/NodeConfigurationImplTest.kt @@ -1,7 +1,7 @@ package net.corda.node.services.config import net.corda.core.utilities.NetworkHostAndPort -import net.corda.testing.ALICE_NAME +import net.corda.testing.core.ALICE_NAME import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties import org.assertj.core.api.Assertions.assertThatThrownBy import org.junit.Test diff --git a/node/src/test/kotlin/net/corda/node/services/events/ScheduledFlowTests.kt b/node/src/test/kotlin/net/corda/node/services/events/ScheduledFlowTests.kt index 600d99e50c..4ff69ccbb2 100644 --- a/node/src/test/kotlin/net/corda/node/services/events/ScheduledFlowTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/events/ScheduledFlowTests.kt @@ -20,8 +20,11 @@ import net.corda.core.transactions.TransactionBuilder import net.corda.core.utilities.getOrThrow import net.corda.node.internal.StartedNode import net.corda.node.services.statemachine.StateMachineManager -import net.corda.testing.* import net.corda.testing.contracts.DummyContract +import net.corda.testing.core.ALICE_NAME +import net.corda.testing.core.BOB_NAME +import net.corda.testing.core.dummyCommand +import net.corda.testing.core.singleIdentity import net.corda.testing.node.MockNetwork import net.corda.testing.node.MockNodeParameters import net.corda.testing.node.startFlow diff --git a/node/src/test/kotlin/net/corda/node/services/identity/InMemoryIdentityServiceTests.kt b/node/src/test/kotlin/net/corda/node/services/identity/InMemoryIdentityServiceTests.kt index 9131143e9f..91c216f12d 100644 --- a/node/src/test/kotlin/net/corda/node/services/identity/InMemoryIdentityServiceTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/identity/InMemoryIdentityServiceTests.kt @@ -10,7 +10,7 @@ import net.corda.core.node.services.UnknownAnonymousPartyException import net.corda.nodeapi.internal.crypto.CertificateType import net.corda.nodeapi.internal.crypto.X509CertificateFactory import net.corda.nodeapi.internal.crypto.X509Utilities -import net.corda.testing.* +import net.corda.testing.core.* import org.junit.Rule import org.junit.Test import kotlin.test.assertEquals diff --git a/node/src/test/kotlin/net/corda/node/services/identity/PersistentIdentityServiceTests.kt b/node/src/test/kotlin/net/corda/node/services/identity/PersistentIdentityServiceTests.kt index 5d6cc41ce1..7e3a062885 100644 --- a/node/src/test/kotlin/net/corda/node/services/identity/PersistentIdentityServiceTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/identity/PersistentIdentityServiceTests.kt @@ -14,7 +14,7 @@ import net.corda.nodeapi.internal.crypto.X509CertificateFactory import net.corda.nodeapi.internal.crypto.X509Utilities import net.corda.nodeapi.internal.persistence.CordaPersistence import net.corda.nodeapi.internal.persistence.DatabaseConfig -import net.corda.testing.* +import net.corda.testing.core.* import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties import net.corda.testing.node.makeTestIdentityService import org.junit.After diff --git a/node/src/test/kotlin/net/corda/node/services/keys/KMSUtilsTests.kt b/node/src/test/kotlin/net/corda/node/services/keys/KMSUtilsTests.kt index 1a84373975..b76caacba4 100644 --- a/node/src/test/kotlin/net/corda/node/services/keys/KMSUtilsTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/keys/KMSUtilsTests.kt @@ -3,7 +3,9 @@ package net.corda.node.services.keys import net.corda.core.CordaOID import net.corda.core.crypto.generateKeyPair import net.corda.core.internal.CertRole -import net.corda.testing.* +import net.corda.testing.core.ALICE_NAME +import net.corda.testing.core.getTestPartyAndCertificate +import net.corda.testing.core.singleIdentityAndCert import net.corda.testing.node.MockServices import net.corda.testing.node.makeTestIdentityService import org.bouncycastle.asn1.DEROctetString diff --git a/node/src/test/kotlin/net/corda/node/services/messaging/ArtemisMessagingTests.kt b/node/src/test/kotlin/net/corda/node/services/messaging/ArtemisMessagingTests.kt index 7a87b0e8a4..67ac413240 100644 --- a/node/src/test/kotlin/net/corda/node/services/messaging/ArtemisMessagingTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/messaging/ArtemisMessagingTests.kt @@ -17,7 +17,7 @@ import net.corda.node.services.transactions.PersistentUniquenessProvider import net.corda.node.utilities.AffinityExecutor.ServiceAffinityExecutor import net.corda.nodeapi.internal.persistence.CordaPersistence import net.corda.nodeapi.internal.persistence.DatabaseConfig -import net.corda.testing.* +import net.corda.testing.core.* import net.corda.testing.internal.LogHelper import net.corda.testing.internal.rigorousMock import net.corda.testing.node.MockServices.Companion.MOCK_VERSION_INFO diff --git a/node/src/test/kotlin/net/corda/node/services/network/NetworkMapCacheTest.kt b/node/src/test/kotlin/net/corda/node/services/network/NetworkMapCacheTest.kt index b56728fcf6..e8809bb833 100644 --- a/node/src/test/kotlin/net/corda/node/services/network/NetworkMapCacheTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/network/NetworkMapCacheTest.kt @@ -2,14 +2,13 @@ package net.corda.node.services.network import net.corda.core.crypto.generateKeyPair import net.corda.core.node.services.NetworkMapCache +import net.corda.testing.core.ALICE_NAME +import net.corda.testing.core.BOB_NAME import net.corda.node.services.api.NetworkMapCacheInternal -import net.corda.testing.ALICE_NAME -import net.corda.testing.BOB_NAME -import net.corda.testing.getTestPartyAndCertificate +import net.corda.testing.core.getTestPartyAndCertificate import net.corda.testing.node.MockNetwork import net.corda.testing.node.MockNodeParameters -import net.corda.testing.singleIdentity -import net.corda.testing.singleIdentityAndCert +import net.corda.testing.core.singleIdentity import org.assertj.core.api.Assertions.assertThat import org.junit.After import org.junit.Test diff --git a/node/src/test/kotlin/net/corda/node/services/network/NetworkMapClientTest.kt b/node/src/test/kotlin/net/corda/node/services/network/NetworkMapClientTest.kt index da4784d060..e9f791782e 100644 --- a/node/src/test/kotlin/net/corda/node/services/network/NetworkMapClientTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/network/NetworkMapClientTest.kt @@ -3,10 +3,10 @@ package net.corda.node.services.network import net.corda.core.crypto.sha256 import net.corda.core.serialization.serialize import net.corda.core.utilities.seconds -import net.corda.testing.ALICE_NAME -import net.corda.testing.BOB_NAME -import net.corda.testing.DEV_ROOT_CA -import net.corda.testing.SerializationEnvironmentRule +import net.corda.testing.core.ALICE_NAME +import net.corda.testing.core.BOB_NAME +import net.corda.testing.core.DEV_ROOT_CA +import net.corda.testing.core.SerializationEnvironmentRule import net.corda.testing.driver.PortAllocation import net.corda.testing.internal.TestNodeInfoBuilder import net.corda.testing.internal.createNodeInfoAndSigned diff --git a/node/src/test/kotlin/net/corda/node/services/network/NetworkMapUpdaterTest.kt b/node/src/test/kotlin/net/corda/node/services/network/NetworkMapUpdaterTest.kt index b76c7d59d9..1f1478eba0 100644 --- a/node/src/test/kotlin/net/corda/node/services/network/NetworkMapUpdaterTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/network/NetworkMapUpdaterTest.kt @@ -18,8 +18,8 @@ import net.corda.core.utilities.millis import net.corda.node.services.api.NetworkMapCacheInternal import net.corda.nodeapi.internal.SignedNodeInfo import net.corda.nodeapi.internal.network.NetworkMap -import net.corda.testing.ALICE_NAME -import net.corda.testing.SerializationEnvironmentRule +import net.corda.testing.core.ALICE_NAME +import net.corda.testing.core.SerializationEnvironmentRule import net.corda.testing.internal.TestNodeInfoBuilder import net.corda.testing.internal.createNodeInfoAndSigned import org.assertj.core.api.Assertions.assertThat diff --git a/node/src/test/kotlin/net/corda/node/services/persistence/DBCheckpointStorageTests.kt b/node/src/test/kotlin/net/corda/node/services/persistence/DBCheckpointStorageTests.kt index e6326128bf..7851fdc874 100644 --- a/node/src/test/kotlin/net/corda/node/services/persistence/DBCheckpointStorageTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/persistence/DBCheckpointStorageTests.kt @@ -9,7 +9,7 @@ import net.corda.node.internal.configureDatabase import net.corda.nodeapi.internal.persistence.CordaPersistence import net.corda.nodeapi.internal.persistence.DatabaseConfig import net.corda.testing.internal.LogHelper -import net.corda.testing.SerializationEnvironmentRule +import net.corda.testing.core.SerializationEnvironmentRule import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties import net.corda.testing.internal.rigorousMock import org.assertj.core.api.Assertions.assertThat diff --git a/node/src/test/kotlin/net/corda/node/services/persistence/DBTransactionStorageTests.kt b/node/src/test/kotlin/net/corda/node/services/persistence/DBTransactionStorageTests.kt index 255ce7aae9..5247788410 100644 --- a/node/src/test/kotlin/net/corda/node/services/persistence/DBTransactionStorageTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/persistence/DBTransactionStorageTests.kt @@ -13,7 +13,7 @@ import net.corda.node.internal.configureDatabase import net.corda.node.services.config.NodeConfiguration import net.corda.nodeapi.internal.persistence.CordaPersistence import net.corda.nodeapi.internal.persistence.DatabaseConfig -import net.corda.testing.* +import net.corda.testing.core.* import net.corda.testing.internal.LogHelper import net.corda.testing.internal.rigorousMock import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties diff --git a/node/src/test/kotlin/net/corda/node/services/persistence/HibernateConfigurationTest.kt b/node/src/test/kotlin/net/corda/node/services/persistence/HibernateConfigurationTest.kt index 4b6c3ab0bd..ffd8aeb02e 100644 --- a/node/src/test/kotlin/net/corda/node/services/persistence/HibernateConfigurationTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/persistence/HibernateConfigurationTest.kt @@ -37,7 +37,7 @@ import net.corda.node.services.api.IdentityServiceInternal import net.corda.nodeapi.internal.persistence.CordaPersistence import net.corda.nodeapi.internal.persistence.DatabaseConfig import net.corda.nodeapi.internal.persistence.HibernateConfiguration -import net.corda.testing.* +import net.corda.testing.core.* import net.corda.testing.internal.rigorousMock import net.corda.testing.internal.vault.VaultFiller import net.corda.testing.node.MockServices diff --git a/node/src/test/kotlin/net/corda/node/services/schema/HibernateObserverTests.kt b/node/src/test/kotlin/net/corda/node/services/schema/HibernateObserverTests.kt index 5c3349f236..95299c0723 100644 --- a/node/src/test/kotlin/net/corda/node/services/schema/HibernateObserverTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/schema/HibernateObserverTests.kt @@ -15,7 +15,7 @@ import net.corda.node.services.api.SchemaService import net.corda.node.internal.configureDatabase import net.corda.nodeapi.internal.persistence.DatabaseConfig import net.corda.testing.internal.LogHelper -import net.corda.testing.TestIdentity +import net.corda.testing.core.TestIdentity import net.corda.testing.contracts.DummyContract import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties import net.corda.testing.internal.rigorousMock diff --git a/node/src/test/kotlin/net/corda/node/services/statemachine/FlowFrameworkTests.kt b/node/src/test/kotlin/net/corda/node/services/statemachine/FlowFrameworkTests.kt index 4090d9d342..94b87c65e5 100644 --- a/node/src/test/kotlin/net/corda/node/services/statemachine/FlowFrameworkTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/statemachine/FlowFrameworkTests.kt @@ -28,9 +28,9 @@ import net.corda.core.utilities.unwrap import net.corda.node.internal.InitiatedFlowFactory import net.corda.node.internal.StartedNode import net.corda.node.services.persistence.checkpoints -import net.corda.testing.* import net.corda.testing.contracts.DummyContract import net.corda.testing.contracts.DummyState +import net.corda.testing.core.* import net.corda.testing.internal.LogHelper import net.corda.testing.node.InMemoryMessagingNetwork.MessageTransfer import net.corda.testing.node.InMemoryMessagingNetwork.ServicePeerAllocationStrategy.RoundRobin diff --git a/node/src/test/kotlin/net/corda/node/services/transactions/DistributedImmutableMapTests.kt b/node/src/test/kotlin/net/corda/node/services/transactions/DistributedImmutableMapTests.kt index 386aed020a..d8a7611cb1 100644 --- a/node/src/test/kotlin/net/corda/node/services/transactions/DistributedImmutableMapTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/transactions/DistributedImmutableMapTests.kt @@ -15,8 +15,8 @@ import net.corda.node.services.schema.NodeSchemaService import net.corda.nodeapi.internal.persistence.CordaPersistence import net.corda.nodeapi.internal.persistence.DatabaseConfig import net.corda.testing.internal.LogHelper -import net.corda.testing.SerializationEnvironmentRule -import net.corda.testing.freeLocalHostAndPort +import net.corda.testing.core.SerializationEnvironmentRule +import net.corda.testing.core.freeLocalHostAndPort import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties import net.corda.testing.internal.rigorousMock import org.junit.After diff --git a/node/src/test/kotlin/net/corda/node/services/transactions/NotaryServiceTests.kt b/node/src/test/kotlin/net/corda/node/services/transactions/NotaryServiceTests.kt index a162c77a54..a60bd303cb 100644 --- a/node/src/test/kotlin/net/corda/node/services/transactions/NotaryServiceTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/transactions/NotaryServiceTests.kt @@ -14,13 +14,13 @@ import net.corda.core.transactions.TransactionBuilder import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.seconds import net.corda.node.services.api.StartedNodeServices -import net.corda.testing.ALICE_NAME +import net.corda.testing.core.ALICE_NAME import net.corda.testing.contracts.DummyContract -import net.corda.testing.dummyCommand +import net.corda.testing.core.dummyCommand import net.corda.testing.node.MockNetwork import net.corda.testing.node.MockNodeParameters import net.corda.testing.node.startFlow -import net.corda.testing.singleIdentity +import net.corda.testing.core.singleIdentity import org.assertj.core.api.Assertions.assertThat import org.junit.After import org.junit.Before diff --git a/node/src/test/kotlin/net/corda/node/services/transactions/PersistentUniquenessProviderTests.kt b/node/src/test/kotlin/net/corda/node/services/transactions/PersistentUniquenessProviderTests.kt index 55f1e089a6..071c184e99 100644 --- a/node/src/test/kotlin/net/corda/node/services/transactions/PersistentUniquenessProviderTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/transactions/PersistentUniquenessProviderTests.kt @@ -7,7 +7,9 @@ import net.corda.node.internal.configureDatabase import net.corda.node.services.schema.NodeSchemaService import net.corda.nodeapi.internal.persistence.CordaPersistence import net.corda.nodeapi.internal.persistence.DatabaseConfig -import net.corda.testing.* +import net.corda.testing.core.SerializationEnvironmentRule +import net.corda.testing.core.TestIdentity +import net.corda.testing.core.generateStateRef import net.corda.testing.internal.LogHelper import net.corda.testing.internal.rigorousMock import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties diff --git a/node/src/test/kotlin/net/corda/node/services/transactions/ValidatingNotaryServiceTests.kt b/node/src/test/kotlin/net/corda/node/services/transactions/ValidatingNotaryServiceTests.kt index ad56bfa5bc..5a28634624 100644 --- a/node/src/test/kotlin/net/corda/node/services/transactions/ValidatingNotaryServiceTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/transactions/ValidatingNotaryServiceTests.kt @@ -16,8 +16,10 @@ import net.corda.core.transactions.TransactionBuilder import net.corda.core.utilities.getOrThrow import net.corda.node.services.api.StartedNodeServices import net.corda.node.services.issueInvalidState -import net.corda.testing.* import net.corda.testing.contracts.DummyContract +import net.corda.testing.core.ALICE_NAME +import net.corda.testing.core.dummyCommand +import net.corda.testing.core.singleIdentity import net.corda.testing.node.MockNetwork import net.corda.testing.node.MockNodeParameters import net.corda.testing.node.startFlow diff --git a/node/src/test/kotlin/net/corda/node/services/vault/NodeVaultServiceTest.kt b/node/src/test/kotlin/net/corda/node/services/vault/NodeVaultServiceTest.kt index bb7a01c77a..b7ecaa8fbf 100644 --- a/node/src/test/kotlin/net/corda/node/services/vault/NodeVaultServiceTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/vault/NodeVaultServiceTest.kt @@ -30,7 +30,7 @@ import net.corda.finance.schemas.CashSchemaV1 import net.corda.finance.utils.sumCash import net.corda.node.services.api.IdentityServiceInternal import net.corda.nodeapi.internal.persistence.CordaPersistence -import net.corda.testing.* +import net.corda.testing.core.* import net.corda.testing.internal.LogHelper import net.corda.testing.internal.rigorousMock import net.corda.testing.internal.vault.VaultFiller diff --git a/node/src/test/kotlin/net/corda/node/services/vault/VaultQueryTests.kt b/node/src/test/kotlin/net/corda/node/services/vault/VaultQueryTests.kt index d29c3e3ca0..db72541f8b 100644 --- a/node/src/test/kotlin/net/corda/node/services/vault/VaultQueryTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/vault/VaultQueryTests.kt @@ -25,7 +25,7 @@ import net.corda.finance.schemas.SampleCashSchemaV3 import net.corda.node.internal.configureDatabase import net.corda.nodeapi.internal.persistence.CordaPersistence import net.corda.nodeapi.internal.persistence.DatabaseConfig -import net.corda.testing.* +import net.corda.testing.core.* import net.corda.testing.internal.rigorousMock import net.corda.testing.internal.vault.DUMMY_LINEAR_CONTRACT_PROGRAM_ID import net.corda.testing.internal.vault.DummyLinearContract diff --git a/node/src/test/kotlin/net/corda/node/services/vault/VaultSoftLockManagerTest.kt b/node/src/test/kotlin/net/corda/node/services/vault/VaultSoftLockManagerTest.kt index f55943c9f6..c165ab7ef9 100644 --- a/node/src/test/kotlin/net/corda/node/services/vault/VaultSoftLockManagerTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/vault/VaultSoftLockManagerTest.kt @@ -26,7 +26,7 @@ import net.corda.core.utilities.unwrap import net.corda.node.internal.InitiatedFlowFactory import net.corda.node.services.api.VaultServiceInternal import net.corda.nodeapi.internal.persistence.HibernateConfiguration -import net.corda.testing.chooseIdentity +import net.corda.testing.core.chooseIdentity import net.corda.testing.node.MockNetwork import net.corda.testing.internal.rigorousMock import net.corda.testing.node.MockNodeParameters diff --git a/node/src/test/kotlin/net/corda/node/services/vault/VaultWithCashTest.kt b/node/src/test/kotlin/net/corda/node/services/vault/VaultWithCashTest.kt index cac2e8db20..19772b9193 100644 --- a/node/src/test/kotlin/net/corda/node/services/vault/VaultWithCashTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/vault/VaultWithCashTest.kt @@ -20,7 +20,7 @@ import net.corda.finance.contracts.asset.Cash import net.corda.finance.contracts.getCashBalance import net.corda.finance.schemas.CashSchemaV1 import net.corda.nodeapi.internal.persistence.CordaPersistence -import net.corda.testing.* +import net.corda.testing.core.* import net.corda.testing.internal.LogHelper import net.corda.testing.internal.rigorousMock import net.corda.testing.internal.vault.* diff --git a/node/src/test/kotlin/net/corda/node/utilities/registration/NetworkRegistrationHelperTest.kt b/node/src/test/kotlin/net/corda/node/utilities/registration/NetworkRegistrationHelperTest.kt index f56c82a6cf..4ce5df1eb4 100644 --- a/node/src/test/kotlin/net/corda/node/utilities/registration/NetworkRegistrationHelperTest.kt +++ b/node/src/test/kotlin/net/corda/node/utilities/registration/NetworkRegistrationHelperTest.kt @@ -13,7 +13,7 @@ import net.corda.core.internal.createDirectories import net.corda.core.internal.x500Name import net.corda.node.services.config.NodeConfiguration import net.corda.nodeapi.internal.crypto.* -import net.corda.testing.ALICE_NAME +import net.corda.testing.core.ALICE_NAME import net.corda.testing.internal.createDevIntermediateCaCertPath import net.corda.testing.internal.rigorousMock import org.assertj.core.api.Assertions.* diff --git a/samples/attachment-demo/src/integration-test/kotlin/net/corda/attachmentdemo/AttachmentDemoTest.kt b/samples/attachment-demo/src/integration-test/kotlin/net/corda/attachmentdemo/AttachmentDemoTest.kt index 0384710236..637efa9d55 100644 --- a/samples/attachment-demo/src/integration-test/kotlin/net/corda/attachmentdemo/AttachmentDemoTest.kt +++ b/samples/attachment-demo/src/integration-test/kotlin/net/corda/attachmentdemo/AttachmentDemoTest.kt @@ -4,8 +4,8 @@ import net.corda.core.messaging.CordaRPCOps import net.corda.core.utilities.getOrThrow import net.corda.node.services.Permissions.Companion.invokeRpc import net.corda.node.services.Permissions.Companion.startFlow -import net.corda.testing.DUMMY_BANK_A_NAME -import net.corda.testing.DUMMY_BANK_B_NAME +import net.corda.testing.core.DUMMY_BANK_A_NAME +import net.corda.testing.core.DUMMY_BANK_B_NAME import net.corda.testing.node.User import net.corda.testing.driver.PortAllocation import net.corda.testing.driver.driver diff --git a/samples/attachment-demo/src/main/kotlin/net/corda/attachmentdemo/AttachmentDemo.kt b/samples/attachment-demo/src/main/kotlin/net/corda/attachmentdemo/AttachmentDemo.kt index 38e051f1b6..a6a49cc01a 100644 --- a/samples/attachment-demo/src/main/kotlin/net/corda/attachmentdemo/AttachmentDemo.kt +++ b/samples/attachment-demo/src/main/kotlin/net/corda/attachmentdemo/AttachmentDemo.kt @@ -24,8 +24,8 @@ import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.ProgressTracker import net.corda.core.utilities.getOrThrow import net.corda.testing.node.internal.poll -import net.corda.testing.DUMMY_BANK_B_NAME -import net.corda.testing.DUMMY_NOTARY_NAME +import net.corda.testing.core.DUMMY_BANK_B_NAME +import net.corda.testing.core.DUMMY_NOTARY_NAME import java.io.InputStream import java.net.HttpURLConnection import java.net.URL diff --git a/samples/attachment-demo/src/main/kotlin/net/corda/attachmentdemo/Main.kt b/samples/attachment-demo/src/main/kotlin/net/corda/attachmentdemo/Main.kt index 44108c93c8..6c3c11e9c1 100644 --- a/samples/attachment-demo/src/main/kotlin/net/corda/attachmentdemo/Main.kt +++ b/samples/attachment-demo/src/main/kotlin/net/corda/attachmentdemo/Main.kt @@ -1,8 +1,8 @@ package net.corda.attachmentdemo import net.corda.core.internal.div -import net.corda.testing.DUMMY_BANK_A_NAME -import net.corda.testing.DUMMY_BANK_B_NAME +import net.corda.testing.core.DUMMY_BANK_A_NAME +import net.corda.testing.core.DUMMY_BANK_B_NAME import net.corda.testing.node.User import net.corda.testing.driver.driver diff --git a/samples/bank-of-corda-demo/src/integration-test/kotlin/net/corda/bank/BankOfCordaRPCClientTest.kt b/samples/bank-of-corda-demo/src/integration-test/kotlin/net/corda/bank/BankOfCordaRPCClientTest.kt index 27177358b2..dfa5199b13 100644 --- a/samples/bank-of-corda-demo/src/integration-test/kotlin/net/corda/bank/BankOfCordaRPCClientTest.kt +++ b/samples/bank-of-corda-demo/src/integration-test/kotlin/net/corda/bank/BankOfCordaRPCClientTest.kt @@ -11,7 +11,10 @@ import net.corda.finance.contracts.asset.Cash import net.corda.finance.flows.CashIssueAndPaymentFlow import net.corda.node.services.Permissions.Companion.invokeRpc import net.corda.node.services.Permissions.Companion.startFlow -import net.corda.testing.* +import net.corda.testing.core.BOC_NAME +import net.corda.testing.core.expect +import net.corda.testing.core.expectEvents +import net.corda.testing.core.sequence import net.corda.testing.driver.driver import net.corda.testing.node.User import org.junit.Test diff --git a/samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/BankOfCordaCordform.kt b/samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/BankOfCordaCordform.kt index 9e7b1dae83..61609835d0 100644 --- a/samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/BankOfCordaCordform.kt +++ b/samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/BankOfCordaCordform.kt @@ -13,7 +13,7 @@ import net.corda.core.utilities.NetworkHostAndPort import net.corda.node.services.Permissions.Companion.all import net.corda.node.services.config.NotaryConfig import net.corda.testing.node.internal.demorun.* -import net.corda.testing.BOC_NAME +import net.corda.testing.core.BOC_NAME import net.corda.testing.node.User import java.util.* import kotlin.system.exitProcess diff --git a/samples/irs-demo/cordapp/src/test/kotlin/net/corda/irs/Main.kt b/samples/irs-demo/cordapp/src/test/kotlin/net/corda/irs/Main.kt index 976126a8c8..8614e26249 100644 --- a/samples/irs-demo/cordapp/src/test/kotlin/net/corda/irs/Main.kt +++ b/samples/irs-demo/cordapp/src/test/kotlin/net/corda/irs/Main.kt @@ -1,8 +1,8 @@ package net.corda.irs import net.corda.core.utilities.getOrThrow -import net.corda.testing.DUMMY_BANK_A_NAME -import net.corda.testing.DUMMY_BANK_B_NAME +import net.corda.testing.core.DUMMY_BANK_A_NAME +import net.corda.testing.core.DUMMY_BANK_B_NAME import net.corda.testing.driver.driver /** diff --git a/samples/irs-demo/cordapp/src/test/kotlin/net/corda/irs/api/NodeInterestRatesTest.kt b/samples/irs-demo/cordapp/src/test/kotlin/net/corda/irs/api/NodeInterestRatesTest.kt index 1cbbadd253..1be2b70c09 100644 --- a/samples/irs-demo/cordapp/src/test/kotlin/net/corda/irs/api/NodeInterestRatesTest.kt +++ b/samples/irs-demo/cordapp/src/test/kotlin/net/corda/irs/api/NodeInterestRatesTest.kt @@ -18,7 +18,7 @@ import net.corda.irs.flows.RatesFixFlow import net.corda.node.internal.configureDatabase import net.corda.nodeapi.internal.persistence.CordaPersistence import net.corda.nodeapi.internal.persistence.DatabaseConfig -import net.corda.testing.* +import net.corda.testing.core.* import net.corda.testing.internal.withoutTestSerialization import net.corda.testing.internal.LogHelper import net.corda.testing.internal.rigorousMock diff --git a/samples/irs-demo/cordapp/src/test/kotlin/net/corda/irs/contract/IRSTests.kt b/samples/irs-demo/cordapp/src/test/kotlin/net/corda/irs/contract/IRSTests.kt index b0d72e4917..7234ee5d72 100644 --- a/samples/irs-demo/cordapp/src/test/kotlin/net/corda/irs/contract/IRSTests.kt +++ b/samples/irs-demo/cordapp/src/test/kotlin/net/corda/irs/contract/IRSTests.kt @@ -24,7 +24,10 @@ import net.corda.finance.contracts.Frequency import net.corda.finance.contracts.PaymentRule import net.corda.finance.contracts.Tenor import net.corda.node.services.api.IdentityServiceInternal -import net.corda.testing.* +import net.corda.testing.core.DUMMY_NOTARY_NAME +import net.corda.testing.core.SerializationEnvironmentRule +import net.corda.testing.core.TEST_TX_TIME +import net.corda.testing.core.TestIdentity import net.corda.testing.dsl.* import net.corda.testing.internal.rigorousMock import net.corda.testing.node.MockServices diff --git a/samples/irs-demo/src/integration-test/kotlin/net/corda/irs/IRSDemoTest.kt b/samples/irs-demo/src/integration-test/kotlin/net/corda/irs/IRSDemoTest.kt index f80342e918..192e3379c1 100644 --- a/samples/irs-demo/src/integration-test/kotlin/net/corda/irs/IRSDemoTest.kt +++ b/samples/irs-demo/src/integration-test/kotlin/net/corda/irs/IRSDemoTest.kt @@ -22,7 +22,10 @@ import net.corda.irs.contract.InterestRateSwap import net.corda.irs.web.IrsDemoWebApplication import net.corda.node.services.config.NodeConfiguration import net.corda.test.spring.springDriver -import net.corda.testing.* +import net.corda.testing.core.DUMMY_BANK_A_NAME +import net.corda.testing.core.DUMMY_BANK_B_NAME +import net.corda.testing.core.DUMMY_NOTARY_NAME +import net.corda.testing.core.chooseIdentity import net.corda.testing.http.HttpApi import net.corda.testing.node.NotarySpec import net.corda.testing.node.User diff --git a/samples/network-visualiser/src/main/kotlin/net/corda/netmap/NetworkMapVisualiser.kt b/samples/network-visualiser/src/main/kotlin/net/corda/netmap/NetworkMapVisualiser.kt index 3411eb5d0d..c96b08c2f2 100644 --- a/samples/network-visualiser/src/main/kotlin/net/corda/netmap/NetworkMapVisualiser.kt +++ b/samples/network-visualiser/src/main/kotlin/net/corda/netmap/NetworkMapVisualiser.kt @@ -18,7 +18,7 @@ import net.corda.netmap.simulation.IRSSimulation import net.corda.node.services.statemachine.SessionConfirm import net.corda.node.services.statemachine.SessionEnd import net.corda.node.services.statemachine.SessionInit -import net.corda.testing.chooseIdentity +import net.corda.testing.core.chooseIdentity import net.corda.testing.node.InMemoryMessagingNetwork import net.corda.testing.node.MockNetwork import rx.Scheduler diff --git a/samples/network-visualiser/src/main/kotlin/net/corda/netmap/VisualiserViewModel.kt b/samples/network-visualiser/src/main/kotlin/net/corda/netmap/VisualiserViewModel.kt index eda67a0277..cb4fa0c488 100644 --- a/samples/network-visualiser/src/main/kotlin/net/corda/netmap/VisualiserViewModel.kt +++ b/samples/network-visualiser/src/main/kotlin/net/corda/netmap/VisualiserViewModel.kt @@ -12,7 +12,7 @@ import net.corda.core.utilities.ProgressTracker import net.corda.finance.utils.ScreenCoordinate import net.corda.netmap.simulation.IRSSimulation import net.corda.netmap.simulation.place -import net.corda.testing.chooseIdentity +import net.corda.testing.core.chooseIdentity import net.corda.testing.node.MockNetwork import java.util.* diff --git a/samples/network-visualiser/src/main/kotlin/net/corda/netmap/simulation/IRSSimulation.kt b/samples/network-visualiser/src/main/kotlin/net/corda/netmap/simulation/IRSSimulation.kt index df5bcb95a8..f651fc8e4e 100644 --- a/samples/network-visualiser/src/main/kotlin/net/corda/netmap/simulation/IRSSimulation.kt +++ b/samples/network-visualiser/src/main/kotlin/net/corda/netmap/simulation/IRSSimulation.kt @@ -21,7 +21,7 @@ import net.corda.finance.flows.TwoPartyDealFlow.Instigator import net.corda.finance.plugin.registerFinanceJSONMappers import net.corda.irs.contract.InterestRateSwap import net.corda.irs.flows.FixingFlow -import net.corda.testing.chooseIdentity +import net.corda.testing.core.chooseIdentity import net.corda.testing.node.InMemoryMessagingNetwork import net.corda.testing.node.makeTestIdentityService import net.corda.testing.node.startFlow diff --git a/samples/network-visualiser/src/main/kotlin/net/corda/netmap/simulation/Simulation.kt b/samples/network-visualiser/src/main/kotlin/net/corda/netmap/simulation/Simulation.kt index 50a7f2b356..8fafa86192 100644 --- a/samples/network-visualiser/src/main/kotlin/net/corda/netmap/simulation/Simulation.kt +++ b/samples/network-visualiser/src/main/kotlin/net/corda/netmap/simulation/Simulation.kt @@ -9,7 +9,7 @@ import net.corda.finance.utils.CityDatabase import net.corda.irs.api.NodeInterestRates import net.corda.node.internal.StartedNode import net.corda.node.services.statemachine.StateMachineManager -import net.corda.testing.TestIdentity +import net.corda.testing.core.TestIdentity import net.corda.testing.node.* import net.corda.testing.node.MockNetwork.MockNode import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties diff --git a/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/BFTNotaryCordform.kt b/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/BFTNotaryCordform.kt index 8e4028956f..8475571bfc 100644 --- a/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/BFTNotaryCordform.kt +++ b/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/BFTNotaryCordform.kt @@ -10,8 +10,8 @@ import net.corda.node.services.config.NotaryConfig import net.corda.node.services.transactions.minCorrectReplicas import net.corda.nodeapi.internal.DevIdentityGenerator import net.corda.testing.node.internal.demorun.* -import net.corda.testing.ALICE_NAME -import net.corda.testing.BOB_NAME +import net.corda.testing.core.ALICE_NAME +import net.corda.testing.core.BOB_NAME import java.nio.file.Paths fun main(args: Array) = BFTNotaryCordform().deployNodes() diff --git a/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/CustomNotaryCordform.kt b/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/CustomNotaryCordform.kt index 91dc8c5f39..8f04c33c3b 100644 --- a/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/CustomNotaryCordform.kt +++ b/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/CustomNotaryCordform.kt @@ -4,7 +4,9 @@ import net.corda.cordform.CordformContext import net.corda.cordform.CordformDefinition import net.corda.node.services.config.NotaryConfig import net.corda.testing.node.internal.demorun.* -import net.corda.testing.* +import net.corda.testing.core.ALICE_NAME +import net.corda.testing.core.BOB_NAME +import net.corda.testing.core.DUMMY_NOTARY_NAME import java.nio.file.Paths fun main(args: Array) = CustomNotaryCordform().deployNodes() diff --git a/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/Notarise.kt b/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/Notarise.kt index a86a6be0ea..25a320a1a9 100644 --- a/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/Notarise.kt +++ b/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/Notarise.kt @@ -11,7 +11,7 @@ import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.getOrThrow import net.corda.notarydemo.flows.DummyIssueAndMove import net.corda.notarydemo.flows.RPCStartableNotaryFlowClient -import net.corda.testing.BOB_NAME +import net.corda.testing.core.BOB_NAME import java.util.concurrent.Future fun main(args: Array) { diff --git a/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/RaftNotaryCordform.kt b/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/RaftNotaryCordform.kt index f999ad68e6..d6898321aa 100644 --- a/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/RaftNotaryCordform.kt +++ b/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/RaftNotaryCordform.kt @@ -9,8 +9,8 @@ import net.corda.node.services.config.NotaryConfig import net.corda.node.services.config.RaftConfig import net.corda.nodeapi.internal.DevIdentityGenerator import net.corda.testing.node.internal.demorun.* -import net.corda.testing.ALICE_NAME -import net.corda.testing.BOB_NAME +import net.corda.testing.core.ALICE_NAME +import net.corda.testing.core.BOB_NAME import java.nio.file.Paths fun main(args: Array) = RaftNotaryCordform().deployNodes() diff --git a/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/SingleNotaryCordform.kt b/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/SingleNotaryCordform.kt index 8386ccece9..a7a64d2816 100644 --- a/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/SingleNotaryCordform.kt +++ b/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/SingleNotaryCordform.kt @@ -4,9 +4,9 @@ import net.corda.cordform.CordformContext import net.corda.cordform.CordformDefinition import net.corda.node.services.Permissions.Companion.all import net.corda.node.services.config.NotaryConfig -import net.corda.testing.ALICE_NAME -import net.corda.testing.BOB_NAME -import net.corda.testing.DUMMY_NOTARY_NAME +import net.corda.testing.core.ALICE_NAME +import net.corda.testing.core.BOB_NAME +import net.corda.testing.core.DUMMY_NOTARY_NAME import net.corda.testing.node.User import net.corda.testing.node.internal.demorun.* import java.nio.file.Paths diff --git a/samples/simm-valuation-demo/src/integration-test/kotlin/net/corda/vega/SimmValuationTest.kt b/samples/simm-valuation-demo/src/integration-test/kotlin/net/corda/vega/SimmValuationTest.kt index 412baeae0c..d0b63477ed 100644 --- a/samples/simm-valuation-demo/src/integration-test/kotlin/net/corda/vega/SimmValuationTest.kt +++ b/samples/simm-valuation-demo/src/integration-test/kotlin/net/corda/vega/SimmValuationTest.kt @@ -3,8 +3,8 @@ package net.corda.vega import com.opengamma.strata.product.common.BuySell import net.corda.core.identity.CordaX500Name import net.corda.core.utilities.getOrThrow -import net.corda.testing.DUMMY_BANK_A_NAME -import net.corda.testing.DUMMY_BANK_B_NAME +import net.corda.testing.core.DUMMY_BANK_A_NAME +import net.corda.testing.core.DUMMY_BANK_B_NAME import net.corda.testing.driver.driver import net.corda.testing.http.HttpApi import net.corda.vega.api.PortfolioApi diff --git a/samples/simm-valuation-demo/src/test/kotlin/net/corda/vega/Main.kt b/samples/simm-valuation-demo/src/test/kotlin/net/corda/vega/Main.kt index 3bf26cef2a..bae73f344a 100644 --- a/samples/simm-valuation-demo/src/test/kotlin/net/corda/vega/Main.kt +++ b/samples/simm-valuation-demo/src/test/kotlin/net/corda/vega/Main.kt @@ -1,7 +1,9 @@ package net.corda.vega import net.corda.core.utilities.getOrThrow -import net.corda.testing.* +import net.corda.testing.core.DUMMY_BANK_A_NAME +import net.corda.testing.core.DUMMY_BANK_B_NAME +import net.corda.testing.core.DUMMY_BANK_C_NAME import net.corda.testing.driver.driver /** diff --git a/samples/trader-demo/src/integration-test/kotlin/net/corda/traderdemo/TraderDemoTest.kt b/samples/trader-demo/src/integration-test/kotlin/net/corda/traderdemo/TraderDemoTest.kt index 0cd187f96d..ad2aa5c637 100644 --- a/samples/trader-demo/src/integration-test/kotlin/net/corda/traderdemo/TraderDemoTest.kt +++ b/samples/trader-demo/src/integration-test/kotlin/net/corda/traderdemo/TraderDemoTest.kt @@ -8,7 +8,10 @@ import net.corda.finance.flows.CashIssueFlow import net.corda.finance.flows.CashPaymentFlow import net.corda.node.services.Permissions.Companion.all import net.corda.node.services.Permissions.Companion.startFlow -import net.corda.testing.* +import net.corda.testing.core.BOC_NAME +import net.corda.testing.core.DUMMY_BANK_A_NAME +import net.corda.testing.core.DUMMY_BANK_B_NAME +import net.corda.testing.core.chooseIdentity import net.corda.testing.driver.NodeHandle import net.corda.testing.driver.driver import net.corda.testing.node.User diff --git a/samples/trader-demo/src/main/kotlin/net/corda/traderdemo/TraderDemo.kt b/samples/trader-demo/src/main/kotlin/net/corda/traderdemo/TraderDemo.kt index 7766be2a19..b8dbb2a955 100644 --- a/samples/trader-demo/src/main/kotlin/net/corda/traderdemo/TraderDemo.kt +++ b/samples/trader-demo/src/main/kotlin/net/corda/traderdemo/TraderDemo.kt @@ -5,8 +5,8 @@ import net.corda.client.rpc.CordaRPCClient import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.contextLogger import net.corda.finance.DOLLARS -import net.corda.testing.DUMMY_BANK_A_NAME -import net.corda.testing.DUMMY_BANK_B_NAME +import net.corda.testing.core.DUMMY_BANK_A_NAME +import net.corda.testing.core.DUMMY_BANK_B_NAME import kotlin.system.exitProcess /** diff --git a/samples/trader-demo/src/test/kotlin/net/corda/traderdemo/Main.kt b/samples/trader-demo/src/test/kotlin/net/corda/traderdemo/Main.kt index 6b4f7fab0e..04322ba86e 100644 --- a/samples/trader-demo/src/test/kotlin/net/corda/traderdemo/Main.kt +++ b/samples/trader-demo/src/test/kotlin/net/corda/traderdemo/Main.kt @@ -4,9 +4,9 @@ import net.corda.core.internal.div import net.corda.finance.flows.CashIssueFlow import net.corda.node.services.Permissions.Companion.all import net.corda.node.services.Permissions.Companion.startFlow -import net.corda.testing.BOC_NAME -import net.corda.testing.DUMMY_BANK_A_NAME -import net.corda.testing.DUMMY_BANK_B_NAME +import net.corda.testing.core.BOC_NAME +import net.corda.testing.core.DUMMY_BANK_A_NAME +import net.corda.testing.core.DUMMY_BANK_B_NAME import net.corda.testing.node.User import net.corda.testing.driver.driver import net.corda.traderdemo.flow.CommercialPaperIssueFlow diff --git a/samples/trader-demo/src/test/kotlin/net/corda/traderdemo/TransactionGraphSearchTests.kt b/samples/trader-demo/src/test/kotlin/net/corda/traderdemo/TransactionGraphSearchTests.kt index bf36525538..a7dc44fb28 100644 --- a/samples/trader-demo/src/test/kotlin/net/corda/traderdemo/TransactionGraphSearchTests.kt +++ b/samples/trader-demo/src/test/kotlin/net/corda/traderdemo/TransactionGraphSearchTests.kt @@ -6,9 +6,12 @@ import net.corda.core.identity.CordaX500Name import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.TransactionBuilder import net.corda.core.transactions.WireTransaction -import net.corda.testing.* import net.corda.testing.contracts.DummyContract import net.corda.testing.contracts.DummyState +import net.corda.testing.core.DUMMY_NOTARY_NAME +import net.corda.testing.core.SerializationEnvironmentRule +import net.corda.testing.core.TestIdentity +import net.corda.testing.core.dummyCommand import net.corda.testing.internal.rigorousMock import net.corda.testing.node.MockServices import net.corda.testing.node.MockTransactionStorage diff --git a/testing/node-driver/src/integration-test/kotlin/net/corda/testing/driver/DriverTests.kt b/testing/node-driver/src/integration-test/kotlin/net/corda/testing/driver/DriverTests.kt index 475ddd7359..e30d1b73e1 100644 --- a/testing/node-driver/src/integration-test/kotlin/net/corda/testing/driver/DriverTests.kt +++ b/testing/node-driver/src/integration-test/kotlin/net/corda/testing/driver/DriverTests.kt @@ -13,8 +13,8 @@ import net.corda.testing.common.internal.ProjectStructure.projectRootDir import net.corda.testing.node.internal.addressMustBeBound import net.corda.testing.node.internal.addressMustNotBeBound import net.corda.testing.node.internal.internalDriver -import net.corda.testing.DUMMY_BANK_A_NAME -import net.corda.testing.DUMMY_NOTARY_NAME +import net.corda.testing.core.DUMMY_BANK_A_NAME +import net.corda.testing.core.DUMMY_NOTARY_NAME import net.corda.testing.http.HttpApi import net.corda.testing.node.NotarySpec import org.assertj.core.api.Assertions.assertThat diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/driver/Driver.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/driver/Driver.kt index ad258f6f70..fa64dbee15 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/driver/Driver.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/driver/Driver.kt @@ -14,9 +14,9 @@ import net.corda.node.internal.Node import net.corda.node.internal.StartedNode import net.corda.node.services.config.NodeConfiguration import net.corda.node.services.config.VerifierType -import net.corda.testing.DUMMY_NOTARY_NAME -import net.corda.testing.node.NotarySpec +import net.corda.testing.core.DUMMY_NOTARY_NAME import net.corda.testing.node.User +import net.corda.testing.node.NotarySpec import net.corda.testing.node.internal.DriverDSLImpl import net.corda.testing.node.internal.genericDriver import net.corda.testing.node.internal.getTimestampAsDirectoryName diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNode.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNode.kt index f6a47a79f8..63f30dbd4a 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNode.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNode.kt @@ -46,13 +46,13 @@ import net.corda.nodeapi.internal.network.NetworkParametersCopier import net.corda.nodeapi.internal.network.NotaryInfo import net.corda.nodeapi.internal.persistence.CordaPersistence import net.corda.nodeapi.internal.persistence.DatabaseConfig -import net.corda.testing.DUMMY_NOTARY_NAME +import net.corda.testing.core.DUMMY_NOTARY_NAME import net.corda.testing.common.internal.testNetworkParameters import net.corda.testing.internal.rigorousMock import net.corda.testing.internal.testThreadFactory import net.corda.testing.node.MockServices.Companion.MOCK_VERSION_INFO import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties -import net.corda.testing.setGlobalSerialization +import net.corda.testing.core.setGlobalSerialization import org.apache.activemq.artemis.utils.ReusableLatch import org.apache.sshd.common.util.security.SecurityUtils import rx.internal.schedulers.CachedThreadScheduler diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockServices.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockServices.kt index 25403c008f..7ff6788ba4 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockServices.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockServices.kt @@ -31,8 +31,8 @@ import net.corda.node.services.vault.NodeVaultService import net.corda.nodeapi.internal.persistence.CordaPersistence import net.corda.nodeapi.internal.persistence.DatabaseConfig import net.corda.nodeapi.internal.persistence.HibernateConfiguration -import net.corda.testing.DEV_ROOT_CA -import net.corda.testing.TestIdentity +import net.corda.testing.core.DEV_ROOT_CA +import net.corda.testing.core.TestIdentity import net.corda.testing.services.MockAttachmentStorage import net.corda.testing.services.MockCordappProvider import org.bouncycastle.operator.ContentSigner diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/NodeTestUtils.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/NodeTestUtils.kt index b4dd36315a..0ce2359249 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/NodeTestUtils.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/NodeTestUtils.kt @@ -15,7 +15,9 @@ import net.corda.core.serialization.internal.effectiveSerializationEnv import net.corda.core.transactions.TransactionBuilder import net.corda.core.utilities.getOrThrow import net.corda.node.services.api.StartedNodeServices -import net.corda.testing.* +import net.corda.testing.core.SerializationEnvironmentRule +import net.corda.testing.core.TestIdentity +import net.corda.testing.core.chooseIdentity import net.corda.testing.dsl.* /** diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/DriverDSLImpl.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/DriverDSLImpl.kt index 21182a1da3..5f5933c031 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/DriverDSLImpl.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/DriverDSLImpl.kt @@ -40,9 +40,9 @@ import net.corda.nodeapi.internal.crypto.save import net.corda.nodeapi.internal.network.NetworkParametersCopier import net.corda.nodeapi.internal.network.NodeInfoFilesCopier import net.corda.nodeapi.internal.network.NotaryInfo -import net.corda.testing.ALICE_NAME -import net.corda.testing.BOB_NAME -import net.corda.testing.DUMMY_BANK_A_NAME +import net.corda.testing.core.ALICE_NAME +import net.corda.testing.core.BOB_NAME +import net.corda.testing.core.DUMMY_BANK_A_NAME import net.corda.testing.common.internal.testNetworkParameters import net.corda.testing.driver.* import net.corda.testing.node.ClusterSpec @@ -51,7 +51,7 @@ import net.corda.testing.node.NotarySpec import net.corda.testing.node.User import net.corda.testing.node.internal.DriverDSLImpl.ClusterType.NON_VALIDATING_RAFT import net.corda.testing.node.internal.DriverDSLImpl.ClusterType.VALIDATING_RAFT -import net.corda.testing.setGlobalSerialization +import net.corda.testing.core.setGlobalSerialization import okhttp3.OkHttpClient import okhttp3.Request import rx.Observable diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/NodeBasedTest.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/NodeBasedTest.kt index e4ee8091c0..5239e6998e 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/NodeBasedTest.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/NodeBasedTest.kt @@ -13,11 +13,11 @@ import net.corda.node.internal.StartedNode import net.corda.node.internal.cordapp.CordappLoader import net.corda.node.services.config.* import net.corda.nodeapi.internal.config.toConfig -import net.corda.testing.SerializationEnvironmentRule +import net.corda.testing.core.SerializationEnvironmentRule import net.corda.nodeapi.internal.network.NetworkParametersCopier import net.corda.testing.node.User import net.corda.testing.common.internal.testNetworkParameters -import net.corda.testing.getFreeLocalPorts +import net.corda.testing.core.getFreeLocalPorts import net.corda.testing.internal.testThreadFactory import net.corda.testing.node.MockServices.Companion.MOCK_VERSION_INFO import org.apache.logging.log4j.Level diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/RPCDriver.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/RPCDriver.kt index 5370276fb2..e41c1916ce 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/RPCDriver.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/RPCDriver.kt @@ -23,7 +23,7 @@ import net.corda.nodeapi.ArtemisTcpTransport import net.corda.nodeapi.ConnectionDirection import net.corda.nodeapi.RPCApi import net.corda.nodeapi.internal.serialization.KRYO_RPC_CLIENT_CONTEXT -import net.corda.testing.MAX_MESSAGE_SIZE +import net.corda.testing.core.MAX_MESSAGE_SIZE import net.corda.testing.driver.JmxPolicy import net.corda.testing.driver.PortAllocation import net.corda.testing.node.NotarySpec diff --git a/testing/test-utils/src/main/kotlin/net/corda/testing/Expect.kt b/testing/test-utils/src/main/kotlin/net/corda/testing/core/Expect.kt similarity index 99% rename from testing/test-utils/src/main/kotlin/net/corda/testing/Expect.kt rename to testing/test-utils/src/main/kotlin/net/corda/testing/core/Expect.kt index 328a6fdbb3..2125954fcd 100644 --- a/testing/test-utils/src/main/kotlin/net/corda/testing/Expect.kt +++ b/testing/test-utils/src/main/kotlin/net/corda/testing/core/Expect.kt @@ -1,4 +1,4 @@ -package net.corda.testing +package net.corda.testing.core import com.google.common.util.concurrent.SettableFuture import net.corda.core.DoNotImplement diff --git a/testing/test-utils/src/main/kotlin/net/corda/testing/SerializationTestHelpers.kt b/testing/test-utils/src/main/kotlin/net/corda/testing/core/SerializationTestHelpers.kt similarity index 99% rename from testing/test-utils/src/main/kotlin/net/corda/testing/SerializationTestHelpers.kt rename to testing/test-utils/src/main/kotlin/net/corda/testing/core/SerializationTestHelpers.kt index b76f571bc3..4a1a8083d4 100644 --- a/testing/test-utils/src/main/kotlin/net/corda/testing/SerializationTestHelpers.kt +++ b/testing/test-utils/src/main/kotlin/net/corda/testing/core/SerializationTestHelpers.kt @@ -1,4 +1,4 @@ -package net.corda.testing +package net.corda.testing.core import com.nhaarman.mockito_kotlin.* import net.corda.client.rpc.internal.KryoClientSerializationScheme diff --git a/testing/test-utils/src/main/kotlin/net/corda/testing/TestConstants.kt b/testing/test-utils/src/main/kotlin/net/corda/testing/core/TestConstants.kt similarity index 98% rename from testing/test-utils/src/main/kotlin/net/corda/testing/TestConstants.kt rename to testing/test-utils/src/main/kotlin/net/corda/testing/core/TestConstants.kt index 17fb7e1294..0a87461d8d 100644 --- a/testing/test-utils/src/main/kotlin/net/corda/testing/TestConstants.kt +++ b/testing/test-utils/src/main/kotlin/net/corda/testing/core/TestConstants.kt @@ -1,6 +1,6 @@ @file:JvmName("TestConstants") -package net.corda.testing +package net.corda.testing.core import net.corda.core.contracts.Command import net.corda.core.contracts.TypeOnlyCommandData diff --git a/testing/test-utils/src/main/kotlin/net/corda/testing/CoreTestUtils.kt b/testing/test-utils/src/main/kotlin/net/corda/testing/core/TestUtils.kt similarity index 96% rename from testing/test-utils/src/main/kotlin/net/corda/testing/CoreTestUtils.kt rename to testing/test-utils/src/main/kotlin/net/corda/testing/core/TestUtils.kt index e24496c50c..f7614e2424 100644 --- a/testing/test-utils/src/main/kotlin/net/corda/testing/CoreTestUtils.kt +++ b/testing/test-utils/src/main/kotlin/net/corda/testing/core/TestUtils.kt @@ -1,7 +1,7 @@ @file:Suppress("UNUSED_PARAMETER") -@file:JvmName("CoreTestUtils") +@file:JvmName("TestUtils") -package net.corda.testing +package net.corda.testing.core import net.corda.core.contracts.PartyAndReference import net.corda.core.contracts.StateRef @@ -20,9 +20,6 @@ import net.corda.nodeapi.internal.crypto.CertificateAndKeyPair import net.corda.nodeapi.internal.crypto.CertificateType import net.corda.nodeapi.internal.crypto.X509CertificateFactory import net.corda.nodeapi.internal.crypto.X509Utilities -import org.bouncycastle.asn1.x509.GeneralName -import org.bouncycastle.asn1.x509.GeneralSubtree -import org.bouncycastle.asn1.x509.NameConstraints import java.math.BigInteger import java.security.KeyPair import java.security.PublicKey diff --git a/testing/test-utils/src/main/kotlin/net/corda/testing/dsl/TestDSL.kt b/testing/test-utils/src/main/kotlin/net/corda/testing/dsl/TestDSL.kt index 3a0e36e5f3..79c66e53a0 100644 --- a/testing/test-utils/src/main/kotlin/net/corda/testing/dsl/TestDSL.kt +++ b/testing/test-utils/src/main/kotlin/net/corda/testing/dsl/TestDSL.kt @@ -15,7 +15,7 @@ import net.corda.core.transactions.TransactionBuilder import net.corda.core.transactions.WireTransaction import net.corda.testing.services.MockAttachmentStorage import net.corda.testing.services.MockCordappProvider -import net.corda.testing.dummyCommand +import net.corda.testing.core.dummyCommand import java.io.InputStream import java.security.PublicKey import java.util.* diff --git a/testing/test-utils/src/main/kotlin/net/corda/testing/internal/InternalSerializationTestHelpers.kt b/testing/test-utils/src/main/kotlin/net/corda/testing/internal/InternalSerializationTestHelpers.kt index 659c889087..f11693d527 100644 --- a/testing/test-utils/src/main/kotlin/net/corda/testing/internal/InternalSerializationTestHelpers.kt +++ b/testing/test-utils/src/main/kotlin/net/corda/testing/internal/InternalSerializationTestHelpers.kt @@ -2,7 +2,7 @@ package net.corda.testing.internal import net.corda.core.serialization.internal._contextSerializationEnv import net.corda.core.serialization.internal._inheritableContextSerializationEnv -import net.corda.testing.SerializationEnvironmentRule +import net.corda.testing.core.SerializationEnvironmentRule /** * For example your test class uses [SerializationEnvironmentRule] but you want to turn it off for one method. diff --git a/testing/test-utils/src/main/kotlin/net/corda/testing/internal/TestNodeInfoBuilder.kt b/testing/test-utils/src/main/kotlin/net/corda/testing/internal/TestNodeInfoBuilder.kt index bdeba56fc6..70ab0f49a9 100644 --- a/testing/test-utils/src/main/kotlin/net/corda/testing/internal/TestNodeInfoBuilder.kt +++ b/testing/test-utils/src/main/kotlin/net/corda/testing/internal/TestNodeInfoBuilder.kt @@ -8,7 +8,7 @@ import net.corda.core.node.NodeInfo import net.corda.core.serialization.serialize import net.corda.core.utilities.NetworkHostAndPort import net.corda.nodeapi.internal.SignedNodeInfo -import net.corda.testing.getTestPartyAndCertificate +import net.corda.testing.core.getTestPartyAndCertificate import java.security.PrivateKey class TestNodeInfoBuilder { diff --git a/testing/test-utils/src/main/kotlin/net/corda/testing/internal/vault/VaultFiller.kt b/testing/test-utils/src/main/kotlin/net/corda/testing/internal/vault/VaultFiller.kt index 5febc72bd8..12c21c591b 100644 --- a/testing/test-utils/src/main/kotlin/net/corda/testing/internal/vault/VaultFiller.kt +++ b/testing/test-utils/src/main/kotlin/net/corda/testing/internal/vault/VaultFiller.kt @@ -16,7 +16,7 @@ import net.corda.finance.contracts.Commodity import net.corda.finance.contracts.DealState import net.corda.finance.contracts.asset.Cash import net.corda.finance.contracts.asset.CommodityContract -import net.corda.testing.* +import net.corda.testing.core.* import java.security.PublicKey import java.time.Duration import java.time.Instant diff --git a/testing/test-utils/src/main/kotlin/net/corda/testing/FlowStackSnapshot.kt b/testing/test-utils/src/main/kotlin/net/corda/testing/services/FlowStackSnapshot.kt similarity index 99% rename from testing/test-utils/src/main/kotlin/net/corda/testing/FlowStackSnapshot.kt rename to testing/test-utils/src/main/kotlin/net/corda/testing/services/FlowStackSnapshot.kt index 74f622bc37..279f45e58f 100644 --- a/testing/test-utils/src/main/kotlin/net/corda/testing/FlowStackSnapshot.kt +++ b/testing/test-utils/src/main/kotlin/net/corda/testing/services/FlowStackSnapshot.kt @@ -1,4 +1,4 @@ -package net.corda.testing +package net.corda.testing.services import co.paralleluniverse.fibers.Fiber import co.paralleluniverse.fibers.Instrumented diff --git a/testing/test-utils/src/main/resources/META-INF/services/net.corda.node.services.statemachine.FlowStackSnapshotFactory b/testing/test-utils/src/main/resources/META-INF/services/net.corda.node.services.statemachine.FlowStackSnapshotFactory index 37ed9eb7fc..adc561e159 100644 --- a/testing/test-utils/src/main/resources/META-INF/services/net.corda.node.services.statemachine.FlowStackSnapshotFactory +++ b/testing/test-utils/src/main/resources/META-INF/services/net.corda.node.services.statemachine.FlowStackSnapshotFactory @@ -1 +1 @@ -net.corda.testing.FlowStackSnapshotFactoryImpl \ No newline at end of file +net.corda.testing.services.FlowStackSnapshotFactoryImpl \ No newline at end of file diff --git a/testing/test-utils/src/test/kotlin/net/corda/testing/TestIdentityTests.kt b/testing/test-utils/src/test/kotlin/net/corda/testing/TestIdentityTests.kt index c5a4e673bd..917309bbb8 100644 --- a/testing/test-utils/src/test/kotlin/net/corda/testing/TestIdentityTests.kt +++ b/testing/test-utils/src/test/kotlin/net/corda/testing/TestIdentityTests.kt @@ -1,5 +1,8 @@ package net.corda.testing +import net.corda.testing.core.ALICE_NAME +import net.corda.testing.core.BOB_NAME +import net.corda.testing.core.TestIdentity import org.junit.Test import kotlin.test.assertEquals import kotlin.test.assertNotEquals diff --git a/tools/explorer/src/main/kotlin/net/corda/explorer/ExplorerSimulation.kt b/tools/explorer/src/main/kotlin/net/corda/explorer/ExplorerSimulation.kt index faebad502d..f0729b9524 100644 --- a/tools/explorer/src/main/kotlin/net/corda/explorer/ExplorerSimulation.kt +++ b/tools/explorer/src/main/kotlin/net/corda/explorer/ExplorerSimulation.kt @@ -21,8 +21,8 @@ import net.corda.finance.flows.* import net.corda.finance.flows.CashExitFlow.ExitRequest import net.corda.finance.flows.CashIssueAndPaymentFlow.IssueAndPaymentRequest import net.corda.node.services.Permissions.Companion.startFlow -import net.corda.testing.ALICE_NAME -import net.corda.testing.BOB_NAME +import net.corda.testing.core.ALICE_NAME +import net.corda.testing.core.BOB_NAME import net.corda.testing.node.User import net.corda.testing.driver.JmxPolicy import net.corda.testing.driver.NodeHandle diff --git a/tools/explorer/src/main/kotlin/net/corda/explorer/views/cordapps/cash/NewTransaction.kt b/tools/explorer/src/main/kotlin/net/corda/explorer/views/cordapps/cash/NewTransaction.kt index 985f962473..d8431707f4 100644 --- a/tools/explorer/src/main/kotlin/net/corda/explorer/views/cordapps/cash/NewTransaction.kt +++ b/tools/explorer/src/main/kotlin/net/corda/explorer/views/cordapps/cash/NewTransaction.kt @@ -39,7 +39,7 @@ import net.corda.finance.flows.CashIssueAndPaymentFlow import net.corda.finance.flows.CashIssueAndPaymentFlow.IssueAndPaymentRequest import net.corda.finance.flows.CashPaymentFlow import net.corda.finance.flows.CashPaymentFlow.PaymentRequest -import net.corda.testing.chooseIdentityAndCert +import net.corda.testing.core.chooseIdentityAndCert import org.controlsfx.dialog.ExceptionDialog import tornadofx.* import java.math.BigDecimal diff --git a/tools/loadtest/src/main/kotlin/net/corda/loadtest/tests/NotaryTest.kt b/tools/loadtest/src/main/kotlin/net/corda/loadtest/tests/NotaryTest.kt index 5e2031b098..87a0b2589c 100644 --- a/tools/loadtest/src/main/kotlin/net/corda/loadtest/tests/NotaryTest.kt +++ b/tools/loadtest/src/main/kotlin/net/corda/loadtest/tests/NotaryTest.kt @@ -9,8 +9,9 @@ import net.corda.core.messaging.startFlow import net.corda.core.transactions.SignedTransaction import net.corda.loadtest.LoadTest import net.corda.loadtest.NodeConnection -import net.corda.testing.* import net.corda.testing.contracts.DummyContract +import net.corda.testing.core.DUMMY_NOTARY_NAME +import net.corda.testing.core.TestIdentity import net.corda.testing.node.MockServices import net.corda.testing.node.makeTestIdentityService import org.slf4j.LoggerFactory diff --git a/verifier/src/integration-test/kotlin/net/corda/verifier/VerifierTests.kt b/verifier/src/integration-test/kotlin/net/corda/verifier/VerifierTests.kt index 65336db7e4..2138b1876f 100644 --- a/verifier/src/integration-test/kotlin/net/corda/verifier/VerifierTests.kt +++ b/verifier/src/integration-test/kotlin/net/corda/verifier/VerifierTests.kt @@ -12,7 +12,9 @@ import net.corda.finance.DOLLARS import net.corda.finance.flows.CashIssueFlow import net.corda.finance.flows.CashPaymentFlow import net.corda.node.services.config.VerifierType -import net.corda.testing.* +import net.corda.testing.core.ALICE_NAME +import net.corda.testing.core.DUMMY_NOTARY_NAME +import net.corda.testing.core.SerializationEnvironmentRule import net.corda.testing.node.NotarySpec import org.junit.Rule import org.junit.Test diff --git a/webserver/src/integration-test/kotlin/net/corda/webserver/WebserverDriverTests.kt b/webserver/src/integration-test/kotlin/net/corda/webserver/WebserverDriverTests.kt index c76709dce5..2876482549 100644 --- a/webserver/src/integration-test/kotlin/net/corda/webserver/WebserverDriverTests.kt +++ b/webserver/src/integration-test/kotlin/net/corda/webserver/WebserverDriverTests.kt @@ -2,7 +2,7 @@ package net.corda.webserver import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.getOrThrow -import net.corda.testing.DUMMY_BANK_A_NAME +import net.corda.testing.core.DUMMY_BANK_A_NAME import net.corda.testing.driver.WebserverHandle import net.corda.testing.node.internal.addressMustBeBound import net.corda.testing.node.internal.addressMustNotBeBound From 4a3379ac8aff5cf7546c411b77b5765c15717d0f Mon Sep 17 00:00:00 2001 From: Michal Kit Date: Mon, 22 Jan 2018 13:06:22 +0000 Subject: [PATCH 14/36] CORDA-937 adding node key pair to utility/testing methods (#2405) --- .../nodeapi/internal/KeyStoreConfigHelpers.kt | 16 +++++----- .../testing/internal/InternalTestUtils.kt | 5 +++- .../testing/internal/TestNodeInfoBuilder.kt | 29 +++++++++++++++---- 3 files changed, 37 insertions(+), 13 deletions(-) diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/KeyStoreConfigHelpers.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/KeyStoreConfigHelpers.kt index 2ba6192454..0c0f8a17a1 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/KeyStoreConfigHelpers.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/KeyStoreConfigHelpers.kt @@ -1,6 +1,6 @@ package net.corda.nodeapi.internal -import net.corda.core.crypto.Crypto +import net.corda.core.crypto.Crypto.generateKeyPair import net.corda.core.identity.CordaX500Name import net.corda.core.internal.x500Name import net.corda.nodeapi.internal.config.SSLConfiguration @@ -8,6 +8,7 @@ import net.corda.nodeapi.internal.crypto.* import org.bouncycastle.asn1.x509.GeneralName import org.bouncycastle.asn1.x509.GeneralSubtree import org.bouncycastle.asn1.x509.NameConstraints +import java.security.KeyPair import java.security.cert.X509Certificate import javax.security.auth.x500.X500Principal @@ -31,7 +32,7 @@ fun SSLConfiguration.createDevKeyStores(legalName: CordaX500Name, save(nodeKeystore, keyStorePassword) } - val tlsKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) + val tlsKeyPair = generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) val tlsCert = X509Utilities.createCertificate(CertificateType.TLS, nodeCaCert, nodeCaKeyPair, legalName.x500Principal, tlsKeyPair.public) loadOrCreateKeyStore(sslKeystore, keyStorePassword).apply { @@ -45,7 +46,7 @@ fun SSLConfiguration.createDevKeyStores(legalName: CordaX500Name, } fun createDevNetworkMapCa(rootCa: CertificateAndKeyPair = DEV_ROOT_CA): CertificateAndKeyPair { - val keyPair = Crypto.generateKeyPair() + val keyPair = generateKeyPair() val cert = X509Utilities.createCertificate( CertificateType.NETWORK_MAP, rootCa.certificate, @@ -59,17 +60,18 @@ fun createDevNetworkMapCa(rootCa: CertificateAndKeyPair = DEV_ROOT_CA): Certific * Create a dev node CA cert, as a sub-cert of the given [intermediateCa], and matching key pair using the given * [CordaX500Name] as the cert subject. */ -fun createDevNodeCa(intermediateCa: CertificateAndKeyPair, legalName: CordaX500Name): CertificateAndKeyPair { - val keyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) +fun createDevNodeCa(intermediateCa: CertificateAndKeyPair, + legalName: CordaX500Name, + nodeKeyPair: KeyPair = generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)): CertificateAndKeyPair { val nameConstraints = NameConstraints(arrayOf(GeneralSubtree(GeneralName(GeneralName.directoryName, legalName.x500Name))), arrayOf()) val cert = X509Utilities.createCertificate( CertificateType.NODE_CA, intermediateCa.certificate, intermediateCa.keyPair, legalName.x500Principal, - keyPair.public, + nodeKeyPair.public, nameConstraints = nameConstraints) - return CertificateAndKeyPair(cert, keyPair) + return CertificateAndKeyPair(cert, nodeKeyPair) } val DEV_INTERMEDIATE_CA: CertificateAndKeyPair get() = DevCaHelper.loadDevCa(X509Utilities.CORDA_INTERMEDIATE_CA) diff --git a/testing/test-utils/src/main/kotlin/net/corda/testing/internal/InternalTestUtils.kt b/testing/test-utils/src/main/kotlin/net/corda/testing/internal/InternalTestUtils.kt index 60a7c40b00..1d73ec5363 100644 --- a/testing/test-utils/src/main/kotlin/net/corda/testing/internal/InternalTestUtils.kt +++ b/testing/test-utils/src/main/kotlin/net/corda/testing/internal/InternalTestUtils.kt @@ -2,6 +2,7 @@ package net.corda.testing.internal import com.nhaarman.mockito_kotlin.doAnswer import net.corda.core.crypto.Crypto +import net.corda.core.crypto.Crypto.generateKeyPair import net.corda.core.identity.CordaX500Name import net.corda.core.utilities.loggerFor import net.corda.node.services.config.configureDevKeyAndTrustStores @@ -15,6 +16,7 @@ import org.mockito.Mockito import org.mockito.internal.stubbing.answers.ThrowsException import java.lang.reflect.Modifier import java.nio.file.Files +import java.security.KeyPair import java.util.* import javax.security.auth.x500.X500Principal @@ -102,11 +104,12 @@ fun createDevIntermediateCaCertPath( */ fun createDevNodeCaCertPath( legalName: CordaX500Name, + nodeKeyPair: KeyPair = generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME), rootCaName: X500Principal = defaultRootCaName, intermediateCaName: X500Principal = defaultIntermediateCaName ): Triple { val (rootCa, intermediateCa) = createDevIntermediateCaCertPath(rootCaName, intermediateCaName) - val nodeCa = createDevNodeCa(intermediateCa, legalName) + val nodeCa = createDevNodeCa(intermediateCa, legalName, nodeKeyPair) return Triple(rootCa, intermediateCa, nodeCa) } diff --git a/testing/test-utils/src/main/kotlin/net/corda/testing/internal/TestNodeInfoBuilder.kt b/testing/test-utils/src/main/kotlin/net/corda/testing/internal/TestNodeInfoBuilder.kt index 70ab0f49a9..83cd569679 100644 --- a/testing/test-utils/src/main/kotlin/net/corda/testing/internal/TestNodeInfoBuilder.kt +++ b/testing/test-utils/src/main/kotlin/net/corda/testing/internal/TestNodeInfoBuilder.kt @@ -8,16 +8,35 @@ import net.corda.core.node.NodeInfo import net.corda.core.serialization.serialize import net.corda.core.utilities.NetworkHostAndPort import net.corda.nodeapi.internal.SignedNodeInfo -import net.corda.testing.core.getTestPartyAndCertificate +import net.corda.nodeapi.internal.createDevNodeCa +import net.corda.nodeapi.internal.crypto.CertificateAndKeyPair +import net.corda.nodeapi.internal.crypto.CertificateType +import net.corda.nodeapi.internal.crypto.X509CertificateFactory +import net.corda.nodeapi.internal.crypto.X509Utilities +import net.corda.testing.core.DEV_INTERMEDIATE_CA +import net.corda.testing.core.DEV_ROOT_CA +import java.security.KeyPair import java.security.PrivateKey +import java.security.cert.X509Certificate -class TestNodeInfoBuilder { +class TestNodeInfoBuilder(private val intermediateAndRoot: Pair = DEV_INTERMEDIATE_CA to DEV_ROOT_CA.certificate) { private val identitiesAndPrivateKeys = ArrayList>() - fun addIdentity(name: CordaX500Name): Pair { + fun addIdentity(name: CordaX500Name, nodeKeyPair: KeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)): Pair { + val nodeCertificateAndKeyPair = createDevNodeCa(intermediateAndRoot.first, name, nodeKeyPair) val identityKeyPair = Crypto.generateKeyPair() - val identity = getTestPartyAndCertificate(name, identityKeyPair.public) - return Pair(identity, identityKeyPair.private).also { + val identityCert = X509Utilities.createCertificate( + CertificateType.LEGAL_IDENTITY, + nodeCertificateAndKeyPair.certificate, + nodeCertificateAndKeyPair.keyPair, + nodeCertificateAndKeyPair.certificate.subjectX500Principal, + identityKeyPair.public) + val certPath = X509CertificateFactory() + .generateCertPath(identityCert, + nodeCertificateAndKeyPair.certificate, + intermediateAndRoot.first.certificate, + intermediateAndRoot.second) + return Pair(PartyAndCertificate(certPath), identityKeyPair.private).also { identitiesAndPrivateKeys += it } } From 8d5611853a7a3ecf587b15bb7b7f76bb7c417090 Mon Sep 17 00:00:00 2001 From: Christian Sailer Date: Mon, 22 Jan 2018 13:41:06 +0000 Subject: [PATCH 15/36] CORDA-929 Attachment caching (#2372) * ENT-1403 Cache node attachments (and attachment content) * ENT-1403 Make cache sizes configurable * Update documentation with new config parameters * Test that non-existence of attachments is not cached * Remove unneeded defaults in interface * It turned out we need the defaults on the interface in quite a few tests * Codereview: typos, size in MB rather than bytes, charset in tests, move concurrencyLevel to a constant * Codereview: Make the internal config value bytes again, but config file in MB * Fix example config unit test --- docs/source/corda-configuration-file.rst | 11 ++- .../net/corda/docs/ExampleConfigTest.kt | 9 ++- .../net/corda/node/internal/AbstractNode.kt | 7 +- .../node/services/config/NodeConfiguration.kt | 16 +++- .../persistence/NodeAttachmentService.kt | 75 ++++++++++++++++++- .../node/utilities/NonInvalidatingCache.kt | 4 +- .../persistence/NodeAttachmentStorageTest.kt | 48 ++++++++++-- 7 files changed, 151 insertions(+), 19 deletions(-) diff --git a/docs/source/corda-configuration-file.rst b/docs/source/corda-configuration-file.rst index 79ce625b0d..1a81da1e3f 100644 --- a/docs/source/corda-configuration-file.rst +++ b/docs/source/corda-configuration-file.rst @@ -170,4 +170,13 @@ path to the node's base directory. Default Jolokia access url is http://127.0.0.1:7005/jolokia/ :useAMQPBridges: Optionally can be set to ``false`` to use Artemis CORE Bridges for peer-to-peer communications. - Otherwise, defaults to ``true`` and the AMQP 1.0 protocol will be used for message transfer between nodes. \ No newline at end of file + Otherwise, defaults to ``true`` and the AMQP 1.0 protocol will be used for message transfer between nodes. + +:transactionCacheSizeMegaBytes: Optionally specify how much memory should be used for caching of ledger transactions in memory. + Otherwise defaults to 8MB plus 5% of all heap memory above 300MB. + +:attachmentContentCacheSizeMegaBytes: Optionally specify how much memory should be used to cache attachment contents in memory. + Otherwise defaults to 10MB + +:attachmentCacheBound: Optionally specify how many attachments should be cached locally. Note that this includes only the key and + metadata, the content is cached separately and can be loaded lazily. Defaults to 1024. \ No newline at end of file diff --git a/docs/source/example-code/src/test/kotlin/net/corda/docs/ExampleConfigTest.kt b/docs/source/example-code/src/test/kotlin/net/corda/docs/ExampleConfigTest.kt index edefef85f7..bf5cb49b1d 100644 --- a/docs/source/example-code/src/test/kotlin/net/corda/docs/ExampleConfigTest.kt +++ b/docs/source/example-code/src/test/kotlin/net/corda/docs/ExampleConfigTest.kt @@ -1,12 +1,15 @@ package net.corda.docs import net.corda.node.services.config.ConfigHelper +import net.corda.node.services.config.NodeConfiguration import net.corda.node.services.config.parseAsNodeConfiguration import net.corda.verifier.Verifier import org.junit.Test import java.nio.file.Path import java.nio.file.Paths +import kotlin.reflect.KVisibility import kotlin.reflect.full.declaredMemberProperties +import kotlin.reflect.jvm.isAccessible class ExampleConfigTest { @@ -17,14 +20,16 @@ class ExampleConfigTest { val config = loadConfig(Paths.get(configFileResource.toURI())) // Force the config fields as they are resolved lazily config.javaClass.kotlin.declaredMemberProperties.forEach { member -> - member.get(config) + if (member.visibility == KVisibility.PUBLIC) { + member.get(config) + } } } } @Test fun `example node_confs parses fine`() { - readAndCheckConfigurations( + readAndCheckConfigurations( "example-node.conf", "example-out-of-process-verifier-node.conf", "example-network-map-node.conf" diff --git a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt index 6280ed522a..5affcadaf8 100644 --- a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt +++ b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt @@ -36,10 +36,7 @@ import net.corda.node.services.ContractUpgradeHandler import net.corda.node.services.FinalityHandler import net.corda.node.services.NotaryChangeHandler import net.corda.node.services.api.* -import net.corda.node.services.config.BFTSMaRtConfiguration -import net.corda.node.services.config.NodeConfiguration -import net.corda.node.services.config.NotaryConfig -import net.corda.node.services.config.configureWithDevSSLCertificate +import net.corda.node.services.config.* import net.corda.node.services.events.NodeSchedulerService import net.corda.node.services.events.ScheduledActivityObserver import net.corda.node.services.identity.PersistentIdentityService @@ -536,7 +533,7 @@ abstract class AbstractNode(val configuration: NodeConfiguration, private fun makeServices(keyPairs: Set, schemaService: SchemaService, transactionStorage: WritableTransactionStorage, database: CordaPersistence, info: NodeInfo, identityService: IdentityServiceInternal, networkMapCache: NetworkMapCacheInternal): MutableList { checkpointStorage = DBCheckpointStorage() val metrics = MetricRegistry() - attachments = NodeAttachmentService(metrics) + attachments = NodeAttachmentService(metrics, configuration.attachmentContentCacheSizeBytes, configuration.attachmentCacheBound) val cordappProvider = CordappProviderImpl(cordappLoader, attachments) val keyManagementService = makeKeyManagementService(identityService, keyPairs) _services = ServiceHubInternalImpl( diff --git a/node/src/main/kotlin/net/corda/node/services/config/NodeConfiguration.kt b/node/src/main/kotlin/net/corda/node/services/config/NodeConfiguration.kt index 61a7da64ab..0868682faa 100644 --- a/node/src/main/kotlin/net/corda/node/services/config/NodeConfiguration.kt +++ b/node/src/main/kotlin/net/corda/node/services/config/NodeConfiguration.kt @@ -14,6 +14,7 @@ import java.net.URL import java.nio.file.Path import java.util.* + val Int.MB: Long get() = this * 1024L * 1024L interface NodeConfiguration : NodeSSLConfiguration { @@ -42,6 +43,9 @@ interface NodeConfiguration : NodeSSLConfiguration { val database: DatabaseConfig val useAMQPBridges: Boolean get() = true val transactionCacheSizeBytes: Long get() = defaultTransactionCacheSize + val attachmentContentCacheSizeBytes: Long get() = defaultAttachmentContentCacheSize + val attachmentCacheBound: Long get() = defaultAttachmentCacheBound + companion object { // default to at least 8MB and a bit extra for larger heap sizes @@ -51,6 +55,9 @@ interface NodeConfiguration : NodeSSLConfiguration { private fun getAdditionalCacheMemory(): Long { return Math.max((Runtime.getRuntime().maxMemory() - 300.MB) / 20, 0) } + + val defaultAttachmentContentCacheSize: Long = 10.MB + val defaultAttachmentCacheBound = 1024L } } @@ -127,10 +134,17 @@ data class NodeConfigurationImpl( override val sshd: SSHDConfiguration? = null, override val database: DatabaseConfig = DatabaseConfig(initialiseSchema = devMode, exportHibernateJMXStatistics = devMode), override val useAMQPBridges: Boolean = true, - override val transactionCacheSizeBytes: Long = NodeConfiguration.defaultTransactionCacheSize + private val transactionCacheSizeMegaBytes: Int? = null, + private val attachmentContentCacheSizeMegaBytes: Int? = null, + override val attachmentCacheBound: Long = NodeConfiguration.defaultAttachmentCacheBound ) : NodeConfiguration { override val exportJMXto: String get() = "http" + override val transactionCacheSizeBytes: Long + get() = transactionCacheSizeMegaBytes?.MB ?: super.transactionCacheSizeBytes + override val attachmentContentCacheSizeBytes: Long + get() = attachmentContentCacheSizeMegaBytes?.MB ?: super.attachmentContentCacheSizeBytes + init { // This is a sanity feature do not remove. diff --git a/node/src/main/kotlin/net/corda/node/services/persistence/NodeAttachmentService.kt b/node/src/main/kotlin/net/corda/node/services/persistence/NodeAttachmentService.kt index 60c5eb373a..784499509d 100644 --- a/node/src/main/kotlin/net/corda/node/services/persistence/NodeAttachmentService.kt +++ b/node/src/main/kotlin/net/corda/node/services/persistence/NodeAttachmentService.kt @@ -1,6 +1,7 @@ package net.corda.node.services.persistence import com.codahale.metrics.MetricRegistry +import com.google.common.cache.Weigher import com.google.common.hash.HashCode import com.google.common.hash.Hashing import com.google.common.hash.HashingInputStream @@ -16,12 +17,17 @@ import net.corda.core.node.services.vault.AttachmentQueryCriteria import net.corda.core.node.services.vault.AttachmentSort import net.corda.core.serialization.* import net.corda.core.utilities.contextLogger +import net.corda.node.services.config.NodeConfiguration import net.corda.node.services.vault.HibernateAttachmentQueryCriteriaParser +import net.corda.node.utilities.NonInvalidatingCache +import net.corda.node.utilities.NonInvalidatingWeightBasedCache +import net.corda.node.utilities.defaultCordaCacheConcurrencyLevel import net.corda.nodeapi.internal.persistence.NODE_DATABASE_PREFIX import net.corda.nodeapi.internal.persistence.currentDBSession import java.io.* import java.nio.file.Paths import java.time.Instant +import java.util.* import java.util.jar.JarInputStream import javax.annotation.concurrent.ThreadSafe import javax.persistence.* @@ -30,7 +36,12 @@ import javax.persistence.* * Stores attachments using Hibernate to database. */ @ThreadSafe -class NodeAttachmentService(metrics: MetricRegistry) : AttachmentStorage, SingletonSerializeAsToken() { +class NodeAttachmentService( + metrics: MetricRegistry, + attachmentContentCacheSize: Long = NodeConfiguration.defaultAttachmentContentCacheSize, + attachmentCacheBound: Long = NodeConfiguration.defaultAttachmentCacheBound +) : AttachmentStorage, SingletonSerializeAsToken( +) { companion object { private val log = contextLogger() @@ -172,11 +183,67 @@ class NodeAttachmentService(metrics: MetricRegistry) : AttachmentStorage, Single } - override fun openAttachment(id: SecureHash): Attachment? { + + // slightly complex 2 level approach to attachment caching: + // On the first level we cache attachment contents loaded from the DB by their key. This is a weight based + // cache (we don't want to waste too much memory on this) and could be evicted quite aggressively. If we fail + // to load an attachment from the db, the loader will insert a non present optional - we invalidate this + // immediately as we definitely want to retry whether the attachment was just delayed. + // On the second level, we cache Attachment implementations that use the first cache to load their content + // when required. As these are fairly small, we can cache quite a lot of them, this will make checking + // repeatedly whether an attachment exists fairly cheap. Here as well, we evict non-existent entries immediately + // to force a recheck if required. + // If repeatedly looking for non-existing attachments becomes a performance issue, this is either indicating a + // a problem somewhere else or this needs to be revisited. + + private val attachmentContentCache = NonInvalidatingWeightBasedCache>( + maxWeight = attachmentContentCacheSize, + concurrencyLevel = defaultCordaCacheConcurrencyLevel, + weigher = object : Weigher> { + override fun weigh(key: SecureHash, value: Optional): Int { + return key.size + if (value.isPresent) value.get().size else 0 + } + }, + loadFunction = { Optional.ofNullable(loadAttachmentContent(it)) } + ) + + private fun loadAttachmentContent(id: SecureHash): ByteArray? { val attachment = currentDBSession().get(NodeAttachmentService.DBAttachment::class.java, id.toString()) - attachment?.let { - return AttachmentImpl(id, { attachment.content }, checkAttachmentsOnLoad) + return attachment?.content + } + + + private val attachmentCache = NonInvalidatingCache>( + attachmentCacheBound, + defaultCordaCacheConcurrencyLevel, + { key -> Optional.ofNullable(createAttachment(key)) } + ) + + private fun createAttachment(key: SecureHash): Attachment? { + val content = attachmentContentCache.get(key) + if (content.isPresent) { + return AttachmentImpl( + key, + { + attachmentContentCache + .get(key) + .orElseThrow { + IllegalArgumentException("No attachement impl should have been created for non existent content") + } + }, + checkAttachmentsOnLoad) } + // if no attachement has been found, we don't want to cache that - it might arrive later + attachmentContentCache.invalidate(key) + return null + } + + override fun openAttachment(id: SecureHash): Attachment? { + val attachment = attachmentCache.get(id) + if (attachment.isPresent) { + return attachment.get() + } + attachmentCache.invalidate(id) return null } diff --git a/node/src/main/kotlin/net/corda/node/utilities/NonInvalidatingCache.kt b/node/src/main/kotlin/net/corda/node/utilities/NonInvalidatingCache.kt index 0a199a0e31..f08a0631cb 100644 --- a/node/src/main/kotlin/net/corda/node/utilities/NonInvalidatingCache.kt +++ b/node/src/main/kotlin/net/corda/node/utilities/NonInvalidatingCache.kt @@ -44,4 +44,6 @@ class NonInvalidatingWeightBasedCache private constructor( return builder.build(NonInvalidatingCache.NonInvalidatingCacheLoader(loadFunction)) } } -} \ No newline at end of file +} + +val defaultCordaCacheConcurrencyLevel: Int = 8 \ No newline at end of file diff --git a/node/src/test/kotlin/net/corda/node/services/persistence/NodeAttachmentStorageTest.kt b/node/src/test/kotlin/net/corda/node/services/persistence/NodeAttachmentStorageTest.kt index 5995f3c177..861a324e78 100644 --- a/node/src/test/kotlin/net/corda/node/services/persistence/NodeAttachmentStorageTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/persistence/NodeAttachmentStorageTest.kt @@ -13,18 +13,18 @@ import net.corda.core.node.services.vault.AttachmentQueryCriteria import net.corda.core.node.services.vault.AttachmentSort import net.corda.core.node.services.vault.Builder import net.corda.core.node.services.vault.Sort -import net.corda.node.services.transactions.PersistentUniquenessProvider import net.corda.node.internal.configureDatabase +import net.corda.node.services.transactions.PersistentUniquenessProvider import net.corda.nodeapi.internal.persistence.CordaPersistence import net.corda.nodeapi.internal.persistence.DatabaseConfig import net.corda.testing.internal.LogHelper -import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties import net.corda.testing.internal.rigorousMock +import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties import org.junit.After import org.junit.Before import org.junit.Ignore import org.junit.Test -import java.nio.charset.Charset +import java.nio.charset.StandardCharsets import java.nio.file.FileAlreadyExistsException import java.nio.file.FileSystem import java.nio.file.Path @@ -66,10 +66,10 @@ class NodeAttachmentStorageTest { val stream = storage.openAttachment(expectedHash)!!.openAsJAR() val e1 = stream.nextJarEntry!! assertEquals("test1.txt", e1.name) - assertEquals(stream.readBytes().toString(Charset.defaultCharset()), "This is some useful content") + assertEquals(stream.readBytes().toString(StandardCharsets.UTF_8), "This is some useful content") val e2 = stream.nextJarEntry!! assertEquals("test2.txt", e2.name) - assertEquals(stream.readBytes().toString(Charset.defaultCharset()), "Some more useful content") + assertEquals(stream.readBytes().toString(StandardCharsets.UTF_8), "Some more useful content") stream.close() @@ -80,6 +80,44 @@ class NodeAttachmentStorageTest { } } + @Test + fun `missing is not cached`() { + val (testJar, expectedHash) = makeTestJar() + val (jarB, hashB) = makeTestJar(listOf(Pair("file", "content"))) + + database.transaction { + val storage = NodeAttachmentService(MetricRegistry()) + val id = testJar.read { storage.importAttachment(it) } + assertEquals(expectedHash, id) + + + assertNull(storage.openAttachment(hashB)) + val stream = storage.openAttachment(expectedHash)!!.openAsJAR() + val e1 = stream.nextJarEntry!! + assertEquals("test1.txt", e1.name) + assertEquals(stream.readBytes().toString(StandardCharsets.UTF_8), "This is some useful content") + val e2 = stream.nextJarEntry!! + assertEquals("test2.txt", e2.name) + assertEquals(stream.readBytes().toString(StandardCharsets.UTF_8), "Some more useful content") + + stream.close() + + val idB = jarB.read { storage.importAttachment(it) } + assertEquals(hashB, idB) + + storage.openAttachment(id)!!.openAsJAR().use { + it.nextJarEntry + it.readBytes() + } + + storage.openAttachment(idB)!!.openAsJAR().use { + it.nextJarEntry + it.readBytes() + } + } + } + + @Test fun `metadata can be used to search`() { val (jarA, _) = makeTestJar() From 642b298d23a4352a19ab1739db6fa9014f698269 Mon Sep 17 00:00:00 2001 From: Austin Moothart Date: Mon, 22 Jan 2018 09:27:35 -0500 Subject: [PATCH 16/36] AWS marketplace getting started docs --- docs/source/aws-vm.rst | 72 +++++++++++++++++++++++++++++++++++++ docs/source/tools-index.rst | 1 + 2 files changed, 73 insertions(+) create mode 100644 docs/source/aws-vm.rst diff --git a/docs/source/aws-vm.rst b/docs/source/aws-vm.rst new file mode 100644 index 0000000000..0a532e339d --- /dev/null +++ b/docs/source/aws-vm.rst @@ -0,0 +1,72 @@ +Building a Corda VM from the AWS Marketplace +============================================ + +To help you design, build and test applications on Corda, called CorDapps, a Corda network AMI can be deployed from the `AWS Marketplace `_. Instructions on running Corda nodes can be found `here `_. + +This Corda network offering builds a pre-configured network of Corda nodes as Ubuntu virtual machines (VM). The network consists of a Notary node and three Corda nodes using version 1 of Corda. The following guide will also show you how to load one of four `Corda Sample apps `_ which demonstrates the basic principles of Corda. When you are ready to go further with developing on Corda and start making contributions to the project head over to the `Corda.net `_. + +Pre-requisites +-------------- +* Ensure you have a registered AWS account which can create virtual machines under your subscription(s) and you are logged on to the `AWS portal `_ +* It is recommended you generate a private-public SSH key pair (see `here `_) + + +Deploying a Corda Network +--------------------------- + +Browse to the `AWS Marketplace `_ and search for Corda. + +Follow the instructions to deploy the AMI to an instance of EC2 which is in a region near to your location. + +Build and Run a Sample CorDapp +------------------------------ +Once the instance is running ssh into the instance using your keypair + +.. sourcecode:: shell + + cd ~/dev + +There are 4 sample apps available by default + +.. sourcecode:: shell + + ubuntu@ip-xxx-xxx-xxx-xxx:~/dev$ ls -la + total 24 + drwxrwxr-x 6 ubuntu ubuntu 4096 Nov 13 21:48 . + drwxr-xr-x 8 ubuntu ubuntu 4096 Nov 21 16:34 .. + drwxrwxr-x 11 ubuntu ubuntu 4096 Oct 31 19:02 cordapp-example + drwxrwxr-x 9 ubuntu ubuntu 4096 Nov 13 21:48 obligation-cordapp + drwxrwxr-x 11 ubuntu ubuntu 4096 Nov 13 21:48 oracle-example + drwxrwxr-x 8 ubuntu ubuntu 4096 Nov 13 21:48 yo-cordapp + +cd into the Corda sample you would like to run. For example: + +.. sourcecode:: shell + + cd cordapp-example/ + +Follow instructions for the specific sample at https://www.corda.net/samples to build and run the Corda sample +For example: with cordapp-example (IOU app) the following commands would be run: + +.. sourcecode:: shell + + ./gradlew deployNodes + ./kotlin-source/build/nodes/runnodes + +Then start the Corda webserver + +.. sourcecode:: shell + + find ~/dev/cordapp-example/kotlin-source/ -name corda-webserver.jar -execdir sh -c 'java -jar {} &' \; + +You can now interact with your running CorDapp. See the instructions `here `_ + +Next Steps +---------- +Now you have built a Corda network and used a basic Corda Cordapp do go and visit the `dedicated Corda website `_ + +Additional support is available on `Stack Overflow `_ and the `Corda Slack channel `_. + +You can build and run any other `Corda samples `_ or your own custom CorDapp here. + +Or to join the growing Corda community and get straight into the Corda open source codebase, head over to the `Github Corda repo `_ diff --git a/docs/source/tools-index.rst b/docs/source/tools-index.rst index 36d24534a0..3955a0cbe1 100644 --- a/docs/source/tools-index.rst +++ b/docs/source/tools-index.rst @@ -8,4 +8,5 @@ Tools demobench node-explorer azure-vm + aws-vm loadtesting \ No newline at end of file From c8e55ae08652fd4c6ffb0f7eb78f2caa6f8a9ba7 Mon Sep 17 00:00:00 2001 From: josecoll Date: Mon, 22 Jan 2018 14:32:04 +0000 Subject: [PATCH 17/36] Backported fix. (#2399) --- .../src/main/kotlin/net/corda/plugins/Cordformation.kt | 3 ++- .../cordformation/src/main/kotlin/net/corda/plugins/Node.kt | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/gradle-plugins/cordformation/src/main/kotlin/net/corda/plugins/Cordformation.kt b/gradle-plugins/cordformation/src/main/kotlin/net/corda/plugins/Cordformation.kt index 26878fa357..43444ca270 100644 --- a/gradle-plugins/cordformation/src/main/kotlin/net/corda/plugins/Cordformation.kt +++ b/gradle-plugins/cordformation/src/main/kotlin/net/corda/plugins/Cordformation.kt @@ -55,7 +55,8 @@ class Cordformation : Plugin { override fun apply(project: Project) { Utils.createCompileConfiguration("cordapp", project) Utils.createRuntimeConfiguration(CORDFORMATION_TYPE, project) - val jolokiaVersion = project.rootProject.ext("jolokia_version") + // TODO: improve how we re-use existing declared external variables from root gradle.build + val jolokiaVersion = try { project.rootProject.ext("jolokia_version") } catch (e: Exception) { "1.3.7" } project.dependencies.add(CORDFORMATION_TYPE, "org.jolokia:jolokia-jvm:$jolokiaVersion:agent") } } diff --git a/gradle-plugins/cordformation/src/main/kotlin/net/corda/plugins/Node.kt b/gradle-plugins/cordformation/src/main/kotlin/net/corda/plugins/Node.kt index e5397ad5aa..1f0cdc9701 100644 --- a/gradle-plugins/cordformation/src/main/kotlin/net/corda/plugins/Node.kt +++ b/gradle-plugins/cordformation/src/main/kotlin/net/corda/plugins/Node.kt @@ -127,7 +127,8 @@ class Node(private val project: Project) : CordformNode() { * Installs the jolokia monitoring agent JAR to the node/drivers directory */ private fun installAgentJar() { - val jolokiaVersion = project.rootProject.ext("jolokia_version") + // TODO: improve how we re-use existing declared external variables from root gradle.build + val jolokiaVersion = try { project.rootProject.ext("jolokia_version") } catch (e: Exception) { "1.3.7" } val agentJar = project.configuration("runtime").files { (it.group == "org.jolokia") && (it.name == "jolokia-jvm") && From 443afb3515e7fe276eae3ae3fec2130fb3227551 Mon Sep 17 00:00:00 2001 From: Joel Dudley Date: Mon, 22 Jan 2018 16:08:26 +0000 Subject: [PATCH 18/36] Rethrows a clearer error message when a test tx in a test ledger does not create a valid tx. --- .../src/main/kotlin/net/corda/testing/dsl/TestDSL.kt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/testing/test-utils/src/main/kotlin/net/corda/testing/dsl/TestDSL.kt b/testing/test-utils/src/main/kotlin/net/corda/testing/dsl/TestDSL.kt index 79c66e53a0..8548ecc1fb 100644 --- a/testing/test-utils/src/main/kotlin/net/corda/testing/dsl/TestDSL.kt +++ b/testing/test-utils/src/main/kotlin/net/corda/testing/dsl/TestDSL.kt @@ -233,7 +233,11 @@ data class TestLedgerDSLInterpreter private constructor( val transactionInterpreter = interpretTransactionDsl(transactionBuilder, dsl) if (fillTransaction) fillTransaction(transactionBuilder) // Create the WireTransaction - val wireTransaction = transactionInterpreter.toWireTransaction() + val wireTransaction = try { + transactionInterpreter.toWireTransaction() + } catch (e: IllegalStateException) { + throw IllegalStateException("A transaction-DSL block that is part of a test ledger must return a valid transaction.", e) + } // Record the output states transactionInterpreter.labelToIndexMap.forEach { label, index -> if (label in labelToOutputStateAndRefs) { From 8de2c78dd261ed254db3ad210df4b88f6161cb84 Mon Sep 17 00:00:00 2001 From: James Brown Date: Mon, 22 Jan 2018 16:21:28 +0000 Subject: [PATCH 19/36] Remove jolokia war reference from webserver --- node/capsule/build.gradle | 2 +- .../corda/webserver/internal/NodeWebServer.kt | 16 ---------------- 2 files changed, 1 insertion(+), 17 deletions(-) diff --git a/node/capsule/build.gradle b/node/capsule/build.gradle index de359edc74..3572e594fb 100644 --- a/node/capsule/build.gradle +++ b/node/capsule/build.gradle @@ -42,7 +42,7 @@ task buildCordaJAR(type: FatCapsule, dependsOn: project(':node').compileJava) { capsuleManifest { applicationVersion = corda_release_version - appClassPath = ["jolokia-war-${project.rootProject.ext.jolokia_version}.war"] + // See experimental/quasar-hook/README.md for how to generate. def quasarExcludeExpression = "x(antlr**;bftsmart**;ch**;co.paralleluniverse**;com.codahale**;com.esotericsoftware**;com.fasterxml**;com.google**;com.ibm**;com.intellij**;com.jcabi**;com.nhaarman**;com.opengamma**;com.typesafe**;com.zaxxer**;de.javakaffee**;groovy**;groovyjarjarantlr**;groovyjarjarasm**;io.atomix**;io.github**;io.netty**;jdk**;joptsimple**;junit**;kotlin**;net.bytebuddy**;net.i2p**;org.apache**;org.assertj**;org.bouncycastle**;org.codehaus**;org.crsh**;org.dom4j**;org.fusesource**;org.h2**;org.hamcrest**;org.hibernate**;org.jboss**;org.jcp**;org.joda**;org.junit**;org.mockito**;org.objectweb**;org.objenesis**;org.slf4j**;org.w3c**;org.xml**;org.yaml**;reflectasm**;rx**;org.jolokia**)" javaAgents = ["quasar-core-${quasar_version}-jdk8.jar=${quasarExcludeExpression}"] diff --git a/webserver/src/main/kotlin/net/corda/webserver/internal/NodeWebServer.kt b/webserver/src/main/kotlin/net/corda/webserver/internal/NodeWebServer.kt index e5a2ce254f..4938ca99c1 100644 --- a/webserver/src/main/kotlin/net/corda/webserver/internal/NodeWebServer.kt +++ b/webserver/src/main/kotlin/net/corda/webserver/internal/NodeWebServer.kt @@ -55,22 +55,6 @@ class NodeWebServer(val config: WebServerConfig) { // Note that the web server handlers will all run concurrently, and not on the node thread. val handlerCollection = HandlerCollection() - // Export JMX monitoring statistics and data over REST/JSON. - if (config.exportJMXto.split(',').contains("http")) { - val classpath = System.getProperty("java.class.path").split(System.getProperty("path.separator")) - val warpath = classpath.firstOrNull { it.contains("jolokia-war") && it.endsWith(".war") } - if (warpath != null) { - handlerCollection.addHandler(WebAppContext().apply { - // Find the jolokia WAR file on the classpath. - contextPath = "/monitoring/json" - setInitParameter("mimeType", MediaType.APPLICATION_JSON) - war = warpath - }) - } else { - log.warn("Unable to locate Jolokia WAR on classpath") - } - } - // API, data upload and download to services (attachments, rates oracles etc) handlerCollection.addHandler(buildServletContextHandler(localRpc)) From 70f1fdeb2b3b861c0222dca7da904139beed261f Mon Sep 17 00:00:00 2001 From: Anthony Keenan <34482776+anthonykr3@users.noreply.github.com> Date: Tue, 23 Jan 2018 14:42:30 +0000 Subject: [PATCH 20/36] CORDA-939 Make SerializationEnvironmentRule.env private so as not to expose internals. (#2404) * Make SerializationEnvironment private so as not to expose internals. * Only expose used parts of api * Make properties lateinit * Removing java calls to getEnv * Initialise properties at declaration * Tidy up imports --- .../ForbiddenLambdaSerializationTests.java | 2 +- .../LambdaCheckpointSerializationTest.java | 2 +- .../ContractAttachmentSerializerTest.kt | 5 +++-- .../serialization/SerializationTokenTest.kt | 4 ++-- .../testing/core/SerializationTestHelpers.kt | 15 ++++++++++++--- 5 files changed, 19 insertions(+), 9 deletions(-) diff --git a/node-api/src/test/java/net/corda/nodeapi/internal/serialization/ForbiddenLambdaSerializationTests.java b/node-api/src/test/java/net/corda/nodeapi/internal/serialization/ForbiddenLambdaSerializationTests.java index 34bcefbc0d..317f9d2aea 100644 --- a/node-api/src/test/java/net/corda/nodeapi/internal/serialization/ForbiddenLambdaSerializationTests.java +++ b/node-api/src/test/java/net/corda/nodeapi/internal/serialization/ForbiddenLambdaSerializationTests.java @@ -25,7 +25,7 @@ public final class ForbiddenLambdaSerializationTests { @Before public void setup() { - factory = testSerialization.getEnv().getSerializationFactory(); + factory = testSerialization.getSerializationFactory(); } @Test diff --git a/node-api/src/test/java/net/corda/nodeapi/internal/serialization/LambdaCheckpointSerializationTest.java b/node-api/src/test/java/net/corda/nodeapi/internal/serialization/LambdaCheckpointSerializationTest.java index f8f9b4451b..9e84dcc49e 100644 --- a/node-api/src/test/java/net/corda/nodeapi/internal/serialization/LambdaCheckpointSerializationTest.java +++ b/node-api/src/test/java/net/corda/nodeapi/internal/serialization/LambdaCheckpointSerializationTest.java @@ -25,7 +25,7 @@ public final class LambdaCheckpointSerializationTest { @Before public void setup() { - factory = testSerialization.getEnv().getSerializationFactory(); + factory = testSerialization.getSerializationFactory(); context = new SerializationContextImpl(KryoSerializationSchemeKt.getKryoHeaderV0_1(), this.getClass().getClassLoader(), AllWhitelist.INSTANCE, Maps.newHashMap(), true, SerializationContext.UseCase.Checkpoint); } diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/ContractAttachmentSerializerTest.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/ContractAttachmentSerializerTest.kt index 7c009212dd..383c90e196 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/ContractAttachmentSerializerTest.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/ContractAttachmentSerializerTest.kt @@ -25,10 +25,11 @@ class ContractAttachmentSerializerTest { private lateinit var context: SerializationContext private lateinit var contextWithToken: SerializationContext private val mockServices = MockServices(emptyList(), rigorousMock(), CordaX500Name("MegaCorp", "London", "GB")) + @Before fun setup() { - factory = testSerialization.env.serializationFactory - context = testSerialization.env.checkpointContext + factory = testSerialization.serializationFactory + context = testSerialization.checkpointContext contextWithToken = context.withTokenContext(SerializeAsTokenContextImpl(Any(), factory, context, mockServices)) } diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/SerializationTokenTest.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/SerializationTokenTest.kt index 1373f363c1..cd10488152 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/SerializationTokenTest.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/SerializationTokenTest.kt @@ -25,8 +25,8 @@ class SerializationTokenTest { @Before fun setup() { - factory = testSerialization.env.serializationFactory - context = testSerialization.env.checkpointContext.withWhitelisted(SingletonSerializationToken::class.java) + factory = testSerialization.serializationFactory + context = testSerialization.checkpointContext.withWhitelisted(SingletonSerializationToken::class.java) } // Large tokenizable object so we can tell from the smaller number of serialized bytes it was actually tokenized diff --git a/testing/test-utils/src/main/kotlin/net/corda/testing/core/SerializationTestHelpers.kt b/testing/test-utils/src/main/kotlin/net/corda/testing/core/SerializationTestHelpers.kt index 4a1a8083d4..698003380d 100644 --- a/testing/test-utils/src/main/kotlin/net/corda/testing/core/SerializationTestHelpers.kt +++ b/testing/test-utils/src/main/kotlin/net/corda/testing/core/SerializationTestHelpers.kt @@ -1,10 +1,16 @@ package net.corda.testing.core -import com.nhaarman.mockito_kotlin.* +import com.nhaarman.mockito_kotlin.any +import com.nhaarman.mockito_kotlin.doAnswer +import com.nhaarman.mockito_kotlin.doNothing +import com.nhaarman.mockito_kotlin.whenever import net.corda.client.rpc.internal.KryoClientSerializationScheme import net.corda.core.DoNotImplement import net.corda.core.internal.staticField -import net.corda.core.serialization.internal.* +import net.corda.core.serialization.internal.SerializationEnvironment +import net.corda.core.serialization.internal.SerializationEnvironmentImpl +import net.corda.core.serialization.internal._globalSerializationEnv +import net.corda.core.serialization.internal.effectiveSerializationEnv import net.corda.node.serialization.KryoServerSerializationScheme import net.corda.nodeapi.internal.serialization.* import net.corda.nodeapi.internal.serialization.amqp.AMQPClientSerializationScheme @@ -42,7 +48,10 @@ class SerializationEnvironmentRule(private val inheritable: Boolean = false) : T } } - lateinit var env: SerializationEnvironment + private lateinit var env: SerializationEnvironment + val serializationFactory get() = env.serializationFactory + val checkpointContext get() = env.checkpointContext + override fun apply(base: Statement, description: Description): Statement { init(description.toString()) return object : Statement() { From 142f52fa82628e885ba20386442d102dc255708c Mon Sep 17 00:00:00 2001 From: Michele Sollecito Date: Tue, 23 Jan 2018 16:23:37 +0000 Subject: [PATCH 21/36] [CORDA:936]: Enable RPC layer to work with SSL --- .../Explorer___demo_nodes.xml | 15 -- .../Explorer___demo_nodes__simulation_.xml | 15 -- .../corda/client/jfx/NodeMonitorModelTest.kt | 4 +- .../client/rpc/CordaRPCJavaClientTest.java | 2 +- .../corda/client/rpc/CordaRPCClientTest.kt | 2 +- .../net/corda/client/rpc/CordaRPCClient.kt | 7 +- docs/source/changelog.rst | 14 ++ docs/source/clientrpc.rst | 5 + docs/source/corda-configuration-file.rst | 23 +- .../src/main/resources/example-node.conf | 7 +- .../nodeapi/internal/KeyStoreConfigHelpers.kt | 37 ++- .../internal/config/ConfigUtilities.kt | 2 +- .../internal/crypto/KeyStoreUtilities.kt | 2 + .../kotlin/net/corda/node/AuthDBTests.kt | 2 +- .../net/corda/node/amqp/AMQPBridgeTest.kt | 7 +- .../net/corda/node/amqp/ProtonWrapperTests.kt | 3 +- .../node/services/network/NetworkMapTest.kt | 2 +- .../net/corda/node/services/rpc/RpcSslTest.kt | 64 +++++ .../statemachine/LargeTransactionsTest.kt | 3 +- .../messaging/MQSecurityAsNodeTest.kt | 6 +- .../services/messaging/MQSecurityAsRPCTest.kt | 4 +- .../services/messaging/MQSecurityTest.kt | 63 ++--- .../services/messaging/P2PMQSecurityTest.kt | 56 +++++ .../services/messaging/RPCMQSecurityTest.kt | 57 +++++ .../test/node/NodeStatePersistenceTests.kt | 5 +- .../corda/node/internal/LifecycleSupport.kt | 13 ++ .../kotlin/net/corda/node/internal/Node.kt | 50 +++- .../node/internal/artemis/ArtemisBroker.kt | 17 ++ .../artemis/CertificateChainCheckPolicy.kt | 82 +++++++ .../artemis/SecureArtemisConfiguration.kt | 15 ++ .../node/services/config/NodeConfiguration.kt | 58 ++++- .../corda/node/services/config/SslOptions.kt | 13 ++ .../services/config/rpc/NodeRpcOptions.kt | 12 + .../messaging/ArtemisMessagingClient.kt | 2 +- .../messaging/ArtemisMessagingServer.kt | 219 ++---------------- .../services/messaging/RPCMessagingClient.kt | 6 +- .../node/services/rpc/ArtemisRpcBroker.kt | 108 +++++++++ .../node/services/rpc/NodeLoginModule.kt | 169 ++++++++++++++ .../node/services/rpc/RolesAdderOnLogin.kt | 36 +++ .../services/rpc/RpcBrokerConfiguration.kt | 131 +++++++++++ node/src/main/resources/reference.conf | 6 +- .../config/NodeConfigurationImplTest.kt | 54 +++-- ...sagingTests.kt => ArtemisMessagingTest.kt} | 8 +- .../node/services/rpc/ArtemisRpcTests.kt | 215 +++++++++++++++++ .../testsupport/UnsafeCertificatesFactory.kt | 206 ++++++++++++++++ .../kotlin/net/corda/irs/IRSDemoTest.kt | 2 +- .../net/corda/test/spring/SpringDriver.kt | 2 +- .../net/corda/traderdemo/TraderDemoTest.kt | 4 +- .../kotlin/net/corda/testing/driver/Driver.kt | 18 +- .../testing/node/internal/DriverDSLImpl.kt | 32 ++- .../testing/node/internal/NodeBasedTest.kt | 7 +- .../testing/internal/InternalTestUtils.kt | 21 ++ .../corda/demobench/model/NodeConfigTest.kt | 2 +- .../net/corda/explorer/ExplorerSimulation.kt | 2 +- .../net/corda/webserver/WebServerConfig.kt | 12 +- 55 files changed, 1563 insertions(+), 366 deletions(-) delete mode 100644 .idea/runConfigurations/Explorer___demo_nodes.xml delete mode 100644 .idea/runConfigurations/Explorer___demo_nodes__simulation_.xml create mode 100644 node/src/integration-test/kotlin/net/corda/node/services/rpc/RpcSslTest.kt create mode 100644 node/src/integration-test/kotlin/net/corda/services/messaging/P2PMQSecurityTest.kt create mode 100644 node/src/integration-test/kotlin/net/corda/services/messaging/RPCMQSecurityTest.kt create mode 100644 node/src/main/kotlin/net/corda/node/internal/LifecycleSupport.kt create mode 100644 node/src/main/kotlin/net/corda/node/internal/artemis/ArtemisBroker.kt create mode 100644 node/src/main/kotlin/net/corda/node/internal/artemis/CertificateChainCheckPolicy.kt create mode 100644 node/src/main/kotlin/net/corda/node/internal/artemis/SecureArtemisConfiguration.kt create mode 100644 node/src/main/kotlin/net/corda/node/services/config/SslOptions.kt create mode 100644 node/src/main/kotlin/net/corda/node/services/config/rpc/NodeRpcOptions.kt create mode 100644 node/src/main/kotlin/net/corda/node/services/rpc/ArtemisRpcBroker.kt create mode 100644 node/src/main/kotlin/net/corda/node/services/rpc/NodeLoginModule.kt create mode 100644 node/src/main/kotlin/net/corda/node/services/rpc/RolesAdderOnLogin.kt create mode 100644 node/src/main/kotlin/net/corda/node/services/rpc/RpcBrokerConfiguration.kt rename node/src/test/kotlin/net/corda/node/services/messaging/{ArtemisMessagingTests.kt => ArtemisMessagingTest.kt} (95%) create mode 100644 node/src/test/kotlin/net/corda/node/services/rpc/ArtemisRpcTests.kt create mode 100644 node/src/test/kotlin/net/corda/node/testsupport/UnsafeCertificatesFactory.kt diff --git a/.idea/runConfigurations/Explorer___demo_nodes.xml b/.idea/runConfigurations/Explorer___demo_nodes.xml deleted file mode 100644 index 42dcfcb487..0000000000 --- a/.idea/runConfigurations/Explorer___demo_nodes.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - \ No newline at end of file diff --git a/.idea/runConfigurations/Explorer___demo_nodes__simulation_.xml b/.idea/runConfigurations/Explorer___demo_nodes__simulation_.xml deleted file mode 100644 index 6671745713..0000000000 --- a/.idea/runConfigurations/Explorer___demo_nodes__simulation_.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - \ No newline at end of file diff --git a/client/jfx/src/integration-test/kotlin/net/corda/client/jfx/NodeMonitorModelTest.kt b/client/jfx/src/integration-test/kotlin/net/corda/client/jfx/NodeMonitorModelTest.kt index 4050d10497..f8f5c58dcf 100644 --- a/client/jfx/src/integration-test/kotlin/net/corda/client/jfx/NodeMonitorModelTest.kt +++ b/client/jfx/src/integration-test/kotlin/net/corda/client/jfx/NodeMonitorModelTest.kt @@ -75,7 +75,7 @@ class NodeMonitorModelTest { vaultUpdates = monitor.vaultUpdates.bufferUntilSubscribed() networkMapUpdates = monitor.networkMap.bufferUntilSubscribed() - monitor.register(aliceNodeHandle.configuration.rpcAddress!!, cashUser.username, cashUser.password) + monitor.register(aliceNodeHandle.configuration.rpcOptions.address!!, cashUser.username, cashUser.password) rpc = monitor.proxyObservable.value!! notaryParty = defaultNotaryIdentity @@ -83,7 +83,7 @@ class NodeMonitorModelTest { bobNode = bobNodeHandle.nodeInfo val monitorBob = NodeMonitorModel() stateMachineUpdatesBob = monitorBob.stateMachineUpdates.bufferUntilSubscribed() - monitorBob.register(bobNodeHandle.configuration.rpcAddress!!, cashUser.username, cashUser.password) + monitorBob.register(bobNodeHandle.configuration.rpcOptions.address!!, cashUser.username, cashUser.password) rpcBob = monitorBob.proxyObservable.value!! runTest() } diff --git a/client/rpc/src/integration-test/java/net/corda/client/rpc/CordaRPCJavaClientTest.java b/client/rpc/src/integration-test/java/net/corda/client/rpc/CordaRPCJavaClientTest.java index e313ac8d10..dc23b13d0a 100644 --- a/client/rpc/src/integration-test/java/net/corda/client/rpc/CordaRPCJavaClientTest.java +++ b/client/rpc/src/integration-test/java/net/corda/client/rpc/CordaRPCJavaClientTest.java @@ -57,7 +57,7 @@ public class CordaRPCJavaClientTest extends NodeBasedTest { @Before public void setUp() throws Exception { node = startNode(ALICE_NAME, 1, singletonList(rpcUser)); - client = new CordaRPCClient(requireNonNull(node.getInternals().getConfiguration().getRpcAddress())); + client = new CordaRPCClient(requireNonNull(node.getInternals().getConfiguration().getRpcOptions().getAddress())); } @After diff --git a/client/rpc/src/integration-test/kotlin/net/corda/client/rpc/CordaRPCClientTest.kt b/client/rpc/src/integration-test/kotlin/net/corda/client/rpc/CordaRPCClientTest.kt index d3d3d4f3ca..bfb77b4aeb 100644 --- a/client/rpc/src/integration-test/kotlin/net/corda/client/rpc/CordaRPCClientTest.kt +++ b/client/rpc/src/integration-test/kotlin/net/corda/client/rpc/CordaRPCClientTest.kt @@ -53,7 +53,7 @@ class CordaRPCClientTest : NodeBasedTest(listOf("net.corda.finance.contracts", C @Before fun setUp() { node = startNode(ALICE_NAME, rpcUsers = listOf(rpcUser)) - client = CordaRPCClient(node.internals.configuration.rpcAddress!!) + client = CordaRPCClient(node.internals.configuration.rpcOptions.address!!) identity = node.info.identityFromX500Name(ALICE_NAME) } diff --git a/client/rpc/src/main/kotlin/net/corda/client/rpc/CordaRPCClient.kt b/client/rpc/src/main/kotlin/net/corda/client/rpc/CordaRPCClient.kt index c09f621550..0c72192854 100644 --- a/client/rpc/src/main/kotlin/net/corda/client/rpc/CordaRPCClient.kt +++ b/client/rpc/src/main/kotlin/net/corda/client/rpc/CordaRPCClient.kt @@ -10,6 +10,7 @@ import net.corda.core.serialization.internal.effectiveSerializationEnv import net.corda.core.utilities.NetworkHostAndPort import net.corda.nodeapi.ArtemisTcpTransport.Companion.tcpTransport import net.corda.nodeapi.ConnectionDirection +import net.corda.nodeapi.internal.config.SSLConfiguration import net.corda.nodeapi.internal.serialization.KRYO_RPC_CLIENT_CONTEXT import java.time.Duration @@ -67,10 +68,12 @@ data class CordaRPCClientConfiguration(val connectionMaxRetryInterval: Duration) * * @param hostAndPort The network address to connect to. * @param configuration An optional configuration used to tweak client behaviour. + * @param sslConfiguration An optional [SSLConfiguration] used to enable secure communication with the server. */ class CordaRPCClient @JvmOverloads constructor( hostAndPort: NetworkHostAndPort, - configuration: CordaRPCClientConfiguration = CordaRPCClientConfiguration.DEFAULT + configuration: CordaRPCClientConfiguration = CordaRPCClientConfiguration.DEFAULT, + sslConfiguration: SSLConfiguration? = null ) { init { try { @@ -85,7 +88,7 @@ class CordaRPCClient @JvmOverloads constructor( } private val rpcClient = RPCClient( - tcpTransport(ConnectionDirection.Outbound(), hostAndPort, config = null), + tcpTransport(ConnectionDirection.Outbound(), hostAndPort, config = sslConfiguration), configuration.toRpcClientConfiguration(), KRYO_RPC_CLIENT_CONTEXT ) diff --git a/docs/source/changelog.rst b/docs/source/changelog.rst index 4119f08ce6..7a1eb2d3bc 100644 --- a/docs/source/changelog.rst +++ b/docs/source/changelog.rst @@ -6,6 +6,20 @@ from the previous milestone release. UNRELEASED ---------- +* Separated our pre-existing Artemis broker into an RPC broker and a P2P broker. + +* Refactored ``NodeConfiguration`` to expose ``NodeRpcOptions`` (using top-level "rpcAddress" property still works with warning). + +* Modified ``CordaRPCClient`` constructor to take a ``SSLConfiguration?`` additional parameter, defaulted to ``null``. + +* Introduced ``CertificateChainCheckPolicy.UsernameMustMatchCommonName`` sub-type, allowing customers to optionally enforce username == CN condition on RPC SSL certificates. + +* Modified ``DriverDSL`` and sub-types to allow specifying RPC settings for the Node. + +* Modified the ``DriverDSL`` to start Cordformation nodes allowing automatic generation of "rpcSettings.adminAddress" in case "rcpSettings.useSsl" is ``false`` (the default). + +* Introduced ``UnsafeCertificatesFactory`` allowing programmatic generation of X509 certificates for test purposes. + * JPA Mapping annotations for States extending ``CommonSchemaV1.LinearState`` and ``CommonSchemaV1.FungibleState`` on the `participants` collection need to be moved to the actual class. This allows to properly specify the unique table name per a collection. See: DummyDealStateSchemaV1.PersistentDummyDealState diff --git a/docs/source/clientrpc.rst b/docs/source/clientrpc.rst index ae30828149..0b9e83afa4 100644 --- a/docs/source/clientrpc.rst +++ b/docs/source/clientrpc.rst @@ -245,6 +245,11 @@ Wire protocol ------------- The client RPC wire protocol is defined and documented in ``net/corda/client/rpc/RPCApi.kt``. +Wire security +------------- +``CordaRPCClient`` has an optional constructor parameter of type ``SSLConfiguration``, defaulted to ``null``, which allows +communication with the node using SSL. Default ``null`` value means no SSL used in the context of RPC. + Whitelisting classes with the Corda node ---------------------------------------- CorDapps must whitelist any classes used over RPC with Corda's serialization framework, unless they are whitelisted by diff --git a/docs/source/corda-configuration-file.rst b/docs/source/corda-configuration-file.rst index 1a81da1e3f..a32613acc7 100644 --- a/docs/source/corda-configuration-file.rst +++ b/docs/source/corda-configuration-file.rst @@ -38,7 +38,12 @@ Simple Notary configuration file. keyStorePassword : "cordacadevpass" trustStorePassword : "trustpass" p2pAddress : "localhost:12345" - rpcAddress : "localhost:12346" + rpcSettings = { + useSsl = false + standAloneBroker = false + address : "my-corda-node:10003" + adminAddress : "my-corda-node:10004" + } webAddress : "localhost:12347" notary : { validating : false @@ -87,7 +92,21 @@ path to the node's base directory. here must be externally accessible when running nodes across a cluster of machines. If the provided host is unreachable, the node will try to auto-discover its public one. -:rpcAddress: The address of the RPC system on which RPC requests can be made to the node. If not provided then the node will run without RPC. +:rpcAddress: The address of the RPC system on which RPC requests can be made to the node. If not provided then the node will run without RPC. This is now deprecated in favour of the ``rpcSettings`` block. + +:rpcSettings: Options for the RPC server. + + :useSsl: (optional) boolean, indicates whether the node should require clients to use SSL for RPC connections, defaulted to ``false``. + :standAloneBroker: (optional) boolean, indicates whether the node will connect to a standalone broker for RPC, defaulted to ``false``. + :address: (optional) host and port for the RPC server binding, if any. + :adminAddress: (optional) host and port for the RPC admin binding (only required when ``useSsl`` is ``false``, because the node connects to Artemis using SSL to ensure admin privileges are not accessible outside the node). + :ssl: (optional) SSL settings for the RPC server. + + :keyStorePassword: password for the key store. + :trustStorePassword: password for the trust store. + :certificatesDirectory: directory in which the stores will be searched, unless absolute paths are provided. + :sslKeystore: absolute path to the ssl key store, defaulted to ``certificatesDirectory / "sslkeystore.jks"``. + :trustStoreFile: absolute path to the trust store, defaulted to ``certificatesDirectory / "truststore.jks"``. :security: Contains various nested fields controlling user authentication/authorization, in particular for RPC accesses. See :doc:`clientrpc` for details. diff --git a/docs/source/example-code/src/main/resources/example-node.conf b/docs/source/example-code/src/main/resources/example-node.conf index 3715ff22e8..538f118df5 100644 --- a/docs/source/example-code/src/main/resources/example-node.conf +++ b/docs/source/example-code/src/main/resources/example-node.conf @@ -8,7 +8,12 @@ dataSourceProperties : { "dataSource.password" : "" } p2pAddress : "my-corda-node:10002" -rpcAddress : "my-corda-node:10003" +rpcSettings = { + useSsl = false + standAloneBroker = false + address : "my-corda-node:10003" + adminAddress : "my-corda-node:10004" +} webAddress : "localhost:10004" rpcUsers : [ { username=user1, password=letmein, permissions=[ StartFlow.net.corda.protocols.CashProtocol ] } diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/KeyStoreConfigHelpers.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/KeyStoreConfigHelpers.kt index 0c0f8a17a1..bbbde7aec4 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/KeyStoreConfigHelpers.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/KeyStoreConfigHelpers.kt @@ -23,15 +23,22 @@ fun SSLConfiguration.createDevKeyStores(legalName: CordaX500Name, intermediateCa: CertificateAndKeyPair = DEV_INTERMEDIATE_CA) { val (nodeCaCert, nodeCaKeyPair) = createDevNodeCa(intermediateCa, legalName) - loadOrCreateKeyStore(nodeKeystore, keyStorePassword).apply { - addOrReplaceKey( - X509Utilities.CORDA_CLIENT_CA, - nodeCaKeyPair.private, - keyStorePassword.toCharArray(), - arrayOf(nodeCaCert, intermediateCa.certificate, rootCert)) - save(nodeKeystore, keyStorePassword) - } + createDevKeyStores(rootCert, intermediateCa, nodeCaCert, nodeCaKeyPair, legalName) +} +/** + * Create the node and SSL key stores needed by a node. The node key store will be populated with a node CA cert (using + * the given legal name), and the SSL key store will store the TLS cert which is a sub-cert of the node CA. + */ +fun SSLConfiguration.createDevKeyStores(rootCert: X509Certificate, intermediateCa: CertificateAndKeyPair, nodeCaCert: X509Certificate, nodeCaKeyPair: KeyPair, legalName: CordaX500Name) { + createNodeKeyStore(nodeCaCert, nodeCaKeyPair, intermediateCa, rootCert) + createSslKeyStore(nodeCaCert, nodeCaKeyPair, legalName, intermediateCa, rootCert) +} + +/** + * Create the SSL key store needed by a node. + */ +fun SSLConfiguration.createSslKeyStore(nodeCaCert: X509Certificate, nodeCaKeyPair: KeyPair, legalName: CordaX500Name, intermediateCa: CertificateAndKeyPair, rootCert: X509Certificate) { val tlsKeyPair = generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) val tlsCert = X509Utilities.createCertificate(CertificateType.TLS, nodeCaCert, nodeCaKeyPair, legalName.x500Principal, tlsKeyPair.public) @@ -45,6 +52,20 @@ fun SSLConfiguration.createDevKeyStores(legalName: CordaX500Name, } } +/** + * Create the node key store needed by a node. + */ +fun SSLConfiguration.createNodeKeyStore(nodeCaCert: X509Certificate, nodeCaKeyPair: KeyPair, intermediateCa: CertificateAndKeyPair, rootCert: X509Certificate) { + loadOrCreateKeyStore(nodeKeystore, keyStorePassword).apply { + addOrReplaceKey( + X509Utilities.CORDA_CLIENT_CA, + nodeCaKeyPair.private, + keyStorePassword.toCharArray(), + arrayOf(nodeCaCert, intermediateCa.certificate, rootCert)) + save(nodeKeystore, keyStorePassword) + } +} + fun createDevNetworkMapCa(rootCa: CertificateAndKeyPair = DEV_ROOT_CA): CertificateAndKeyPair { val keyPair = generateKeyPair() val cert = X509Utilities.createCertificate( diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/config/ConfigUtilities.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/config/ConfigUtilities.kt index 53a9ab7ce2..94997d34b8 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/config/ConfigUtilities.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/config/ConfigUtilities.kt @@ -38,7 +38,7 @@ operator fun Config.getValue(receiver: Any, metadata: KProperty<*>): T } fun Config.parseAs(clazz: KClass): T { - require(clazz.isData) { "Only Kotlin data classes can be parsed" } + require(clazz.isData) { "Only Kotlin data classes can be parsed. Offending: ${clazz.qualifiedName}" } val constructor = clazz.primaryConstructor!! val args = constructor.parameters .filterNot { it.isOptional && !hasPath(it.name!!) } diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/crypto/KeyStoreUtilities.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/crypto/KeyStoreUtilities.kt index d060c756b0..d00f994d9c 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/crypto/KeyStoreUtilities.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/crypto/KeyStoreUtilities.kt @@ -3,6 +3,7 @@ package net.corda.nodeapi.internal.crypto import net.corda.core.crypto.Crypto +import net.corda.core.internal.createDirectories import net.corda.core.internal.exists import net.corda.core.internal.read import net.corda.core.internal.write @@ -30,6 +31,7 @@ fun loadOrCreateKeyStore(keyStoreFilePath: Path, storePassword: String): KeyStor keyStoreFilePath.read { keyStore.load(it, pass) } } else { keyStore.load(null, pass) + keyStoreFilePath.parent.createDirectories() keyStoreFilePath.write { keyStore.store(it, pass) } } return keyStore diff --git a/node/src/integration-test/kotlin/net/corda/node/AuthDBTests.kt b/node/src/integration-test/kotlin/net/corda/node/AuthDBTests.kt index db4ec0b049..0485a5e70f 100644 --- a/node/src/integration-test/kotlin/net/corda/node/AuthDBTests.kt +++ b/node/src/integration-test/kotlin/net/corda/node/AuthDBTests.kt @@ -94,7 +94,7 @@ class AuthDBTests : NodeBasedTest() { ) node = startNode(ALICE_NAME, rpcUsers = emptyList(), configOverrides = securityConfig) - client = CordaRPCClient(node.internals.configuration.rpcAddress!!) + client = CordaRPCClient(node.internals.configuration.rpcOptions.address!!) } @Test diff --git a/node/src/integration-test/kotlin/net/corda/node/amqp/AMQPBridgeTest.kt b/node/src/integration-test/kotlin/net/corda/node/amqp/AMQPBridgeTest.kt index 33555018bb..dea8798d4a 100644 --- a/node/src/integration-test/kotlin/net/corda/node/amqp/AMQPBridgeTest.kt +++ b/node/src/integration-test/kotlin/net/corda/node/amqp/AMQPBridgeTest.kt @@ -161,7 +161,6 @@ class AMQPBridgeTest { artemisServer.stop() artemisLegacyClient.stop() artemisLegacyServer.stop() - } private fun createArtemis(sourceQueueName: String?): Pair { @@ -170,6 +169,7 @@ class AMQPBridgeTest { doReturn(ALICE_NAME).whenever(it).myLegalName doReturn("trustpass").whenever(it).trustStorePassword doReturn("cordacadevpass").whenever(it).keyStorePassword + doReturn(artemisAddress).whenever(it).p2pAddress doReturn("").whenever(it).exportJMXto doReturn(emptyList()).whenever(it).certificateChainCheckPolicies doReturn(true).whenever(it).useAMQPBridges @@ -180,7 +180,7 @@ class AMQPBridgeTest { doReturn(listOf(NodeInfo(listOf(amqpAddress), listOf(BOB.identity), 1, 1L))).whenever(it).getNodesByOwningKeyIndex(any()) } val userService = rigorousMock() - val artemisServer = ArtemisMessagingServer(artemisConfig, artemisPort, null, networkMap, userService, MAX_MESSAGE_SIZE) + val artemisServer = ArtemisMessagingServer(artemisConfig, artemisPort, networkMap, userService, MAX_MESSAGE_SIZE) val artemisClient = ArtemisMessagingClient(artemisConfig, artemisAddress, MAX_MESSAGE_SIZE) artemisServer.start() artemisClient.start() @@ -198,6 +198,7 @@ class AMQPBridgeTest { doReturn(BOB_NAME).whenever(it).myLegalName doReturn("trustpass").whenever(it).trustStorePassword doReturn("cordacadevpass").whenever(it).keyStorePassword + doReturn(artemisAddress).whenever(it).p2pAddress doReturn("").whenever(it).exportJMXto doReturn(emptyList()).whenever(it).certificateChainCheckPolicies doReturn(false).whenever(it).useAMQPBridges @@ -209,7 +210,7 @@ class AMQPBridgeTest { doReturn(listOf(NodeInfo(listOf(artemisAddress), listOf(ALICE.identity), 1, 1L))).whenever(it).getNodesByOwningKeyIndex(any()) } val userService = rigorousMock() - val artemisServer = ArtemisMessagingServer(artemisConfig, artemisPort2, null, networkMap, userService, MAX_MESSAGE_SIZE) + val artemisServer = ArtemisMessagingServer(artemisConfig, artemisPort2, networkMap, userService, MAX_MESSAGE_SIZE) val artemisClient = ArtemisMessagingClient(artemisConfig, artemisAddress2, MAX_MESSAGE_SIZE) artemisServer.start() artemisClient.start() diff --git a/node/src/integration-test/kotlin/net/corda/node/amqp/ProtonWrapperTests.kt b/node/src/integration-test/kotlin/net/corda/node/amqp/ProtonWrapperTests.kt index 6c46f6f407..5d198758c8 100644 --- a/node/src/integration-test/kotlin/net/corda/node/amqp/ProtonWrapperTests.kt +++ b/node/src/integration-test/kotlin/net/corda/node/amqp/ProtonWrapperTests.kt @@ -226,6 +226,7 @@ class ProtonWrapperTests { doReturn(CHARLIE_NAME).whenever(it).myLegalName doReturn("trustpass").whenever(it).trustStorePassword doReturn("cordacadevpass").whenever(it).keyStorePassword + doReturn(NetworkHostAndPort("0.0.0.0", artemisPort)).whenever(it).p2pAddress doReturn("").whenever(it).exportJMXto doReturn(emptyList()).whenever(it).certificateChainCheckPolicies doReturn(true).whenever(it).useAMQPBridges @@ -236,7 +237,7 @@ class ProtonWrapperTests { doReturn(never()).whenever(it).changed } val userService = rigorousMock() - val server = ArtemisMessagingServer(artemisConfig, artemisPort, null, networkMap, userService, MAX_MESSAGE_SIZE) + val server = ArtemisMessagingServer(artemisConfig, artemisPort, networkMap, userService, MAX_MESSAGE_SIZE) val client = ArtemisMessagingClient(artemisConfig, NetworkHostAndPort("localhost", artemisPort), MAX_MESSAGE_SIZE) server.start() client.start() diff --git a/node/src/integration-test/kotlin/net/corda/node/services/network/NetworkMapTest.kt b/node/src/integration-test/kotlin/net/corda/node/services/network/NetworkMapTest.kt index 558fa62ae1..833691cae2 100644 --- a/node/src/integration-test/kotlin/net/corda/node/services/network/NetworkMapTest.kt +++ b/node/src/integration-test/kotlin/net/corda/node/services/network/NetworkMapTest.kt @@ -35,7 +35,7 @@ class NetworkMapTest { val testSerialization = SerializationEnvironmentRule(true) private val cacheTimeout = 1.seconds - private val portAllocation = PortAllocation.Incremental(10000) + private val portAllocation = PortAllocation.RandomFree private lateinit var networkMapServer: NetworkMapServer private lateinit var compatibilityZone: CompatibilityZoneParams diff --git a/node/src/integration-test/kotlin/net/corda/node/services/rpc/RpcSslTest.kt b/node/src/integration-test/kotlin/net/corda/node/services/rpc/RpcSslTest.kt new file mode 100644 index 0000000000..03387d9a13 --- /dev/null +++ b/node/src/integration-test/kotlin/net/corda/node/services/rpc/RpcSslTest.kt @@ -0,0 +1,64 @@ +package net.corda.node.services.rpc + +import net.corda.core.identity.CordaX500Name +import net.corda.core.utilities.getOrThrow +import net.corda.node.services.Permissions.Companion.all +import net.corda.node.testsupport.withCertificates +import net.corda.node.testsupport.withKeyStores +import net.corda.testing.driver.PortAllocation +import net.corda.testing.driver.driver +import net.corda.testing.internal.useSslRpcOverrides +import net.corda.testing.node.User +import org.assertj.core.api.Assertions.assertThat +import org.junit.Test + +class RpcSslTest { + @Test + fun rpc_client_using_ssl() { + val user = User("mark", "dadada", setOf(all())) + withCertificates { server, client, createSelfSigned, createSignedBy -> + val rootCertificate = createSelfSigned(CordaX500Name("SystemUsers/Node", "IT", "R3 London", "London", "London", "GB")) + val markCertificate = createSignedBy(CordaX500Name("mark", "IT", "R3 London", "London", "London", "GB"), rootCertificate) + + // truststore needs to contain root CA for how the driver works... + server.keyStore["cordaclienttls"] = rootCertificate + server.trustStore["cordaclienttls"] = rootCertificate + server.trustStore["mark"] = markCertificate + + client.keyStore["mark"] = markCertificate + client.trustStore["cordaclienttls"] = rootCertificate + + withKeyStores(server, client) { nodeSslOptions, clientSslOptions -> + var successful = false + driver(isDebug = true, startNodesInProcess = true, portAllocation = PortAllocation.RandomFree) { + startNode(rpcUsers = listOf(user), customOverrides = nodeSslOptions.useSslRpcOverrides()).getOrThrow().use { node -> + node.rpcClientToNode(clientSslOptions).start(user.username, user.password).use { connection -> + connection.proxy.apply { + nodeInfo() + successful = true + } + } + } + } + assertThat(successful).isTrue() + } + } + } + + @Test + fun rpc_client_not_using_ssl() { + val user = User("mark", "dadada", setOf(all())) + var successful = false + driver(isDebug = true, startNodesInProcess = true, portAllocation = PortAllocation.RandomFree) { + startNode(rpcUsers = listOf(user)).getOrThrow().use { node -> + node.rpcClientToNode().start(user.username, user.password).use { connection -> + connection.proxy.apply { + nodeInfo() + successful = true + } + } + } + } + assertThat(successful).isTrue() + } +} \ No newline at end of file diff --git a/node/src/integration-test/kotlin/net/corda/node/services/statemachine/LargeTransactionsTest.kt b/node/src/integration-test/kotlin/net/corda/node/services/statemachine/LargeTransactionsTest.kt index c49f63aaef..23c3a987c3 100644 --- a/node/src/integration-test/kotlin/net/corda/node/services/statemachine/LargeTransactionsTest.kt +++ b/node/src/integration-test/kotlin/net/corda/node/services/statemachine/LargeTransactionsTest.kt @@ -11,6 +11,7 @@ import net.corda.core.utilities.getOrThrow import net.corda.testing.contracts.DummyContract import net.corda.testing.contracts.DummyState import net.corda.testing.core.* +import net.corda.testing.driver.PortAllocation import net.corda.testing.driver.driver import net.corda.testing.node.User import org.junit.Test @@ -69,7 +70,7 @@ class LargeTransactionsTest { val bigFile2 = InputStreamAndHash.createInMemoryTestZip(1024 * 1024 * 3, 1) val bigFile3 = InputStreamAndHash.createInMemoryTestZip(1024 * 1024 * 3, 2) val bigFile4 = InputStreamAndHash.createInMemoryTestZip(1024 * 1024 * 3, 3) - driver(startNodesInProcess = true, extraCordappPackagesToScan = listOf("net.corda.testing.contracts")) { + driver(startNodesInProcess = true, extraCordappPackagesToScan = listOf("net.corda.testing.contracts"), portAllocation = PortAllocation.RandomFree) { val rpcUser = User("admin", "admin", setOf("ALL")) val (alice, _) = listOf(ALICE_NAME, BOB_NAME).map { startNode(providedName = it, rpcUsers = listOf(rpcUser)) }.transpose().getOrThrow() alice.rpcClientToNode().use(rpcUser.username, rpcUser.password) { diff --git a/node/src/integration-test/kotlin/net/corda/services/messaging/MQSecurityAsNodeTest.kt b/node/src/integration-test/kotlin/net/corda/services/messaging/MQSecurityAsNodeTest.kt index e8aef9bffd..bc6d9128d3 100644 --- a/node/src/integration-test/kotlin/net/corda/services/messaging/MQSecurityAsNodeTest.kt +++ b/node/src/integration-test/kotlin/net/corda/services/messaging/MQSecurityAsNodeTest.kt @@ -27,7 +27,7 @@ import java.nio.file.Files /** * Runs the security tests with the attacker pretending to be a node on the network. */ -class MQSecurityAsNodeTest : MQSecurityTest() { +class MQSecurityAsNodeTest : P2PMQSecurityTest() { override fun createAttacker(): SimpleMQClient { return clientTo(alice.internals.configuration.p2pAddress) } @@ -67,7 +67,7 @@ class MQSecurityAsNodeTest : MQSecurityTest() { @Test fun `login to a non ssl port as a node user`() { - val attacker = clientTo(alice.internals.configuration.rpcAddress!!, sslConfiguration = null) + val attacker = clientTo(alice.internals.configuration.rpcOptions.address!!, sslConfiguration = null) assertThatExceptionOfType(ActiveMQSecurityException::class.java).isThrownBy { attacker.start(NODE_USER, NODE_USER, enableSSL = false) } @@ -75,7 +75,7 @@ class MQSecurityAsNodeTest : MQSecurityTest() { @Test fun `login to a non ssl port as a peer user`() { - val attacker = clientTo(alice.internals.configuration.rpcAddress!!, sslConfiguration = null) + val attacker = clientTo(alice.internals.configuration.rpcOptions.address!!, sslConfiguration = null) assertThatExceptionOfType(ActiveMQSecurityException::class.java).isThrownBy { attacker.start(PEER_USER, PEER_USER, enableSSL = false) // Login as a peer } diff --git a/node/src/integration-test/kotlin/net/corda/services/messaging/MQSecurityAsRPCTest.kt b/node/src/integration-test/kotlin/net/corda/services/messaging/MQSecurityAsRPCTest.kt index 4609aab319..e06d21eee6 100644 --- a/node/src/integration-test/kotlin/net/corda/services/messaging/MQSecurityAsRPCTest.kt +++ b/node/src/integration-test/kotlin/net/corda/services/messaging/MQSecurityAsRPCTest.kt @@ -6,9 +6,9 @@ import org.junit.Test /** * Runs the security tests with the attacker being a valid RPC user of Alice. */ -class MQSecurityAsRPCTest : MQSecurityTest() { +class MQSecurityAsRPCTest : RPCMQSecurityTest() { override fun createAttacker(): SimpleMQClient { - return clientTo(alice.internals.configuration.rpcAddress!!) + return clientTo(alice.internals.configuration.rpcOptions.address!!) } @Test diff --git a/node/src/integration-test/kotlin/net/corda/services/messaging/MQSecurityTest.kt b/node/src/integration-test/kotlin/net/corda/services/messaging/MQSecurityTest.kt index 86b71bebfd..abbe152173 100644 --- a/node/src/integration-test/kotlin/net/corda/services/messaging/MQSecurityTest.kt +++ b/node/src/integration-test/kotlin/net/corda/services/messaging/MQSecurityTest.kt @@ -21,8 +21,6 @@ import net.corda.node.internal.StartedNode import net.corda.nodeapi.RPCApi import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.INTERNAL_PREFIX import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.NOTIFICATIONS_ADDRESS -import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.P2P_PREFIX -import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.PEERS_PREFIX import net.corda.nodeapi.internal.config.SSLConfiguration import net.corda.testing.core.ALICE_NAME import net.corda.testing.core.BOB_NAME @@ -69,46 +67,6 @@ abstract class MQSecurityTest : NodeBasedTest() { clients.forEach { it.stop() } } - @Test - fun `consume message from P2P queue`() { - assertConsumeAttackFails("$P2P_PREFIX${alice.info.chooseIdentity().owningKey.toStringShort()}") - } - - @Test - fun `consume message from peer queue`() { - val bobParty = startBobAndCommunicateWithAlice() - assertConsumeAttackFails("$PEERS_PREFIX${bobParty.owningKey.toStringShort()}") - } - - @Test - fun `send message to address of peer which has been communicated with`() { - val bobParty = startBobAndCommunicateWithAlice() - assertSendAttackFails("$PEERS_PREFIX${bobParty.owningKey.toStringShort()}") - } - - @Test - fun `create queue for peer which has not been communicated with`() { - val bob = startNode(BOB_NAME) - assertAllQueueCreationAttacksFail("$PEERS_PREFIX${bob.info.chooseIdentity().owningKey.toStringShort()}") - } - - @Test - fun `create queue for unknown peer`() { - val invalidPeerQueue = "$PEERS_PREFIX${generateKeyPair().public.toStringShort()}" - assertAllQueueCreationAttacksFail(invalidPeerQueue) - } - - @Test - fun `consume message from RPC requests queue`() { - assertConsumeAttackFails(RPCApi.RPC_SERVER_QUEUE_NAME) - } - - @Test - fun `consume message from logged in user's RPC queue`() { - val user1Queue = loginToRPCAndGetClientQueue() - assertConsumeAttackFails(user1Queue) - } - @Test fun `create queue for valid RPC user`() { val user1Queue = "${RPCApi.RPC_CLIENT_QUEUE_NAME_PREFIX}.${rpcUser.username}.${random63BitValue()}" @@ -155,9 +113,9 @@ abstract class MQSecurityTest : NodeBasedTest() { } fun loginToRPCAndGetClientQueue(): String { - loginToRPC(alice.internals.configuration.rpcAddress!!, rpcUser) + loginToRPC(alice.internals.configuration.rpcOptions.address!!, rpcUser) val clientQueueQuery = SimpleString("${RPCApi.RPC_CLIENT_QUEUE_NAME_PREFIX}.${rpcUser.username}.*") - val client = clientTo(alice.internals.configuration.rpcAddress!!) + val client = clientTo(alice.internals.configuration.rpcOptions.address!!) client.start(rpcUser.username, rpcUser.password, false) return client.session.addressQuery(clientQueueQuery).queueNames.single().toString() } @@ -178,6 +136,12 @@ abstract class MQSecurityTest : NodeBasedTest() { } } + fun assertAttackFailsNonexistent(queue: String, attack: () -> Unit) { + assertThatExceptionOfType(ActiveMQNonExistentQueueException::class.java) + .isThrownBy(attack) + .withMessageContaining(queue) + } + fun assertNonTempQueueCreationAttackFails(queue: String, durable: Boolean) { val permission = if (durable) "CREATE_DURABLE_QUEUE" else "CREATE_NON_DURABLE_QUEUE" assertAttackFails(queue, permission) { @@ -208,6 +172,15 @@ abstract class MQSecurityTest : NodeBasedTest() { } } + fun assertConsumeAttackFailsNonexistent(queue: String) { + assertAttackFailsNonexistent(queue) { + attacker.session.createConsumer(queue) + } + assertAttackFailsNonexistent(queue) { + attacker.session.createConsumer(queue, true) + } + } + fun assertAttackFails(queue: String, permission: String, attack: () -> Unit) { assertThatExceptionOfType(ActiveMQSecurityException::class.java) .isThrownBy(attack) @@ -215,7 +188,7 @@ abstract class MQSecurityTest : NodeBasedTest() { .withMessageContaining(permission) } - private fun startBobAndCommunicateWithAlice(): Party { + protected fun startBobAndCommunicateWithAlice(): Party { val bob = startNode(BOB_NAME) bob.registerInitiatedFlow(ReceiveFlow::class.java) val bobParty = bob.info.chooseIdentity() diff --git a/node/src/integration-test/kotlin/net/corda/services/messaging/P2PMQSecurityTest.kt b/node/src/integration-test/kotlin/net/corda/services/messaging/P2PMQSecurityTest.kt new file mode 100644 index 0000000000..df8970ff65 --- /dev/null +++ b/node/src/integration-test/kotlin/net/corda/services/messaging/P2PMQSecurityTest.kt @@ -0,0 +1,56 @@ +package net.corda.services.messaging + +import net.corda.core.crypto.generateKeyPair +import net.corda.core.crypto.toStringShort +import net.corda.nodeapi.RPCApi +import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.P2P_PREFIX +import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.PEERS_PREFIX +import net.corda.testing.core.BOB_NAME +import net.corda.testing.core.chooseIdentity +import org.junit.Test + +/** + * Runs a series of MQ-related attacks against a node. Subclasses need to call [startAttacker] to connect + * the attacker to [alice]. + */ +abstract class P2PMQSecurityTest : MQSecurityTest() { + @Test + fun `consume message from P2P queue`() { + assertConsumeAttackFails("$P2P_PREFIX${alice.info.chooseIdentity().owningKey.toStringShort()}") + } + + @Test + fun `consume message from peer queue`() { + val bobParty = startBobAndCommunicateWithAlice() + assertConsumeAttackFails("$PEERS_PREFIX${bobParty.owningKey.toStringShort()}") + } + + @Test + fun `send message to address of peer which has been communicated with`() { + val bobParty = startBobAndCommunicateWithAlice() + assertSendAttackFails("$PEERS_PREFIX${bobParty.owningKey.toStringShort()}") + } + + @Test + fun `create queue for peer which has not been communicated with`() { + val bob = startNode(BOB_NAME) + assertAllQueueCreationAttacksFail("$PEERS_PREFIX${bob.info.chooseIdentity().owningKey.toStringShort()}") + } + + @Test + fun `create queue for unknown peer`() { + val invalidPeerQueue = "$PEERS_PREFIX${generateKeyPair().public.toStringShort()}" + assertAllQueueCreationAttacksFail(invalidPeerQueue) + } + + @Test + fun `consume message from RPC requests queue`() { + assertConsumeAttackFailsNonexistent(RPCApi.RPC_SERVER_QUEUE_NAME) + } + + @Test + fun `consume message from logged in user's RPC queue`() { + val user1Queue = loginToRPCAndGetClientQueue() + assertConsumeAttackFailsNonexistent(user1Queue) + } +} \ No newline at end of file diff --git a/node/src/integration-test/kotlin/net/corda/services/messaging/RPCMQSecurityTest.kt b/node/src/integration-test/kotlin/net/corda/services/messaging/RPCMQSecurityTest.kt new file mode 100644 index 0000000000..b9535b670b --- /dev/null +++ b/node/src/integration-test/kotlin/net/corda/services/messaging/RPCMQSecurityTest.kt @@ -0,0 +1,57 @@ +package net.corda.services.messaging + +import net.corda.core.crypto.generateKeyPair +import net.corda.core.crypto.toStringShort +import net.corda.core.utilities.toBase58String +import net.corda.nodeapi.RPCApi +import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.P2P_PREFIX +import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.PEERS_PREFIX +import net.corda.testing.core.BOB_NAME +import net.corda.testing.core.chooseIdentity +import org.junit.Test + +/** + * Runs a series of MQ-related attacks against a node. Subclasses need to call [startAttacker] to connect + * the attacker to [alice]. + */ +abstract class RPCMQSecurityTest : MQSecurityTest() { + @Test + fun `consume message from P2P queue`() { + assertConsumeAttackFailsNonexistent("$P2P_PREFIX${alice.info.chooseIdentity().owningKey.toStringShort()}") + } + + @Test + fun `consume message from peer queue`() { + val bobParty = startBobAndCommunicateWithAlice() + assertConsumeAttackFailsNonexistent("$PEERS_PREFIX${bobParty.owningKey.toBase58String()}") + } + + @Test + fun `send message to address of peer which has been communicated with`() { + val bobParty = startBobAndCommunicateWithAlice() + assertConsumeAttackFailsNonexistent("$PEERS_PREFIX${bobParty.owningKey.toBase58String()}") + } + + @Test + fun `create queue for peer which has not been communicated with`() { + val bob = startNode(BOB_NAME) + assertConsumeAttackFailsNonexistent("$PEERS_PREFIX${bob.info.chooseIdentity().owningKey.toBase58String()}") + } + + @Test + fun `create queue for unknown peer`() { + val invalidPeerQueue = "$PEERS_PREFIX${generateKeyPair().public.toBase58String()}" + assertConsumeAttackFailsNonexistent(invalidPeerQueue) + } + + @Test + fun `consume message from RPC requests queue`() { + assertConsumeAttackFails(RPCApi.RPC_SERVER_QUEUE_NAME) + } + + @Test + fun `consume message from logged in user's RPC queue`() { + val user1Queue = loginToRPCAndGetClientQueue() + assertConsumeAttackFails(user1Queue) + } +} \ No newline at end of file diff --git a/node/src/integration-test/kotlin/net/corda/test/node/NodeStatePersistenceTests.kt b/node/src/integration-test/kotlin/net/corda/test/node/NodeStatePersistenceTests.kt index f9d84eee28..d711b59ebc 100644 --- a/node/src/integration-test/kotlin/net/corda/test/node/NodeStatePersistenceTests.kt +++ b/node/src/integration-test/kotlin/net/corda/test/node/NodeStatePersistenceTests.kt @@ -21,6 +21,7 @@ import net.corda.node.services.Permissions.Companion.invokeRpc import net.corda.node.services.Permissions.Companion.startFlow import net.corda.testing.node.User import net.corda.testing.core.chooseIdentity +import net.corda.testing.driver.PortAllocation import net.corda.testing.driver.driver import org.junit.Assume.assumeFalse import org.junit.Test @@ -40,7 +41,7 @@ class NodeStatePersistenceTests { val user = User("mark", "dadada", setOf(startFlow(), invokeRpc("vaultQuery"))) val message = Message("Hello world!") - val stateAndRef: StateAndRef? = driver(isDebug = true, startNodesInProcess = isQuasarAgentSpecified()) { + val stateAndRef: StateAndRef? = driver(isDebug = true, startNodesInProcess = isQuasarAgentSpecified(), portAllocation = PortAllocation.RandomFree) { val nodeName = { val nodeHandle = startNode(rpcUsers = listOf(user)).getOrThrow() val nodeName = nodeHandle.nodeInfo.chooseIdentity().name @@ -74,7 +75,7 @@ class NodeStatePersistenceTests { val user = User("mark", "dadada", setOf(startFlow(), invokeRpc("vaultQuery"))) val message = Message("Hello world!") - val stateAndRef: StateAndRef? = driver(isDebug = true, startNodesInProcess = isQuasarAgentSpecified()) { + val stateAndRef: StateAndRef? = driver(isDebug = true, startNodesInProcess = isQuasarAgentSpecified(), portAllocation = PortAllocation.RandomFree) { val nodeName = { val nodeHandle = startNode(rpcUsers = listOf(user)).getOrThrow() val nodeName = nodeHandle.nodeInfo.chooseIdentity().name diff --git a/node/src/main/kotlin/net/corda/node/internal/LifecycleSupport.kt b/node/src/main/kotlin/net/corda/node/internal/LifecycleSupport.kt new file mode 100644 index 0000000000..0ab80198da --- /dev/null +++ b/node/src/main/kotlin/net/corda/node/internal/LifecycleSupport.kt @@ -0,0 +1,13 @@ +package net.corda.node.internal + +interface LifecycleSupport : Startable, Stoppable + +interface Stoppable { + fun stop() +} + +interface Startable { + fun start() + + val started: Boolean +} \ No newline at end of file diff --git a/node/src/main/kotlin/net/corda/node/internal/Node.kt b/node/src/main/kotlin/net/corda/node/internal/Node.kt index c4149f74ac..d706aaffd0 100644 --- a/node/src/main/kotlin/net/corda/node/internal/Node.kt +++ b/node/src/main/kotlin/net/corda/node/internal/Node.kt @@ -4,6 +4,7 @@ import com.codahale.metrics.JmxReporter import net.corda.core.concurrent.CordaFuture import net.corda.core.internal.concurrent.openFuture import net.corda.core.internal.concurrent.thenMatch +import net.corda.core.internal.div import net.corda.core.internal.uncheckedCast import net.corda.core.messaging.RPCOps import net.corda.core.node.NodeInfo @@ -15,6 +16,8 @@ import net.corda.core.serialization.internal.nodeSerializationEnv import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.contextLogger import net.corda.node.VersionInfo +import net.corda.node.internal.artemis.ArtemisBroker +import net.corda.node.internal.artemis.BrokerAddresses import net.corda.node.internal.cordapp.CordappLoader import net.corda.node.internal.security.RPCSecurityManagerImpl import net.corda.node.serialization.KryoServerSerializationScheme @@ -23,6 +26,7 @@ import net.corda.node.services.config.NodeConfiguration import net.corda.node.services.config.SecurityConfiguration import net.corda.node.services.config.VerifierType import net.corda.node.services.messaging.* +import net.corda.node.services.rpc.ArtemisRpcBroker import net.corda.node.services.transactions.InMemoryTransactionVerifierService import net.corda.node.utilities.AddressUtils import net.corda.node.utilities.AffinityExecutor @@ -36,6 +40,7 @@ import org.slf4j.Logger import org.slf4j.LoggerFactory import rx.Scheduler import rx.schedulers.Schedulers +import java.nio.file.Path import java.security.PublicKey import java.time.Clock import java.util.concurrent.atomic.AtomicInteger @@ -132,6 +137,7 @@ open class Node(configuration: NodeConfiguration, override lateinit var serverThread: AffinityExecutor.ServiceAffinityExecutor private var messageBroker: ArtemisMessagingServer? = null + private var rpcBroker: ArtemisBroker? = null private var shutdownHook: ShutdownHook? = null @@ -139,15 +145,16 @@ open class Node(configuration: NodeConfiguration, // Construct security manager reading users data either from the 'security' config section // if present or from rpcUsers list if the former is missing from config. val securityManagerConfig = configuration.security?.authService ?: - SecurityConfiguration.AuthService.fromUsers(configuration.rpcUsers) + SecurityConfiguration.AuthService.fromUsers(configuration.rpcUsers) securityManager = RPCSecurityManagerImpl(securityManagerConfig) val serverAddress = configuration.messagingServerAddress ?: makeLocalMessageBroker() - val advertisedAddress = info.addresses.single() + val rpcServerAddresses = if (configuration.rpcOptions.standAloneBroker) BrokerAddresses(configuration.rpcOptions.address!!, configuration.rpcOptions.adminAddress) else startLocalRpcBroker() + val advertisedAddress = info.addresses.first() printBasicNodeInfo("Incoming connection address", advertisedAddress.toString()) - rpcMessagingClient = RPCMessagingClient(configuration, serverAddress, networkParameters.maxMessageSize) + rpcMessagingClient = RPCMessagingClient(configuration.rpcOptions.sslConfig, rpcServerAddresses.admin, networkParameters.maxMessageSize) verifierMessagingClient = when (configuration.verifierType) { VerifierType.OutOfProcess -> VerifierMessagingClient(configuration, serverAddress, services.monitoringService.metrics, networkParameters.maxMessageSize) VerifierType.InMemory -> null @@ -166,15 +173,38 @@ open class Node(configuration: NodeConfiguration, networkParameters.maxMessageSize) } + private fun startLocalRpcBroker(): BrokerAddresses { + with(configuration) { + require(rpcOptions.address != null) { "RPC address needs to be specified for local RPC broker." } + val rpcBrokerDirectory: Path = baseDirectory / "brokers" / "rpc" + with(rpcOptions) { + rpcBroker = if (useSsl) { + ArtemisRpcBroker.withSsl(this.address!!, sslConfig, securityManager, certificateChainCheckPolicies, networkParameters.maxMessageSize, exportJMXto.isNotEmpty(), rpcBrokerDirectory) + } else { + ArtemisRpcBroker.withoutSsl(this.address!!, adminAddress!!, sslConfig, securityManager, certificateChainCheckPolicies, networkParameters.maxMessageSize, exportJMXto.isNotEmpty(), rpcBrokerDirectory) + } + } + return rpcBroker!!.addresses + } + } + private fun makeLocalMessageBroker(): NetworkHostAndPort { with(configuration) { - messageBroker = ArtemisMessagingServer(this, p2pAddress.port, rpcAddress?.port, services.networkMapCache, securityManager, networkParameters.maxMessageSize) + messageBroker = ArtemisMessagingServer(this, p2pAddress.port, services.networkMapCache, securityManager, networkParameters.maxMessageSize) return NetworkHostAndPort("localhost", p2pAddress.port) } } override fun myAddresses(): List { - return listOf(configuration.messagingServerAddress ?: getAdvertisedAddress()) + val addresses = mutableListOf() + addresses.add(configuration.messagingServerAddress ?: getAdvertisedAddress()) + rpcBroker?.addresses?.let { + addresses.add(it.primary) + if (it.admin != it.primary) { + addresses.add(it.admin) + } + } + return addresses } private fun getAdvertisedAddress(): NetworkHostAndPort { @@ -220,12 +250,16 @@ open class Node(configuration: NodeConfiguration, override fun startMessagingService(rpcOps: RPCOps) { // Start up the embedded MQ server messageBroker?.apply { - runOnStop += this::stop + runOnStop += this::close + start() + } + rpcBroker?.apply { + runOnStop += this::close start() } // Start up the MQ clients. rpcMessagingClient.run { - runOnStop += this::stop + runOnStop += this::close start(rpcOps, securityManager) } verifierMessagingClient?.run { @@ -331,7 +365,7 @@ open class Node(configuration: NodeConfiguration, private var verifierMessagingClient: VerifierMessagingClient? = null /** Starts a blocking event loop for message dispatch. */ fun run() { - rpcMessagingClient.start2(messageBroker!!.serverControl) + rpcMessagingClient.start2(rpcBroker!!.serverControl) verifierMessagingClient?.start2() (network as P2PMessagingClient).run() } diff --git a/node/src/main/kotlin/net/corda/node/internal/artemis/ArtemisBroker.kt b/node/src/main/kotlin/net/corda/node/internal/artemis/ArtemisBroker.kt new file mode 100644 index 0000000000..2803f52039 --- /dev/null +++ b/node/src/main/kotlin/net/corda/node/internal/artemis/ArtemisBroker.kt @@ -0,0 +1,17 @@ +package net.corda.node.internal.artemis + +import net.corda.core.utilities.NetworkHostAndPort +import net.corda.node.internal.LifecycleSupport +import org.apache.activemq.artemis.api.core.management.ActiveMQServerControl + +interface ArtemisBroker : LifecycleSupport, AutoCloseable { + val addresses: BrokerAddresses + + val serverControl: ActiveMQServerControl + + override fun close() = stop() +} + +data class BrokerAddresses(val primary: NetworkHostAndPort, private val adminArg: NetworkHostAndPort?) { + val admin = adminArg ?: primary +} \ No newline at end of file diff --git a/node/src/main/kotlin/net/corda/node/internal/artemis/CertificateChainCheckPolicy.kt b/node/src/main/kotlin/net/corda/node/internal/artemis/CertificateChainCheckPolicy.kt new file mode 100644 index 0000000000..fa52680cf3 --- /dev/null +++ b/node/src/main/kotlin/net/corda/node/internal/artemis/CertificateChainCheckPolicy.kt @@ -0,0 +1,82 @@ +package net.corda.node.internal.artemis + +import net.corda.core.identity.CordaX500Name +import net.corda.nodeapi.internal.crypto.X509Utilities +import java.security.KeyStore +import javax.security.cert.CertificateException +import javax.security.cert.X509Certificate + +sealed class CertificateChainCheckPolicy { + @FunctionalInterface + interface Check { + fun checkCertificateChain(theirChain: Array) + } + + abstract fun createCheck(keyStore: KeyStore, trustStore: KeyStore): Check + + object Any : CertificateChainCheckPolicy() { + override fun createCheck(keyStore: KeyStore, trustStore: KeyStore): Check { + return object : Check { + override fun checkCertificateChain(theirChain: Array) { + // nothing to do here + } + } + } + } + + object RootMustMatch : CertificateChainCheckPolicy() { + override fun createCheck(keyStore: KeyStore, trustStore: KeyStore): Check { + val rootPublicKey = trustStore.getCertificate(X509Utilities.CORDA_ROOT_CA).publicKey + return object : Check { + override fun checkCertificateChain(theirChain: Array) { + val theirRoot = theirChain.last().publicKey + if (rootPublicKey != theirRoot) { + throw CertificateException("Root certificate mismatch, their root = $theirRoot") + } + } + } + } + } + + object LeafMustMatch : CertificateChainCheckPolicy() { + override fun createCheck(keyStore: KeyStore, trustStore: KeyStore): Check { + val ourPublicKey = keyStore.getCertificate(X509Utilities.CORDA_CLIENT_TLS).publicKey + return object : Check { + override fun checkCertificateChain(theirChain: Array) { + val theirLeaf = theirChain.first().publicKey + if (ourPublicKey != theirLeaf) { + throw CertificateException("Leaf certificate mismatch, their leaf = $theirLeaf") + } + } + } + } + } + + data class MustContainOneOf(private val trustedAliases: Set) : CertificateChainCheckPolicy() { + override fun createCheck(keyStore: KeyStore, trustStore: KeyStore): Check { + val trustedPublicKeys = trustedAliases.map { trustStore.getCertificate(it).publicKey }.toSet() + return object : Check { + override fun checkCertificateChain(theirChain: Array) { + if (!theirChain.any { it.publicKey in trustedPublicKeys }) { + throw CertificateException("Their certificate chain contained none of the trusted ones") + } + } + } + } + } + + object UsernameMustMatchCommonName : CertificateChainCheckPolicy() { + override fun createCheck(keyStore: KeyStore, trustStore: KeyStore): Check { + return UsernameMustMatchCommonNameCheck() + } + } + + class UsernameMustMatchCommonNameCheck : Check { + lateinit var username: String + override fun checkCertificateChain(theirChain: Array) { + if (!theirChain.any { certificate -> CordaX500Name.parse(certificate.subjectDN.name).commonName == username }) { + throw CertificateException("Client certificate does not match login username.") + } + } + } +} \ No newline at end of file diff --git a/node/src/main/kotlin/net/corda/node/internal/artemis/SecureArtemisConfiguration.kt b/node/src/main/kotlin/net/corda/node/internal/artemis/SecureArtemisConfiguration.kt new file mode 100644 index 0000000000..757fde38b7 --- /dev/null +++ b/node/src/main/kotlin/net/corda/node/internal/artemis/SecureArtemisConfiguration.kt @@ -0,0 +1,15 @@ +package net.corda.node.internal.artemis + +import net.corda.core.crypto.newSecureRandom +import org.apache.activemq.artemis.core.config.impl.ConfigurationImpl +import java.math.BigInteger + +internal open class SecureArtemisConfiguration : ConfigurationImpl() { + init { + // Artemis allows multiple servers to be grouped together into a cluster for load balancing purposes. The cluster + // user is used for connecting the nodes together. It has super-user privileges and so it's imperative that its + // password be changed from the default (as warned in the docs). Since we don't need this feature we turn it off + // by having its password be an unknown securely random 128-bit value. + clusterPassword = BigInteger(128, newSecureRandom()).toString(16) + } +} \ No newline at end of file diff --git a/node/src/main/kotlin/net/corda/node/services/config/NodeConfiguration.kt b/node/src/main/kotlin/net/corda/node/services/config/NodeConfiguration.kt index 0868682faa..f8316b466a 100644 --- a/node/src/main/kotlin/net/corda/node/services/config/NodeConfiguration.kt +++ b/node/src/main/kotlin/net/corda/node/services/config/NodeConfiguration.kt @@ -3,10 +3,14 @@ package net.corda.node.services.config import com.typesafe.config.Config import net.corda.core.context.AuthServiceId import net.corda.core.identity.CordaX500Name +import net.corda.core.internal.div import net.corda.core.utilities.NetworkHostAndPort +import net.corda.core.utilities.loggerFor import net.corda.core.utilities.seconds -import net.corda.node.services.messaging.CertificateChainCheckPolicy +import net.corda.node.internal.artemis.CertificateChainCheckPolicy +import net.corda.node.services.config.rpc.NodeRpcOptions import net.corda.nodeapi.internal.config.NodeSSLConfiguration +import net.corda.nodeapi.internal.config.SSLConfiguration import net.corda.nodeapi.internal.config.User import net.corda.nodeapi.internal.config.parseAs import net.corda.nodeapi.internal.persistence.DatabaseConfig @@ -34,7 +38,7 @@ interface NodeConfiguration : NodeSSLConfiguration { val activeMQServer: ActiveMqServerConfiguration val additionalNodeInfoPollingFrequencyMsec: Long val p2pAddress: NetworkHostAndPort - val rpcAddress: NetworkHostAndPort? + val rpcOptions: NodeRpcOptions val messagingServerAddress: NetworkHostAndPort? // TODO Move into DevModeOptions val useTestClock: Boolean get() = false @@ -46,7 +50,6 @@ interface NodeConfiguration : NodeSSLConfiguration { val attachmentContentCacheSizeBytes: Long get() = defaultAttachmentContentCacheSize val attachmentCacheBound: Long get() = defaultAttachmentCacheBound - companion object { // default to at least 8MB and a bit extra for larger heap sizes val defaultTransactionCacheSize: Long = 8.MB + getAdditionalCacheMemory() @@ -100,7 +103,7 @@ data class BridgeConfiguration(val retryIntervalMs: Long, data class ActiveMqServerConfiguration(val bridge: BridgeConfiguration) -fun Config.parseAsNodeConfiguration(): NodeConfiguration = this.parseAs() +fun Config.parseAsNodeConfiguration(): NodeConfiguration = parseAs() data class NodeConfigurationImpl( /** This is not retrieved from the config file but rather from a command line argument. */ @@ -118,7 +121,8 @@ data class NodeConfigurationImpl( // Then rename this to messageRedeliveryDelay and make it of type Duration override val messageRedeliveryDelaySeconds: Int = 30, override val p2pAddress: NetworkHostAndPort, - override val rpcAddress: NetworkHostAndPort?, + private val rpcAddress: NetworkHostAndPort? = null, + private val rpcSettings: NodeRpcSettings, // TODO This field is slightly redundant as p2pAddress is sufficient to hold the address of the node's MQ broker. // Instead this should be a Boolean indicating whether that broker is an internal one started by the node or an external one override val messagingServerAddress: NetworkHostAndPort?, @@ -137,7 +141,23 @@ data class NodeConfigurationImpl( private val transactionCacheSizeMegaBytes: Int? = null, private val attachmentContentCacheSizeMegaBytes: Int? = null, override val attachmentCacheBound: Long = NodeConfiguration.defaultAttachmentCacheBound - ) : NodeConfiguration { + ) : NodeConfiguration { + companion object { + private val logger = loggerFor() + } + + override val rpcOptions: NodeRpcOptions = initialiseRpcOptions(rpcAddress, rpcSettings, SslOptions(baseDirectory / "certificates", keyStorePassword, trustStorePassword)) + + private fun initialiseRpcOptions(explicitAddress: NetworkHostAndPort?, settings: NodeRpcSettings, fallbackSslOptions: SSLConfiguration): NodeRpcOptions { + return when { + explicitAddress != null -> { + require(settings.address == null) { "Can't provide top-level rpcAddress and rpcSettings.address (they control the same property)." } + logger.warn("Top-level declaration of property 'rpcAddress' is deprecated. Please use 'rpcSettings.address' instead.") + settings.copy(address = explicitAddress) + } + else -> settings + }.asOptions(fallbackSslOptions) + } override val exportJMXto: String get() = "http" override val transactionCacheSizeBytes: Long @@ -156,6 +176,28 @@ data class NodeConfigurationImpl( } } +data class NodeRpcSettings( + val address: NetworkHostAndPort?, + val adminAddress: NetworkHostAndPort?, + val standAloneBroker: Boolean = false, + val useSsl: Boolean = false, + val ssl: SslOptions? +) { + fun asOptions(fallbackSslOptions: SSLConfiguration): NodeRpcOptions { + return object : NodeRpcOptions { + override val address = this@NodeRpcSettings.address + override val adminAddress = this@NodeRpcSettings.adminAddress + override val standAloneBroker = this@NodeRpcSettings.standAloneBroker + override val useSsl = this@NodeRpcSettings.useSsl + override val sslConfig = this@NodeRpcSettings.ssl ?: fallbackSslOptions + + override fun toString(): String { + return "address: $address, adminAddress: $adminAddress, standAloneBroker: $standAloneBroker, useSsl: $useSsl, sslConfig: $sslConfig" + } + } + } +} + enum class VerifierType { InMemory, OutOfProcess @@ -165,7 +207,8 @@ enum class CertChainPolicyType { Any, RootMustMatch, LeafMustMatch, - MustContainOneOf + MustContainOneOf, + UsernameMustMatch } data class CertChainPolicyConfig(val role: String, private val policy: CertChainPolicyType, private val trustedAliases: Set) { @@ -176,6 +219,7 @@ data class CertChainPolicyConfig(val role: String, private val policy: CertChain CertChainPolicyType.RootMustMatch -> CertificateChainCheckPolicy.RootMustMatch CertChainPolicyType.LeafMustMatch -> CertificateChainCheckPolicy.LeafMustMatch CertChainPolicyType.MustContainOneOf -> CertificateChainCheckPolicy.MustContainOneOf(trustedAliases) + CertChainPolicyType.UsernameMustMatch -> CertificateChainCheckPolicy.UsernameMustMatchCommonName } } } diff --git a/node/src/main/kotlin/net/corda/node/services/config/SslOptions.kt b/node/src/main/kotlin/net/corda/node/services/config/SslOptions.kt new file mode 100644 index 0000000000..e4b6f7f9f5 --- /dev/null +++ b/node/src/main/kotlin/net/corda/node/services/config/SslOptions.kt @@ -0,0 +1,13 @@ +package net.corda.node.services.config + +import net.corda.nodeapi.internal.config.SSLConfiguration +import java.nio.file.Path +import java.nio.file.Paths + +data class SslOptions(override val certificatesDirectory: Path, override val keyStorePassword: String, override val trustStorePassword: String) : SSLConfiguration { + constructor(certificatesDirectory: String, keyStorePassword: String, trustStorePassword: String) : this(certificatesDirectory.toAbsolutePath(), keyStorePassword, trustStorePassword) + + fun copy(certificatesDirectory: String = this.certificatesDirectory.toString(), keyStorePassword: String = this.keyStorePassword, trustStorePassword: String = this.trustStorePassword): SslOptions = copy(certificatesDirectory = certificatesDirectory.toAbsolutePath(), keyStorePassword = keyStorePassword, trustStorePassword = trustStorePassword) +} + +private fun String.toAbsolutePath() = Paths.get(this).toAbsolutePath() \ No newline at end of file diff --git a/node/src/main/kotlin/net/corda/node/services/config/rpc/NodeRpcOptions.kt b/node/src/main/kotlin/net/corda/node/services/config/rpc/NodeRpcOptions.kt new file mode 100644 index 0000000000..353c90a883 --- /dev/null +++ b/node/src/main/kotlin/net/corda/node/services/config/rpc/NodeRpcOptions.kt @@ -0,0 +1,12 @@ +package net.corda.node.services.config.rpc + +import net.corda.core.utilities.NetworkHostAndPort +import net.corda.nodeapi.internal.config.SSLConfiguration + +interface NodeRpcOptions { + val address: NetworkHostAndPort? + val adminAddress: NetworkHostAndPort? + val standAloneBroker: Boolean + val useSsl: Boolean + val sslConfig: SSLConfiguration +} \ No newline at end of file diff --git a/node/src/main/kotlin/net/corda/node/services/messaging/ArtemisMessagingClient.kt b/node/src/main/kotlin/net/corda/node/services/messaging/ArtemisMessagingClient.kt index 84dd18298d..72d0e1ca07 100644 --- a/node/src/main/kotlin/net/corda/node/services/messaging/ArtemisMessagingClient.kt +++ b/node/src/main/kotlin/net/corda/node/services/messaging/ArtemisMessagingClient.kt @@ -37,7 +37,7 @@ class ArtemisMessagingClient(private val config: SSLConfiguration, private val s isUseGlobalPools = nodeSerializationEnv != null } val sessionFactory = locator.createSessionFactory() - // Login using the node username. The broker will authentiate us as its node (as opposed to another peer) + // Login using the node username. The broker will authenticate us as its node (as opposed to another peer) // using our TLS certificate. // Note that the acknowledgement of messages is not flushed to the Artermis journal until the default buffer // size of 1MB is acknowledged. diff --git a/node/src/main/kotlin/net/corda/node/services/messaging/ArtemisMessagingServer.kt b/node/src/main/kotlin/net/corda/node/services/messaging/ArtemisMessagingServer.kt index 7eacea9520..b568685c14 100644 --- a/node/src/main/kotlin/net/corda/node/services/messaging/ArtemisMessagingServer.kt +++ b/node/src/main/kotlin/net/corda/node/services/messaging/ArtemisMessagingServer.kt @@ -1,7 +1,6 @@ package net.corda.node.services.messaging import net.corda.core.crypto.AddressFormatException -import net.corda.core.crypto.newSecureRandom import net.corda.core.identity.CordaX500Name import net.corda.core.internal.ThreadBox import net.corda.core.internal.div @@ -14,17 +13,18 @@ import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.contextLogger import net.corda.core.utilities.debug import net.corda.node.internal.Node -import net.corda.node.internal.security.Password +import net.corda.node.internal.artemis.ArtemisBroker +import net.corda.node.internal.artemis.BrokerAddresses +import net.corda.node.internal.artemis.CertificateChainCheckPolicy +import net.corda.node.internal.artemis.SecureArtemisConfiguration import net.corda.node.internal.security.RPCSecurityManager import net.corda.node.services.api.NetworkMapCacheInternal import net.corda.node.services.config.NodeConfiguration import net.corda.node.services.messaging.NodeLoginModule.Companion.NODE_ROLE import net.corda.node.services.messaging.NodeLoginModule.Companion.PEER_ROLE -import net.corda.node.services.messaging.NodeLoginModule.Companion.RPC_ROLE import net.corda.node.services.messaging.NodeLoginModule.Companion.VERIFIER_ROLE import net.corda.nodeapi.ArtemisTcpTransport import net.corda.nodeapi.ConnectionDirection -import net.corda.nodeapi.RPCApi import net.corda.nodeapi.VerifierApi import net.corda.nodeapi.internal.ArtemisMessagingComponent.ArtemisPeerAddress import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.INTERNAL_PREFIX @@ -34,32 +34,23 @@ import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.P2P_PREFIX import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.PEERS_PREFIX import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.PEER_USER import net.corda.nodeapi.internal.ArtemisMessagingComponent.NodeAddress -import net.corda.nodeapi.internal.crypto.X509Utilities.CORDA_CLIENT_TLS -import net.corda.nodeapi.internal.crypto.X509Utilities.CORDA_ROOT_CA import net.corda.nodeapi.internal.crypto.loadKeyStore import net.corda.nodeapi.internal.requireOnDefaultFileSystem import org.apache.activemq.artemis.api.core.SimpleString import org.apache.activemq.artemis.api.core.management.ActiveMQServerControl import org.apache.activemq.artemis.core.config.Configuration -import org.apache.activemq.artemis.core.config.CoreQueueConfiguration import org.apache.activemq.artemis.core.config.impl.ConfigurationImpl import org.apache.activemq.artemis.core.config.impl.SecurityConfiguration import org.apache.activemq.artemis.core.remoting.impl.netty.NettyAcceptorFactory import org.apache.activemq.artemis.core.security.Role import org.apache.activemq.artemis.core.server.ActiveMQServer -import org.apache.activemq.artemis.core.server.SecuritySettingPlugin import org.apache.activemq.artemis.core.server.impl.ActiveMQServerImpl -import org.apache.activemq.artemis.core.settings.HierarchicalRepository -import org.apache.activemq.artemis.core.settings.impl.AddressFullMessagePolicy -import org.apache.activemq.artemis.core.settings.impl.AddressSettings import org.apache.activemq.artemis.spi.core.security.ActiveMQJAASSecurityManager import org.apache.activemq.artemis.spi.core.security.jaas.CertificateCallback import org.apache.activemq.artemis.spi.core.security.jaas.RolePrincipal import org.apache.activemq.artemis.spi.core.security.jaas.UserPrincipal import rx.Subscription import java.io.IOException -import java.math.BigInteger -import java.security.KeyStore import java.security.KeyStoreException import java.security.Principal import java.util.* @@ -67,14 +58,12 @@ import javax.annotation.concurrent.ThreadSafe import javax.security.auth.Subject import javax.security.auth.callback.CallbackHandler import javax.security.auth.callback.NameCallback -import javax.security.auth.callback.PasswordCallback import javax.security.auth.callback.UnsupportedCallbackException import javax.security.auth.login.AppConfigurationEntry import javax.security.auth.login.AppConfigurationEntry.LoginModuleControlFlag.REQUIRED import javax.security.auth.login.FailedLoginException import javax.security.auth.login.LoginException import javax.security.auth.spi.LoginModule -import javax.security.cert.CertificateException // TODO: Verify that nobody can connect to us and fiddle with our config over the socket due to the secman. // TODO: Implement a discovery engine that can trigger builds of new connections when another node registers? (later) @@ -92,10 +81,9 @@ import javax.security.cert.CertificateException @ThreadSafe class ArtemisMessagingServer(private val config: NodeConfiguration, private val p2pPort: Int, - val rpcPort: Int?, val networkMapCache: NetworkMapCacheInternal, val securityManager: RPCSecurityManager, - val maxMessageSize: Int) : SingletonSerializeAsToken() { + val maxMessageSize: Int) : ArtemisBroker, SingletonSerializeAsToken() { companion object { private val log = contextLogger() } @@ -105,7 +93,7 @@ class ArtemisMessagingServer(private val config: NodeConfiguration, private val mutex = ThreadBox(InnerState()) private lateinit var activeMQServer: ActiveMQServer - val serverControl: ActiveMQServerControl get() = activeMQServer.activeMQServerControl + override val serverControl: ActiveMQServerControl get() = activeMQServer.activeMQServerControl private var networkChangeHandle: Subscription? = null private lateinit var bridgeManager: BridgeManager @@ -117,8 +105,7 @@ class ArtemisMessagingServer(private val config: NodeConfiguration, * The server will make sure the bridge exists on network map changes, see method [updateBridgesOnNetworkChange] * We assume network map will be updated accordingly when the client node register with the network map. */ - @Throws(IOException::class, KeyStoreException::class) - fun start() = mutex.locked { + override fun start() = mutex.locked { if (!running) { configureAndStartServer() networkChangeHandle = networkMapCache.changed.subscribe { updateBridgesOnNetworkChange(it) } @@ -126,7 +113,7 @@ class ArtemisMessagingServer(private val config: NodeConfiguration, } } - fun stop() = mutex.locked { + override fun stop() = mutex.locked { bridgeManager.close() networkChangeHandle?.unsubscribe() networkChangeHandle = null @@ -134,6 +121,11 @@ class ArtemisMessagingServer(private val config: NodeConfiguration, running = false } + override val addresses = config.p2pAddress.let { BrokerAddresses(it, it) } + + override val started: Boolean + get() = activeMQServer.isStarted + // TODO: Maybe wrap [IOException] on a key store load error so that it's clearly splitting key store loading from // Artemis IO errors @Throws(IOException::class, KeyStoreException::class) @@ -157,12 +149,9 @@ class ArtemisMessagingServer(private val config: NodeConfiguration, activeMQServer.start() bridgeManager.start() Node.printBasicNodeInfo("Listening on port", p2pPort.toString()) - if (rpcPort != null) { - Node.printBasicNodeInfo("RPC service listening on port", rpcPort.toString()) - } } - private fun createArtemisConfig() = ConfigurationImpl().apply { + private fun createArtemisConfig() = SecureArtemisConfiguration().apply { val artemisDir = config.baseDirectory / "artemis" bindingsDirectory = (artemisDir / "bindings").toString() journalDirectory = (artemisDir / "journal").toString() @@ -171,9 +160,6 @@ class ArtemisMessagingServer(private val config: NodeConfiguration, acceptorFactoryClassName = NettyAcceptorFactory::class.java.name ) val acceptors = mutableSetOf(createTcpTransport(connectionDirection, "0.0.0.0", p2pPort)) - if (rpcPort != null) { - acceptors.add(createTcpTransport(connectionDirection, "0.0.0.0", rpcPort, enableSSL = false)) - } acceptorConfigurations = acceptors // Enable built in message deduplication. Note we still have to do our own as the delayed commits // and our own definition of commit mean that the built in deduplication cannot remove all duplicates. @@ -184,35 +170,7 @@ class ArtemisMessagingServer(private val config: NodeConfiguration, journalBufferSize_AIO = maxMessageSize // Required to address IllegalArgumentException (when Artemis uses Linux Async IO): Record is too large to store. journalFileSize = maxMessageSize // The size of each journal file in bytes. Artemis default is 10MiB. managementNotificationAddress = SimpleString(NOTIFICATIONS_ADDRESS) - // Artemis allows multiple servers to be grouped together into a cluster for load balancing purposes. The cluster - // user is used for connecting the nodes together. It has super-user privileges and so it's imperative that its - // password be changed from the default (as warned in the docs). Since we don't need this feature we turn it off - // by having its password be an unknown securely random 128-bit value. - clusterPassword = BigInteger(128, newSecureRandom()).toString(16) - queueConfigurations = listOf( - // Create an RPC queue: this will service locally connected clients only (not via a bridge) and those - // clients must have authenticated. We could use a single consumer for everything and perhaps we should, - // but these queues are not worth persisting. - queueConfig(RPCApi.RPC_SERVER_QUEUE_NAME, durable = false), - queueConfig( - name = RPCApi.RPC_CLIENT_BINDING_REMOVALS, - address = NOTIFICATIONS_ADDRESS, - filter = RPCApi.RPC_CLIENT_BINDING_REMOVAL_FILTER_EXPRESSION, - durable = false - ), - queueConfig( - name = RPCApi.RPC_CLIENT_BINDING_ADDITIONS, - address = NOTIFICATIONS_ADDRESS, - filter = RPCApi.RPC_CLIENT_BINDING_ADDITION_FILTER_EXPRESSION, - durable = false - ) - ) - addressesSettings = mapOf( - "${RPCApi.RPC_CLIENT_QUEUE_NAME_PREFIX}.#" to AddressSettings().apply { - maxSizeBytes = 10L * maxMessageSize - addressFullMessagePolicy = AddressFullMessagePolicy.FAIL - } - ) + // JMX enablement if (config.exportJMXto.isNotEmpty()) { isJMXManagementEnabled = true @@ -221,16 +179,6 @@ class ArtemisMessagingServer(private val config: NodeConfiguration, }.configureAddressSecurity() - - private fun queueConfig(name: String, address: String = name, filter: String? = null, durable: Boolean): CoreQueueConfiguration { - return CoreQueueConfiguration().apply { - this.name = name - this.address = address - filterString = filter - isDurable = durable - } - } - /** * Authenticated clients connecting to us fall in one of the following groups: * 1. The node itself. It is given full access to all valid queues. @@ -242,24 +190,9 @@ class ArtemisMessagingServer(private val config: NodeConfiguration, val nodeInternalRole = Role(NODE_ROLE, true, true, true, true, true, true, true, true) securityRoles["$INTERNAL_PREFIX#"] = setOf(nodeInternalRole) // Do not add any other roles here as it's only for the node securityRoles["$P2P_PREFIX#"] = setOf(nodeInternalRole, restrictedRole(PEER_ROLE, send = true)) - securityRoles[RPCApi.RPC_SERVER_QUEUE_NAME] = setOf(nodeInternalRole, restrictedRole(RPC_ROLE, send = true)) - // Each RPC user must have its own role and its own queue. This prevents users accessing each other's queues - // and stealing RPC responses. - val rolesAdderOnLogin = RolesAdderOnLogin { username -> - Pair( - "${RPCApi.RPC_CLIENT_QUEUE_NAME_PREFIX}.$username.#", - setOf( - nodeInternalRole, - restrictedRole( - "${RPCApi.RPC_CLIENT_QUEUE_NAME_PREFIX}.$username", - consume = true, - createNonDurableQueue = true, - deleteNonDurableQueue = true))) - } - securitySettingPlugins.add(rolesAdderOnLogin) securityRoles[VerifierApi.VERIFICATION_REQUESTS_QUEUE_NAME] = setOf(nodeInternalRole, restrictedRole(VERIFIER_ROLE, consume = true)) securityRoles["${VerifierApi.VERIFICATION_RESPONSES_QUEUE_NAME_PREFIX}.#"] = setOf(nodeInternalRole, restrictedRole(VERIFIER_ROLE, send = true)) - val onLoginListener = { username: String -> rolesAdderOnLogin.onLogin(username) } + val onLoginListener = { _: String -> } return Pair(this, onLoginListener) } @@ -369,66 +302,6 @@ class ArtemisMessagingServer(private val config: NodeConfiguration, private fun getBridgeName(queueName: String, hostAndPort: NetworkHostAndPort): String = "$queueName -> $hostAndPort" } -sealed class CertificateChainCheckPolicy { - - @FunctionalInterface - interface Check { - fun checkCertificateChain(theirChain: Array) - } - - abstract fun createCheck(keyStore: KeyStore, trustStore: KeyStore): Check - - object Any : CertificateChainCheckPolicy() { - override fun createCheck(keyStore: KeyStore, trustStore: KeyStore): Check { - return object : Check { - override fun checkCertificateChain(theirChain: Array) { - } - } - } - } - - object RootMustMatch : CertificateChainCheckPolicy() { - override fun createCheck(keyStore: KeyStore, trustStore: KeyStore): Check { - val rootPublicKey = trustStore.getCertificate(CORDA_ROOT_CA).publicKey - return object : Check { - override fun checkCertificateChain(theirChain: Array) { - val theirRoot = theirChain.last().publicKey - if (rootPublicKey != theirRoot) { - throw CertificateException("Root certificate mismatch, their root = $theirRoot") - } - } - } - } - } - - object LeafMustMatch : CertificateChainCheckPolicy() { - override fun createCheck(keyStore: KeyStore, trustStore: KeyStore): Check { - val ourPublicKey = keyStore.getCertificate(CORDA_CLIENT_TLS).publicKey - return object : Check { - override fun checkCertificateChain(theirChain: Array) { - val theirLeaf = theirChain.first().publicKey - if (ourPublicKey != theirLeaf) { - throw CertificateException("Leaf certificate mismatch, their leaf = $theirLeaf") - } - } - } - } - } - - data class MustContainOneOf(val trustedAliases: Set) : CertificateChainCheckPolicy() { - override fun createCheck(keyStore: KeyStore, trustStore: KeyStore): Check { - val trustedPublicKeys = trustedAliases.map { trustStore.getCertificate(it).publicKey }.toSet() - return object : Check { - override fun checkCertificateChain(theirChain: Array) { - if (!theirChain.any { it.publicKey in trustedPublicKeys }) { - throw CertificateException("Their certificate chain contained none of the trusted ones") - } - } - } - } - } -} - /** * Clients must connect to us with a username and password and must use TLS. If a someone connects with * [ArtemisMessagingComponent.NODE_USER] then we confirm it's just us as the node by checking their TLS certificate @@ -445,7 +318,6 @@ class NodeLoginModule : LoginModule { // Include forbidden username character to prevent name clash with any RPC usernames const val PEER_ROLE = "SystemRoles/Peer" const val NODE_ROLE = "SystemRoles/Node" - const val RPC_ROLE = "SystemRoles/RPC" const val VERIFIER_ROLE = "SystemRoles/Verifier" const val CERT_CHAIN_CHECKS_OPTION_NAME = "CertChainChecks" @@ -475,10 +347,9 @@ class NodeLoginModule : LoginModule { override fun login(): Boolean { val nameCallback = NameCallback("Username: ") - val passwordCallback = PasswordCallback("Password: ", false) val certificateCallback = CertificateCallback() try { - callbackHandler.handle(arrayOf(nameCallback, passwordCallback, certificateCallback)) + callbackHandler.handle(arrayOf(nameCallback, certificateCallback)) } catch (e: IOException) { throw LoginException(e.message) } catch (e: UnsupportedCallbackException) { @@ -486,7 +357,6 @@ class NodeLoginModule : LoginModule { } val username = nameCallback.name ?: throw FailedLoginException("Username not provided") - val password = String(passwordCallback.password ?: throw FailedLoginException("Password not provided")) val certificates = certificateCallback.certificates log.debug { "Processing login for $username" } @@ -496,7 +366,6 @@ class NodeLoginModule : LoginModule { PEER_ROLE -> authenticatePeer(certificates) NODE_ROLE -> authenticateNode(certificates) VERIFIER_ROLE -> authenticateVerifier(certificates) - RPC_ROLE -> authenticateRpcUser(username, Password(password)) else -> throw FailedLoginException("Peer does not belong on our network") } principals += UserPrincipal(validatedUser) @@ -527,14 +396,6 @@ class NodeLoginModule : LoginModule { return certificates.first().subjectDN.name } - private fun authenticateRpcUser(username: String, password: Password): String { - securityManager.authenticate(username, password) - loginListener(username) - principals += RolePrincipal(RPC_ROLE) // This enables the RPC client to send requests - principals += RolePrincipal("${RPCApi.RPC_CLIENT_QUEUE_NAME_PREFIX}.$username") // This enables the RPC client to receive responses - return username - } - private fun determineUserRole(certificates: Array?, username: String): String? { fun requireTls() = require(certificates != null) { "No TLS?" } return when (username) { @@ -550,14 +411,7 @@ class NodeLoginModule : LoginModule { requireTls() VERIFIER_ROLE } - else -> { - // Assume they're an RPC user if its from a non-ssl connection - if (certificates == null) { - RPC_ROLE - } else { - null - } - } + else -> null } } @@ -585,39 +439,4 @@ class NodeLoginModule : LoginModule { } } -typealias LoginListener = (String) -> Unit -typealias RolesRepository = HierarchicalRepository> - -/** - * Helper class to dynamically assign security roles to RPC users - * on their authentication. This object is plugged into the server - * as [SecuritySettingPlugin]. It responds to authentication events - * from [NodeLoginModule] by adding the address -> roles association - * generated by the given [source], unless already done before. - */ -private class RolesAdderOnLogin(val source: (String) -> Pair>) - : SecuritySettingPlugin { - - // Artemis internal container storing roles association - private lateinit var repository: RolesRepository - - fun onLogin(username: String) { - val (address, roles) = source(username) - val entry = repository.getMatch(address) - if (entry == null || entry.isEmpty()) { - repository.addMatch(address, roles.toMutableSet()) - } - } - - // Initializer called by the Artemis framework - override fun setSecurityRepository(repository: RolesRepository) { - this.repository = repository - } - - // Part of SecuritySettingPlugin interface which is no-op in this case - override fun stop() = this - - override fun init(options: MutableMap?) = this - - override fun getSecurityRoles() = null -} +typealias LoginListener = (String) -> Unit \ No newline at end of file diff --git a/node/src/main/kotlin/net/corda/node/services/messaging/RPCMessagingClient.kt b/node/src/main/kotlin/net/corda/node/services/messaging/RPCMessagingClient.kt index e9fe065ac6..3c5f4127a1 100644 --- a/node/src/main/kotlin/net/corda/node/services/messaging/RPCMessagingClient.kt +++ b/node/src/main/kotlin/net/corda/node/services/messaging/RPCMessagingClient.kt @@ -5,14 +5,14 @@ import net.corda.core.messaging.RPCOps import net.corda.core.serialization.SingletonSerializeAsToken import net.corda.core.utilities.NetworkHostAndPort import net.corda.node.internal.security.RPCSecurityManager -import net.corda.nodeapi.internal.config.SSLConfiguration import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.NODE_USER +import net.corda.nodeapi.internal.config.SSLConfiguration import net.corda.nodeapi.internal.crypto.X509Utilities import net.corda.nodeapi.internal.crypto.getX509Certificate import net.corda.nodeapi.internal.crypto.loadKeyStore import org.apache.activemq.artemis.api.core.management.ActiveMQServerControl -class RPCMessagingClient(private val config: SSLConfiguration, serverAddress: NetworkHostAndPort, private val maxMessageSize: Int) : SingletonSerializeAsToken() { +class RPCMessagingClient(private val config: SSLConfiguration, serverAddress: NetworkHostAndPort, maxMessageSize: Int) : SingletonSerializeAsToken(), AutoCloseable { private val artemis = ArtemisMessagingClient(config, serverAddress, maxMessageSize) private var rpcServer: RPCServer? = null @@ -30,4 +30,6 @@ class RPCMessagingClient(private val config: SSLConfiguration, serverAddress: Ne rpcServer?.close() artemis.stop() } + + override fun close() = stop() } diff --git a/node/src/main/kotlin/net/corda/node/services/rpc/ArtemisRpcBroker.kt b/node/src/main/kotlin/net/corda/node/services/rpc/ArtemisRpcBroker.kt new file mode 100644 index 0000000000..c2f23c89f1 --- /dev/null +++ b/node/src/main/kotlin/net/corda/node/services/rpc/ArtemisRpcBroker.kt @@ -0,0 +1,108 @@ +package net.corda.node.services.rpc + +import net.corda.core.internal.noneOrSingle +import net.corda.core.utilities.NetworkHostAndPort +import net.corda.core.utilities.loggerFor +import net.corda.node.internal.artemis.ArtemisBroker +import net.corda.node.internal.artemis.BrokerAddresses +import net.corda.node.internal.security.RPCSecurityManager +import net.corda.node.services.config.CertChainPolicyConfig +import net.corda.node.internal.artemis.CertificateChainCheckPolicy +import net.corda.nodeapi.internal.config.SSLConfiguration +import net.corda.nodeapi.internal.crypto.loadKeyStore +import org.apache.activemq.artemis.api.core.management.ActiveMQServerControl +import org.apache.activemq.artemis.core.config.impl.SecurityConfiguration +import org.apache.activemq.artemis.core.server.ActiveMQServer +import org.apache.activemq.artemis.core.server.impl.ActiveMQServerImpl +import org.apache.activemq.artemis.spi.core.security.ActiveMQJAASSecurityManager +import rx.Observable +import java.io.IOException +import java.nio.file.Path +import java.security.KeyStoreException +import java.util.concurrent.CompletableFuture +import javax.security.auth.login.AppConfigurationEntry + +internal class ArtemisRpcBroker internal constructor( + address: NetworkHostAndPort, + private val adminAddressOptional: NetworkHostAndPort?, + private val sslOptions: SSLConfiguration, + private val useSsl: Boolean, + private val securityManager: RPCSecurityManager, + private val certificateChainCheckPolicies: List, + private val maxMessageSize: Int, + private val jmxEnabled: Boolean = false, + private val baseDirectory: Path) : ArtemisBroker { + + companion object { + private val logger = loggerFor() + + fun withSsl(address: NetworkHostAndPort, sslOptions: SSLConfiguration, securityManager: RPCSecurityManager, certificateChainCheckPolicies: List, maxMessageSize: Int, jmxEnabled: Boolean, baseDirectory: Path): ArtemisBroker { + return ArtemisRpcBroker(address, null, sslOptions, true, securityManager, certificateChainCheckPolicies, maxMessageSize, jmxEnabled, baseDirectory) + } + + fun withoutSsl(address: NetworkHostAndPort, adminAddress: NetworkHostAndPort, sslOptions: SSLConfiguration, securityManager: RPCSecurityManager, certificateChainCheckPolicies: List, maxMessageSize: Int, jmxEnabled: Boolean, baseDirectory: Path): ArtemisBroker { + return ArtemisRpcBroker(address, adminAddress, sslOptions, false, securityManager, certificateChainCheckPolicies, maxMessageSize, jmxEnabled, baseDirectory) + } + } + + override fun start() { + logger.debug("Artemis RPC broker is starting.") + server.start() + logger.debug("Artemis RPC broker is started.") + } + + override fun stop() { + logger.debug("Artemis RPC broker is stopping.") + server.stop(true) + logger.debug("Artemis RPC broker is stopped.") + } + + override val started get() = server.isStarted + + override val serverControl: ActiveMQServerControl get() = server.activeMQServerControl + + override val addresses = BrokerAddresses(address, adminAddressOptional ?: address) + + private val server = initialiseServer() + + private fun initialiseServer(): ActiveMQServer { + val serverConfiguration = RpcBrokerConfiguration(baseDirectory, maxMessageSize, jmxEnabled, addresses.primary, adminAddressOptional, sslOptions, useSsl) + val serverSecurityManager = createArtemisSecurityManager(serverConfiguration.loginListener, sslOptions) + + return ActiveMQServerImpl(serverConfiguration, serverSecurityManager).apply { + registerActivationFailureListener { exception -> throw exception } + registerPostQueueDeletionCallback { address, qName -> logger.debug("Queue deleted: $qName for $address") } + } + } + + @Throws(IOException::class, KeyStoreException::class) + private fun createArtemisSecurityManager(loginListener: LoginListener, sslOptions: SSLConfiguration): ActiveMQJAASSecurityManager { + val keyStore = loadKeyStore(sslOptions.sslKeystore, sslOptions.keyStorePassword) + val trustStore = loadKeyStore(sslOptions.trustStoreFile, sslOptions.trustStorePassword) + + val defaultCertPolicies = mapOf( + NodeLoginModule.NODE_ROLE to CertificateChainCheckPolicy.LeafMustMatch, + NodeLoginModule.RPC_ROLE to CertificateChainCheckPolicy.Any + ) + val certChecks = defaultCertPolicies.mapValues { (role, defaultPolicy) -> + val policy = certificateChainCheckPolicies.noneOrSingle { it.role == role }?.certificateChainCheckPolicy ?: defaultPolicy + policy.createCheck(keyStore, trustStore) + } + + val securityConfig = object : SecurityConfiguration() { + override fun getAppConfigurationEntry(name: String): Array { + val options = mapOf( + NodeLoginModule.LOGIN_LISTENER_ARG to loginListener, + NodeLoginModule.SECURITY_MANAGER_ARG to securityManager, + NodeLoginModule.USE_SSL_ARG to useSsl, + NodeLoginModule.CERT_CHAIN_CHECKS_ARG to certChecks) + return arrayOf(AppConfigurationEntry(name, AppConfigurationEntry.LoginModuleControlFlag.REQUIRED, options)) + } + } + return ActiveMQJAASSecurityManager(NodeLoginModule::class.java.name, securityConfig) + } +} + +typealias LoginListener = (String) -> Unit + +private fun CompletableFuture.toObservable() = Observable.from(this) \ No newline at end of file diff --git a/node/src/main/kotlin/net/corda/node/services/rpc/NodeLoginModule.kt b/node/src/main/kotlin/net/corda/node/services/rpc/NodeLoginModule.kt new file mode 100644 index 0000000000..eaf87efd24 --- /dev/null +++ b/node/src/main/kotlin/net/corda/node/services/rpc/NodeLoginModule.kt @@ -0,0 +1,169 @@ +package net.corda.node.services.rpc + +import net.corda.core.internal.uncheckedCast +import net.corda.core.utilities.loggerFor +import net.corda.node.internal.security.Password +import net.corda.node.internal.security.RPCSecurityManager +import net.corda.node.internal.artemis.CertificateChainCheckPolicy +import net.corda.nodeapi.RPCApi +import net.corda.nodeapi.internal.ArtemisMessagingComponent +import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.NODE_USER +import org.apache.activemq.artemis.spi.core.security.jaas.CertificateCallback +import org.apache.activemq.artemis.spi.core.security.jaas.RolePrincipal +import org.apache.activemq.artemis.spi.core.security.jaas.UserPrincipal +import java.io.IOException +import java.security.Principal +import java.util.* +import javax.security.auth.Subject +import javax.security.auth.callback.CallbackHandler +import javax.security.auth.callback.NameCallback +import javax.security.auth.callback.PasswordCallback +import javax.security.auth.callback.UnsupportedCallbackException +import javax.security.auth.login.FailedLoginException +import javax.security.auth.login.LoginException +import javax.security.auth.spi.LoginModule +import javax.security.cert.X509Certificate + +/** + * Clients must connect to us with a username and password and must use TLS. If a someone connects with + * [ArtemisMessagingComponent.NODE_USER] then we confirm it's just us as the node by checking their TLS certificate + * is the same as our one in our key store. Then they're given full access to all valid queues. If they connect with + * [ArtemisMessagingComponent.PEER_USER] then we confirm they belong on our P2P network by checking their root CA is + * the same as our root CA. If that's the case the only access they're given is the ablility send to our P2P address. + * In both cases the messages these authenticated nodes send to us are tagged with their subject DN and we assume + * the CN within that is their legal name. + * Otherwise if the username is neither of the above we assume it's an RPC user and authenticate against our list of + * valid RPC users. RPC clients are given permission to perform RPC and nothing else. + */ +internal class NodeLoginModule : LoginModule { + companion object { + internal const val NODE_ROLE = "SystemRoles/Node" + internal const val RPC_ROLE = "SystemRoles/RPC" + + internal const val CERT_CHAIN_CHECKS_ARG = "CertChainChecks" + internal const val USE_SSL_ARG = "useSsl" + internal val SECURITY_MANAGER_ARG = "RpcSecurityManager" + internal val LOGIN_LISTENER_ARG = "LoginListener" + private val log = loggerFor() + } + + private var loginSucceeded: Boolean = false + private lateinit var subject: Subject + private lateinit var callbackHandler: CallbackHandler + private lateinit var securityManager: RPCSecurityManager + private lateinit var loginListener: LoginListener + private var useSsl: Boolean? = null + private lateinit var nodeCertCheck: CertificateChainCheckPolicy.Check + private lateinit var rpcCertCheck: CertificateChainCheckPolicy.Check + private val principals = ArrayList() + + override fun initialize(subject: Subject, callbackHandler: CallbackHandler, sharedState: Map, options: Map) { + this.subject = subject + this.callbackHandler = callbackHandler + securityManager = uncheckedCast(options[SECURITY_MANAGER_ARG]) + loginListener = uncheckedCast(options[LOGIN_LISTENER_ARG]) + useSsl = options[USE_SSL_ARG] as Boolean + val certChainChecks: Map = uncheckedCast(options[CERT_CHAIN_CHECKS_ARG]) + nodeCertCheck = certChainChecks[NODE_ROLE]!! + rpcCertCheck = certChainChecks[RPC_ROLE]!! + } + + override fun login(): Boolean { + val nameCallback = NameCallback("Username: ") + val passwordCallback = PasswordCallback("Password: ", false) + val certificateCallback = CertificateCallback() + try { + callbackHandler.handle(arrayOf(nameCallback, passwordCallback, certificateCallback)) + } catch (e: IOException) { + throw LoginException(e.message) + } catch (e: UnsupportedCallbackException) { + throw LoginException("${e.message} not available to obtain information from user") + } + + val username = nameCallback.name ?: throw FailedLoginException("Username not provided") + val password = String(passwordCallback.password ?: throw FailedLoginException("Password not provided")) + val certificates = certificateCallback.certificates ?: emptyArray() + + if (rpcCertCheck is CertificateChainCheckPolicy.UsernameMustMatchCommonNameCheck) { + (rpcCertCheck as CertificateChainCheckPolicy.UsernameMustMatchCommonNameCheck).username = username + } + + log.debug("Logging user in") + + try { + val role = determineUserRole(certificates, username, useSsl!!) + val validatedUser = when (role) { + NodeLoginModule.NODE_ROLE -> { + authenticateNode(certificates) + NODE_USER + } + RPC_ROLE -> { + authenticateRpcUser(username, Password(password), certificates, useSsl!!) + username + } + else -> throw FailedLoginException("Peer does not belong on our network") + } + principals += UserPrincipal(validatedUser) + + loginSucceeded = true + return loginSucceeded + } catch (exception: FailedLoginException) { + log.warn("$exception") + throw exception + } + } + + private fun authenticateNode(certificates: Array) { + nodeCertCheck.checkCertificateChain(certificates) + principals += RolePrincipal(NodeLoginModule.NODE_ROLE) + } + + private fun authenticateRpcUser(username: String, password: Password, certificates: Array, useSsl: Boolean) { + if (useSsl) { + rpcCertCheck.checkCertificateChain(certificates) + // no point in matching username with CN because companies wouldn't want to provide a certificate for each user + } + securityManager.authenticate(username, password) + loginListener(username) + principals += RolePrincipal(RPC_ROLE) // This enables the RPC client to send requests + principals += RolePrincipal("${RPCApi.RPC_CLIENT_QUEUE_NAME_PREFIX}.$username") // This enables the RPC client to receive responses + } + + private fun determineUserRole(certificates: Array, username: String, useSsl: Boolean): String? { + return when (username) { + ArtemisMessagingComponent.NODE_USER -> { + require(certificates.isNotEmpty()) { "No TLS?" } + NodeLoginModule.NODE_ROLE + } + else -> { + if (useSsl) { + require(certificates.isNotEmpty()) { "No TLS?" } + } + return RPC_ROLE + } + } + } + + override fun commit(): Boolean { + val result = loginSucceeded + if (result) { + subject.principals.addAll(principals) + } + clear() + return result + } + + override fun abort(): Boolean { + clear() + return true + } + + override fun logout(): Boolean { + subject.principals.removeAll(principals) + return true + } + + private fun clear() { + loginSucceeded = false + } +} \ No newline at end of file diff --git a/node/src/main/kotlin/net/corda/node/services/rpc/RolesAdderOnLogin.kt b/node/src/main/kotlin/net/corda/node/services/rpc/RolesAdderOnLogin.kt new file mode 100644 index 0000000000..78a1e9d268 --- /dev/null +++ b/node/src/main/kotlin/net/corda/node/services/rpc/RolesAdderOnLogin.kt @@ -0,0 +1,36 @@ +package net.corda.node.services.rpc + +import org.apache.activemq.artemis.core.security.Role +import org.apache.activemq.artemis.core.server.SecuritySettingPlugin +import org.apache.activemq.artemis.core.settings.HierarchicalRepository + +/** + * Helper class to dynamically assign security roles to RPC users + * on their authentication. This object is plugged into the server + * as [SecuritySettingPlugin]. It responds to authentication events + * from [NodeLoginModule] by adding the address -> roles association + * generated by the given [source], unless already done before. + */ +internal class RolesAdderOnLogin(val source: (String) -> Pair>) : SecuritySettingPlugin { + private lateinit var repository: RolesRepository + + fun onLogin(username: String) { + val (address, roles) = source(username) + val entry = repository.getMatch(address) + if (entry == null || entry.isEmpty()) { + repository.addMatch(address, roles.toMutableSet()) + } + } + + override fun setSecurityRepository(repository: RolesRepository) { + this.repository = repository + } + + override fun stop() = this + + override fun init(options: MutableMap?) = this + + override fun getSecurityRoles() = null +} + +typealias RolesRepository = HierarchicalRepository> \ No newline at end of file diff --git a/node/src/main/kotlin/net/corda/node/services/rpc/RpcBrokerConfiguration.kt b/node/src/main/kotlin/net/corda/node/services/rpc/RpcBrokerConfiguration.kt new file mode 100644 index 0000000000..e8d819df0a --- /dev/null +++ b/node/src/main/kotlin/net/corda/node/services/rpc/RpcBrokerConfiguration.kt @@ -0,0 +1,131 @@ +package net.corda.node.services.rpc + +import net.corda.core.internal.div +import net.corda.core.utilities.NetworkHostAndPort +import net.corda.node.internal.artemis.SecureArtemisConfiguration +import net.corda.nodeapi.ArtemisTcpTransport.Companion.tcpTransport +import net.corda.nodeapi.ConnectionDirection +import net.corda.nodeapi.RPCApi +import net.corda.nodeapi.internal.ArtemisMessagingComponent +import net.corda.nodeapi.internal.config.SSLConfiguration +import org.apache.activemq.artemis.api.core.SimpleString +import org.apache.activemq.artemis.api.core.TransportConfiguration +import org.apache.activemq.artemis.core.config.CoreQueueConfiguration +import org.apache.activemq.artemis.core.remoting.impl.netty.NettyAcceptorFactory +import org.apache.activemq.artemis.core.security.Role +import org.apache.activemq.artemis.core.settings.impl.AddressFullMessagePolicy +import org.apache.activemq.artemis.core.settings.impl.AddressSettings +import java.nio.file.Path + +internal class RpcBrokerConfiguration(baseDirectory: Path, maxMessageSize: Int, jmxEnabled: Boolean, address: NetworkHostAndPort, adminAddress: NetworkHostAndPort?, sslOptions: SSLConfiguration?, useSsl: Boolean) : SecureArtemisConfiguration() { + val loginListener: (String) -> Unit + + init { + setDirectories(baseDirectory) + + val acceptorConfigurationsSet = mutableSetOf(acceptorConfiguration(address, useSsl, sslOptions)) + adminAddress?.let { + acceptorConfigurationsSet += acceptorConfiguration(adminAddress, true, sslOptions) + } + acceptorConfigurations = acceptorConfigurationsSet + + queueConfigurations = queueConfigurations() + + managementNotificationAddress = SimpleString(ArtemisMessagingComponent.NOTIFICATIONS_ADDRESS) + addressesSettings = mapOf( + "${RPCApi.RPC_CLIENT_QUEUE_NAME_PREFIX}.#" to AddressSettings().apply { + maxSizeBytes = 10L * maxMessageSize + addressFullMessagePolicy = AddressFullMessagePolicy.FAIL + } + ) + + initialiseSettings(maxMessageSize) + + val nodeInternalRole = Role(NodeLoginModule.NODE_ROLE, true, true, true, true, true, true, true, true) + + val rolesAdderOnLogin = RolesAdderOnLogin { username -> + "${RPCApi.RPC_CLIENT_QUEUE_NAME_PREFIX}.$username.#" to setOf(nodeInternalRole, restrictedRole( + "${RPCApi.RPC_CLIENT_QUEUE_NAME_PREFIX}.$username", + consume = true, + createNonDurableQueue = true, + deleteNonDurableQueue = true) + ) + } + + configureAddressSecurity(nodeInternalRole, rolesAdderOnLogin) + + if (jmxEnabled) { + enableJmx() + } + + loginListener = { username: String -> rolesAdderOnLogin.onLogin(username) } + } + + private fun configureAddressSecurity(nodeInternalRole: Role, rolesAdderOnLogin: RolesAdderOnLogin) { + securityRoles["${ArtemisMessagingComponent.INTERNAL_PREFIX}#"] = setOf(nodeInternalRole) + securityRoles[RPCApi.RPC_SERVER_QUEUE_NAME] = setOf(nodeInternalRole, restrictedRole(NodeLoginModule.RPC_ROLE, send = true)) + securitySettingPlugins.add(rolesAdderOnLogin) + } + + private fun enableJmx() { + isJMXManagementEnabled = true + isJMXUseBrokerName = true + } + + private fun initialiseSettings(maxMessageSize: Int) { + // Enable built in message deduplication. Note we still have to do our own as the delayed commits + // and our own definition of commit mean that the built in deduplication cannot remove all duplicates. + idCacheSize = 2000 // Artemis Default duplicate cache size i.e. a guess + isPersistIDCache = true + isPopulateValidatedUser = true + journalBufferSize_NIO = maxMessageSize // Artemis default is 490KiB - required to address IllegalArgumentException (when Artemis uses Java NIO): Record is too large to store. + journalBufferSize_AIO = maxMessageSize // Required to address IllegalArgumentException (when Artemis uses Linux Async IO): Record is too large to store. + journalFileSize = maxMessageSize // The size of each journal file in bytes. Artemis default is 10MiB. + } + + private fun queueConfigurations(): List { + return listOf( + queueConfiguration(RPCApi.RPC_SERVER_QUEUE_NAME, durable = false), + queueConfiguration( + name = RPCApi.RPC_CLIENT_BINDING_REMOVALS, + address = ArtemisMessagingComponent.NOTIFICATIONS_ADDRESS, + filter = RPCApi.RPC_CLIENT_BINDING_REMOVAL_FILTER_EXPRESSION, + durable = false + ), + queueConfiguration( + name = RPCApi.RPC_CLIENT_BINDING_ADDITIONS, + address = ArtemisMessagingComponent.NOTIFICATIONS_ADDRESS, + filter = RPCApi.RPC_CLIENT_BINDING_ADDITION_FILTER_EXPRESSION, + durable = false + ) + ) + } + + private fun setDirectories(baseDirectory: Path) { + bindingsDirectory = (baseDirectory / "bindings").toString() + journalDirectory = (baseDirectory / "journal").toString() + largeMessagesDirectory = (baseDirectory / "large-messages").toString() + } + + private fun queueConfiguration(name: String, address: String = name, filter: String? = null, durable: Boolean): CoreQueueConfiguration { + val configuration = CoreQueueConfiguration() + + configuration.name = name + configuration.address = address + configuration.filterString = filter + configuration.isDurable = durable + + return configuration + } + + + private fun acceptorConfiguration(address: NetworkHostAndPort, enableSsl: Boolean, sslOptions: SSLConfiguration?): TransportConfiguration { + return tcpTransport(ConnectionDirection.Inbound(NettyAcceptorFactory::class.java.name), address, sslOptions, enableSsl) + } + + private fun restrictedRole(name: String, send: Boolean = false, consume: Boolean = false, createDurableQueue: Boolean = false, + deleteDurableQueue: Boolean = false, createNonDurableQueue: Boolean = false, + deleteNonDurableQueue: Boolean = false, manage: Boolean = false, browse: Boolean = false): Role { + return Role(name, send, consume, createDurableQueue, deleteDurableQueue, createNonDurableQueue, deleteNonDurableQueue, manage, browse) + } +} \ No newline at end of file diff --git a/node/src/main/resources/reference.conf b/node/src/main/resources/reference.conf index d73f0b8ae8..bc2e599de7 100644 --- a/node/src/main/resources/reference.conf +++ b/node/src/main/resources/reference.conf @@ -24,4 +24,8 @@ activeMQServer = { maxRetryIntervalMin = 3 } } -useAMQPBridges = true \ No newline at end of file +useAMQPBridges = true +rpcSettings = { + useSsl = false + standAloneBroker = false +} \ No newline at end of file diff --git a/node/src/test/kotlin/net/corda/node/services/config/NodeConfigurationImplTest.kt b/node/src/test/kotlin/net/corda/node/services/config/NodeConfigurationImplTest.kt index 73067bc769..ab9958f73c 100644 --- a/node/src/test/kotlin/net/corda/node/services/config/NodeConfigurationImplTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/config/NodeConfigurationImplTest.kt @@ -1,11 +1,13 @@ package net.corda.node.services.config +import net.corda.core.internal.div import net.corda.core.utilities.NetworkHostAndPort import net.corda.testing.core.ALICE_NAME import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties import org.assertj.core.api.Assertions.assertThatThrownBy import org.junit.Test import java.nio.file.Paths +import java.util.* import kotlin.test.assertFalse import kotlin.test.assertTrue @@ -27,24 +29,42 @@ class NodeConfigurationImplTest { assertFalse { configDebugOptions(true, DevModeOptions(true)).shouldCheckCheckpoints() } } - private fun configDebugOptions(devMode: Boolean, devModeOptions: DevModeOptions?) : NodeConfiguration { + private fun configDebugOptions(devMode: Boolean, devModeOptions: DevModeOptions?): NodeConfiguration { return testConfiguration.copy(devMode = devMode, devModeOptions = devModeOptions) } - private val testConfiguration = NodeConfigurationImpl( - baseDirectory = Paths.get("."), - myLegalName = ALICE_NAME, - emailAddress = "", - keyStorePassword = "cordacadevpass", - trustStorePassword = "trustpass", - dataSourceProperties = makeTestDataSourceProperties(ALICE_NAME.organisation), - rpcUsers = emptyList(), - verifierType = VerifierType.InMemory, - p2pAddress = NetworkHostAndPort("localhost", 0), - rpcAddress = NetworkHostAndPort("localhost", 1), - messagingServerAddress = null, - notary = null, - certificateChainCheckPolicies = emptyList(), - devMode = true, - activeMQServer = ActiveMqServerConfiguration(BridgeConfiguration(0, 0, 0.0))) + private fun testConfiguration(dataSourceProperties: Properties): NodeConfigurationImpl { + return testConfiguration.copy(dataSourceProperties = dataSourceProperties) + } + + private val testConfiguration = testNodeConfiguration() + + private fun testNodeConfiguration(): NodeConfigurationImpl { + val baseDirectory = Paths.get(".") + val keyStorePassword = "cordacadevpass" + val trustStorePassword = "trustpass" + val rpcSettings = NodeRpcSettings( + address = NetworkHostAndPort("localhost", 1), + adminAddress = NetworkHostAndPort("localhost", 2), + standAloneBroker = false, + useSsl = false, + ssl = SslOptions(baseDirectory / "certificates", keyStorePassword, trustStorePassword)) + return NodeConfigurationImpl( + baseDirectory = baseDirectory, + myLegalName = ALICE_NAME, + emailAddress = "", + keyStorePassword = keyStorePassword, + trustStorePassword = trustStorePassword, + dataSourceProperties = makeTestDataSourceProperties(ALICE_NAME.organisation), + rpcUsers = emptyList(), + verifierType = VerifierType.InMemory, + p2pAddress = NetworkHostAndPort("localhost", 0), + messagingServerAddress = null, + notary = null, + certificateChainCheckPolicies = emptyList(), + devMode = true, + activeMQServer = ActiveMqServerConfiguration(BridgeConfiguration(0, 0, 0.0)), + rpcSettings = rpcSettings + ) + } } diff --git a/node/src/test/kotlin/net/corda/node/services/messaging/ArtemisMessagingTests.kt b/node/src/test/kotlin/net/corda/node/services/messaging/ArtemisMessagingTest.kt similarity index 95% rename from node/src/test/kotlin/net/corda/node/services/messaging/ArtemisMessagingTests.kt rename to node/src/test/kotlin/net/corda/node/services/messaging/ArtemisMessagingTest.kt index 67ac413240..8ab4fb33b6 100644 --- a/node/src/test/kotlin/net/corda/node/services/messaging/ArtemisMessagingTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/messaging/ArtemisMessagingTest.kt @@ -37,7 +37,7 @@ import kotlin.concurrent.thread import kotlin.test.assertEquals import kotlin.test.assertNull -class ArtemisMessagingTests { +class ArtemisMessagingTest { companion object { const val TOPIC = "platform.self" } @@ -51,7 +51,6 @@ class ArtemisMessagingTests { val temporaryFolder = TemporaryFolder() private val serverPort = freePort() - private val rpcPort = freePort() private val identity = generateKeyPair() private lateinit var config: NodeConfiguration @@ -71,6 +70,7 @@ class ArtemisMessagingTests { doReturn(ALICE_NAME).whenever(it).myLegalName doReturn("trustpass").whenever(it).trustStorePassword doReturn("cordacadevpass").whenever(it).keyStorePassword + doReturn(NetworkHostAndPort("0.0.0.0", serverPort)).whenever(it).p2pAddress doReturn("").whenever(it).exportJMXto doReturn(emptyList()).whenever(it).certificateChainCheckPolicies doReturn(5).whenever(it).messageRedeliveryDelaySeconds @@ -183,8 +183,8 @@ class ArtemisMessagingTests { } } - private fun createMessagingServer(local: Int = serverPort, rpc: Int = rpcPort, maxMessageSize: Int = MAX_MESSAGE_SIZE): ArtemisMessagingServer { - return ArtemisMessagingServer(config, local, rpc, networkMapCache, securityManager, maxMessageSize).apply { + private fun createMessagingServer(local: Int = serverPort, maxMessageSize: Int = MAX_MESSAGE_SIZE): ArtemisMessagingServer { + return ArtemisMessagingServer(config, local, networkMapCache, securityManager, maxMessageSize).apply { config.configureWithDevSSLCertificate() messagingServer = this } diff --git a/node/src/test/kotlin/net/corda/node/services/rpc/ArtemisRpcTests.kt b/node/src/test/kotlin/net/corda/node/services/rpc/ArtemisRpcTests.kt new file mode 100644 index 0000000000..1898743c8b --- /dev/null +++ b/node/src/test/kotlin/net/corda/node/services/rpc/ArtemisRpcTests.kt @@ -0,0 +1,215 @@ +package net.corda.node.services.rpc + +import net.corda.client.rpc.internal.RPCClient +import net.corda.core.context.AuthServiceId +import net.corda.core.identity.CordaX500Name +import net.corda.core.messaging.RPCOps +import net.corda.core.utilities.NetworkHostAndPort +import net.corda.node.internal.artemis.ArtemisBroker +import net.corda.node.internal.security.RPCSecurityManager +import net.corda.node.internal.security.RPCSecurityManagerImpl +import net.corda.node.services.Permissions.Companion.all +import net.corda.node.services.config.CertChainPolicyConfig +import net.corda.node.services.messaging.RPCMessagingClient +import net.corda.node.testsupport.withCertificates +import net.corda.node.testsupport.withKeyStores +import net.corda.nodeapi.ArtemisTcpTransport.Companion.tcpTransport +import net.corda.nodeapi.ConnectionDirection +import net.corda.nodeapi.internal.config.SSLConfiguration +import net.corda.nodeapi.internal.config.User +import net.corda.testing.core.SerializationEnvironmentRule +import net.corda.testing.driver.PortAllocation +import org.apache.activemq.artemis.api.core.ActiveMQConnectionTimedOutException +import org.apache.activemq.artemis.api.core.ActiveMQNotConnectedException +import org.apache.activemq.artemis.api.core.management.ActiveMQServerControl +import org.assertj.core.api.Assertions.assertThat +import org.assertj.core.api.Assertions.assertThatThrownBy +import org.junit.Rule +import org.junit.Test +import java.nio.file.Files +import java.nio.file.Path +import kotlin.reflect.KClass + +class ArtemisRpcTests { + private val ports: PortAllocation = PortAllocation.RandomFree + + private val user = User("mark", "dadada", setOf(all())) + private val users = listOf(user) + private val securityManager = RPCSecurityManagerImpl.fromUserList(AuthServiceId("test"), users) + + @Rule + @JvmField + val testSerialization = SerializationEnvironmentRule(true) + + @Test + fun rpc_with_ssl_enabled() { + withCertificates { server, client, createSelfSigned, createSignedBy -> + val rootCertificate = createSelfSigned(CordaX500Name("SystemUsers/Node", "IT", "R3 London", "London", "London", "GB")) + val markCertificate = createSignedBy(CordaX500Name("mark", "IT", "R3 London", "London", "London", "GB"), rootCertificate) + + // truststore needs to contain root CA for how the driver works... + server.keyStore["cordaclienttls"] = rootCertificate + server.trustStore["cordaclienttls"] = rootCertificate + server.trustStore["mark"] = markCertificate + + client.keyStore["mark"] = markCertificate + client.trustStore["cordaclienttls"] = rootCertificate + + withKeyStores(server, client) { brokerSslOptions, clientSslOptions -> + testSslCommunication(brokerSslOptions, true, clientSslOptions) + } + } + } + + @Test + fun rpc_with_ssl_disabled() { + withCertificates { server, client, createSelfSigned, _ -> + val rootCertificate = createSelfSigned(CordaX500Name("SystemUsers/Node", "IT", "R3 London", "London", "London", "GB")) + + // truststore needs to contain root CA for how the driver works... + server.keyStore["cordaclienttls"] = rootCertificate + server.trustStore["cordaclienttls"] = rootCertificate + + withKeyStores(server, client) { brokerSslOptions, _ -> + // here server is told not to use SSL, and client sslOptions are null (as in, do not use SSL) + testSslCommunication(brokerSslOptions, false, null) + } + } + } + + @Test + fun rpc_with_server_certificate_untrusted_to_client() { + withCertificates { server, client, createSelfSigned, createSignedBy -> + val rootCertificate = createSelfSigned(CordaX500Name("SystemUsers/Node", "IT", "R3 London", "London", "London", "GB")) + val markCertificate = createSignedBy(CordaX500Name("mark", "IT", "R3 London", "London", "London", "GB"), rootCertificate) + + // truststore needs to contain root CA for how the driver works... + server.keyStore["cordaclienttls"] = rootCertificate + server.trustStore["cordaclienttls"] = rootCertificate + server.trustStore["mark"] = markCertificate + + client.keyStore["mark"] = markCertificate + // here the server certificate is not trusted by the client +// client.trustStore["cordaclienttls"] = rootCertificate + + withKeyStores(server, client) { brokerSslOptions, clientSslOptions -> + testSslCommunication(brokerSslOptions, true, clientSslOptions, clientConnectionSpy = expectExceptionOfType(ActiveMQNotConnectedException::class)) + } + } + } + + @Test + fun rpc_with_no_client_certificate() { + withCertificates { server, client, createSelfSigned, createSignedBy -> + val rootCertificate = createSelfSigned(CordaX500Name("SystemUsers/Node", "IT", "R3 London", "London", "London", "GB")) + val markCertificate = createSignedBy(CordaX500Name("mark", "IT", "R3 London", "London", "London", "GB"), rootCertificate) + + // truststore needs to contain root CA for how the driver works... + server.keyStore["cordaclienttls"] = rootCertificate + server.trustStore["cordaclienttls"] = rootCertificate + server.trustStore["mark"] = markCertificate + + // here client keystore is empty +// client.keyStore["mark"] = markCertificate + client.trustStore["cordaclienttls"] = rootCertificate + + withKeyStores(server, client) { brokerSslOptions, clientSslOptions -> + testSslCommunication(brokerSslOptions, true, clientSslOptions, clientConnectionSpy = expectExceptionOfType(ActiveMQNotConnectedException::class)) + } + } + } + + @Test + fun rpc_with_no_ssl_on_client_side_and_ssl_on_server_side() { + withCertificates { server, client, createSelfSigned, createSignedBy -> + val rootCertificate = createSelfSigned(CordaX500Name("SystemUsers/Node", "IT", "R3 London", "London", "London", "GB")) + val markCertificate = createSignedBy(CordaX500Name("mark", "IT", "R3 London", "London", "London", "GB"), rootCertificate) + + // truststore needs to contain root CA for how the driver works... + server.keyStore["cordaclienttls"] = rootCertificate + server.trustStore["cordaclienttls"] = rootCertificate + server.trustStore["mark"] = markCertificate + + client.keyStore["mark"] = markCertificate + client.trustStore["cordaclienttls"] = rootCertificate + + withKeyStores(server, client) { brokerSslOptions, _ -> + // here client sslOptions are passed null (as in, do not use SSL) + testSslCommunication(brokerSslOptions, true, null, clientConnectionSpy = expectExceptionOfType(ActiveMQConnectionTimedOutException::class)) + } + } + } + + @Test + fun rpc_client_certificate_untrusted_to_server() { + withCertificates { server, client, createSelfSigned, _ -> + val rootCertificate = createSelfSigned(CordaX500Name("SystemUsers/Node", "IT", "R3 London", "London", "London", "GB")) + // here client's certificate is self-signed, otherwise Artemis allows the connection (the issuing certificate is in the truststore) + val markCertificate = createSelfSigned(CordaX500Name("mark", "IT", "R3 London", "London", "London", "GB")) + + // truststore needs to contain root CA for how the driver works... + server.keyStore["cordaclienttls"] = rootCertificate + server.trustStore["cordaclienttls"] = rootCertificate + // here the client certificate is not trusted by the server +// server.trustStore["mark"] = markCertificate + + client.keyStore["mark"] = markCertificate + client.trustStore["cordaclienttls"] = rootCertificate + + withKeyStores(server, client) { brokerSslOptions, clientSslOptions -> + testSslCommunication(brokerSslOptions, true, clientSslOptions, clientConnectionSpy = expectExceptionOfType(ActiveMQNotConnectedException::class)) + } + } + } + + private fun testSslCommunication(brokerSslOptions: SSLConfiguration, useSslForBroker: Boolean, clientSslOptions: SSLConfiguration?, address: NetworkHostAndPort = ports.nextHostAndPort(), + adminAddress: NetworkHostAndPort = ports.nextHostAndPort(), baseDirectory: Path = Files.createTempDirectory(null), clientConnectionSpy: (() -> Unit) -> Unit = {}) { + val maxMessageSize = 10000 + val jmxEnabled = false + val certificateChainCheckPolicies: List = listOf() + + val artemisBroker: ArtemisBroker = if (useSslForBroker) { + ArtemisRpcBroker.withSsl(address, brokerSslOptions, securityManager, certificateChainCheckPolicies, maxMessageSize, jmxEnabled, baseDirectory) + } else { + ArtemisRpcBroker.withoutSsl(address, adminAddress, brokerSslOptions, securityManager, certificateChainCheckPolicies, maxMessageSize, jmxEnabled, baseDirectory) + } + artemisBroker.use { broker -> + broker.start() + RPCMessagingClient(brokerSslOptions, broker.addresses.admin, maxMessageSize).use { server -> + server.start(TestRpcOpsImpl(), securityManager, broker.serverControl) + + val client = RPCClient(tcpTransport(ConnectionDirection.Outbound(), broker.addresses.primary, clientSslOptions)) + + clientConnectionSpy { + client.start(TestRpcOps::class.java, user.username, user.password).use { connection -> + connection.proxy.apply { + val greeting = greet("Frodo") + assertThat(greeting).isEqualTo("Oh, hello Frodo!") + } + } + } + } + } + } + + private fun RPCMessagingClient.start(ops: OPS, securityManager: RPCSecurityManager, brokerControl: ActiveMQServerControl) { + apply { + start(ops, securityManager) + start2(brokerControl) + } + } + + private fun expectExceptionOfType(exceptionType: KClass): (() -> Unit) -> Unit { + return { action -> assertThatThrownBy { action.invoke() }.isInstanceOf(exceptionType.java) } + } + + interface TestRpcOps : RPCOps { + fun greet(name: String): String + } + + class TestRpcOpsImpl : TestRpcOps { + override fun greet(name: String): String = "Oh, hello $name!" + + override val protocolVersion: Int = 1 + } +} \ No newline at end of file diff --git a/node/src/test/kotlin/net/corda/node/testsupport/UnsafeCertificatesFactory.kt b/node/src/test/kotlin/net/corda/node/testsupport/UnsafeCertificatesFactory.kt new file mode 100644 index 0000000000..bac182604e --- /dev/null +++ b/node/src/test/kotlin/net/corda/node/testsupport/UnsafeCertificatesFactory.kt @@ -0,0 +1,206 @@ +package net.corda.node.testsupport + +import net.corda.core.identity.CordaX500Name +import net.corda.core.internal.div +import net.corda.node.services.config.SslOptions +import net.corda.nodeapi.internal.crypto.* +import org.apache.commons.io.FileUtils +import sun.security.tools.keytool.CertAndKeyGen +import sun.security.x509.X500Name +import java.nio.file.Files +import java.nio.file.Path +import java.security.KeyPair +import java.security.KeyStore +import java.security.PrivateKey +import java.security.cert.X509Certificate +import java.time.Duration +import java.time.Instant +import java.time.Instant.now +import java.time.temporal.ChronoUnit +import java.util.* +import javax.security.auth.x500.X500Principal + +class UnsafeCertificatesFactory( + defaults: Defaults = defaults(), + private val keyType: String = defaults.keyType, + private val signatureAlgorithm: String = defaults.signatureAlgorithm, + private val keySize: Int = defaults.keySize, + private val certificatesValidityWindow: CertificateValidityWindow = defaults.certificatesValidityWindow, + private val keyStoreType: String = defaults.keyStoreType) { + + companion object { + private const val KEY_TYPE_RSA = "RSA" + private const val SIG_ALG_SHA_RSA = "SHA1WithRSA" + private const val KEY_SIZE = 1024 + private val DEFAULT_DURATION = Duration.of(365, ChronoUnit.DAYS) + private const val DEFAULT_KEYSTORE_TYPE = "JKS" + + fun defaults() = Defaults(KEY_TYPE_RSA, SIG_ALG_SHA_RSA, KEY_SIZE, CertificateValidityWindow(now(), DEFAULT_DURATION), DEFAULT_KEYSTORE_TYPE) + } + + data class Defaults( + val keyType: String, + val signatureAlgorithm: String, + val keySize: Int, + val certificatesValidityWindow: CertificateValidityWindow, + val keyStoreType: String) + + fun createSelfSigned(name: X500Name): UnsafeCertificate = createSelfSigned(name, keyType, signatureAlgorithm, keySize, certificatesValidityWindow) + + fun createSelfSigned(name: CordaX500Name) = createSelfSigned(name.asX500Name()) + + fun createSignedBy(subject: X500Principal, issuer: UnsafeCertificate): UnsafeCertificate = issuer.createSigned(subject, keyType, signatureAlgorithm, keySize, certificatesValidityWindow) + + fun createSignedBy(name: CordaX500Name, issuer: UnsafeCertificate): UnsafeCertificate = issuer.createSigned(name, keyType, signatureAlgorithm, keySize, certificatesValidityWindow) + + fun newKeyStore(password: String) = UnsafeKeyStore.create(keyStoreType, password) + + fun newKeyStores(keyStorePassword: String, trustStorePassword: String): KeyStores = KeyStores(newKeyStore(keyStorePassword), newKeyStore(trustStorePassword)) +} + +class KeyStores(val keyStore: UnsafeKeyStore, val trustStore: UnsafeKeyStore) { + fun save(directory: Path = Files.createTempDirectory(null)): AutoClosableSSLConfiguration { + val keyStoreFile = keyStore.toTemporaryFile("sslkeystore", directory = directory) + val trustStoreFile = trustStore.toTemporaryFile("truststore", directory = directory) + + val sslConfiguration = sslConfiguration(directory) + + return object : AutoClosableSSLConfiguration { + override val value = sslConfiguration + + override fun close() { + keyStoreFile.close() + trustStoreFile.close() + } + } + } + + private fun sslConfiguration(directory: Path) = SslOptions(directory, keyStore.password, trustStore.password) +} + +interface AutoClosableSSLConfiguration : AutoCloseable { + val value: SslOptions +} + +typealias KeyStoreEntry = Pair + +data class UnsafeKeyStore(private val delegate: KeyStore, val password: String) : Iterable { + companion object { + private const val JKS_TYPE = "JKS" + + fun create(type: String, password: String) = UnsafeKeyStore(newKeyStore(type, password), password) + + fun createJKS(password: String) = create(JKS_TYPE, password) + } + + operator fun plus(entry: KeyStoreEntry) = set(entry.first, entry.second) + + override fun iterator(): Iterator> = delegate.aliases().toList().map { alias -> alias to get(alias) }.iterator() + + operator fun get(alias: String): UnsafeCertificate { + return when { + delegate.isKeyEntry(alias) -> delegate.getCertificateAndKeyPair(alias, password).unsafe() + else -> UnsafeCertificate(delegate.getX509Certificate(alias), null) + } + } + + operator fun set(alias: String, certificate: UnsafeCertificate) { + delegate.setCertificateEntry(alias, certificate.value) + delegate.setKeyEntry(alias, certificate.privateKey, password.toCharArray(), arrayOf(certificate.value)) + } + + fun save(path: Path) = delegate.save(path, password) + + fun toTemporaryFile(fileName: String, fileExtension: String? = delegate.type.toLowerCase(), directory: Path): TemporaryFile { + return TemporaryFile("$fileName.$fileExtension", directory).also { save(it.path) } + } +} + +class TemporaryFile(fileName: String, val directory: Path) : AutoCloseable { + private val file = (directory / fileName).toFile() + init { + file.createNewFile() + file.deleteOnExit() + } + + val path: Path = file.toPath().toAbsolutePath() + + override fun close() = FileUtils.forceDelete(file) +} + +data class UnsafeCertificate(val value: X509Certificate, val privateKey: PrivateKey?) { + val keyPair = KeyPair(value.publicKey, privateKey) + + val principal: X500Principal get() = value.subjectX500Principal + + val issuer: X500Principal get() = value.issuerX500Principal + + fun createSigned(subject: X500Principal, keyType: String, signatureAlgorithm: String, keySize: Int, certificatesValidityWindow: CertificateValidityWindow): UnsafeCertificate { + val keyGen = keyGen(keyType, signatureAlgorithm, keySize) + + return UnsafeCertificate(X509Utilities.createCertificate( + certificateType = CertificateType.TLS, + issuer = value.subjectX500Principal, + issuerKeyPair = keyPair, + validityWindow = certificatesValidityWindow.datePair, + subject = subject, + subjectPublicKey = keyGen.publicKey + ), keyGen.privateKey) + } + + fun createSigned(name: CordaX500Name, keyType: String, signatureAlgorithm: String, keySize: Int, certificatesValidityWindow: CertificateValidityWindow) = createSigned(name.x500Principal, keyType, signatureAlgorithm, keySize, certificatesValidityWindow) +} + +data class CertificateValidityWindow(val from: Instant, val to: Instant) { + constructor(from: Instant, duration: Duration) : this(from, from.plus(duration)) + + val duration = Duration.between(from, to)!! + + val datePair = Date.from(from) to Date.from(to) +} + +private fun createSelfSigned(name: X500Name, keyType: String, signatureAlgorithm: String, keySize: Int, certificatesValidityWindow: CertificateValidityWindow): UnsafeCertificate { + val keyGen = keyGen(keyType, signatureAlgorithm, keySize) + return UnsafeCertificate(keyGen.getSelfCertificate(name, certificatesValidityWindow.duration.toMillis()), keyGen.privateKey) +} + +private fun CordaX500Name.asX500Name(): X500Name = X500Name.asX500Name(x500Principal) + +private fun CertificateAndKeyPair.unsafe() = UnsafeCertificate(certificate, keyPair.private) + +private fun keyGen(keyType: String, signatureAlgorithm: String, keySize: Int): CertAndKeyGen { + val keyGen = CertAndKeyGen(keyType, signatureAlgorithm) + keyGen.generate(keySize) + return keyGen +} + +private fun newKeyStore(type: String, password: String): KeyStore { + val keyStore = KeyStore.getInstance(type) + // Loading creates the store, can't do anything with it until it's loaded + keyStore.load(null, password.toCharArray()) + + return keyStore +} + +fun withKeyStores(server: KeyStores, client: KeyStores, action: (brokerSslOptions: SslOptions, clientSslOptions: SslOptions) -> Unit) { + val serverDir = Files.createTempDirectory(null) + FileUtils.forceDeleteOnExit(serverDir.toFile()) + + val clientDir = Files.createTempDirectory(null) + FileUtils.forceDeleteOnExit(clientDir.toFile()) + + server.save(serverDir).use { serverSslConfiguration -> + client.save(clientDir).use { clientSslConfiguration -> + action(serverSslConfiguration.value, clientSslConfiguration.value) + } + } + FileUtils.deleteQuietly(clientDir.toFile()) + FileUtils.deleteQuietly(serverDir.toFile()) +} + +fun withCertificates(factoryDefaults: UnsafeCertificatesFactory.Defaults = UnsafeCertificatesFactory.defaults(), action: (server: KeyStores, client: KeyStores, createSelfSigned: (name: CordaX500Name) -> UnsafeCertificate, createSignedBy: (name: CordaX500Name, issuer: UnsafeCertificate) -> UnsafeCertificate) -> Unit) { + val factory = UnsafeCertificatesFactory(factoryDefaults) + val server = factory.newKeyStores("serverKeyStorePass", "serverTrustKeyStorePass") + val client = factory.newKeyStores("clientKeyStorePass", "clientTrustKeyStorePass") + action(server, client, factory::createSelfSigned, factory::createSignedBy) +} \ No newline at end of file diff --git a/samples/irs-demo/src/integration-test/kotlin/net/corda/irs/IRSDemoTest.kt b/samples/irs-demo/src/integration-test/kotlin/net/corda/irs/IRSDemoTest.kt index 192e3379c1..26877ebf93 100644 --- a/samples/irs-demo/src/integration-test/kotlin/net/corda/irs/IRSDemoTest.kt +++ b/samples/irs-demo/src/integration-test/kotlin/net/corda/irs/IRSDemoTest.kt @@ -101,7 +101,7 @@ class IRSDemoTest { } private fun getFixingDateObservable(config: NodeConfiguration): Observable { - val client = CordaRPCClient(config.rpcAddress!!) + val client = CordaRPCClient(config.rpcOptions.address!!) val proxy = client.start("user", "password").proxy val vaultUpdates = proxy.vaultTrackBy().updates diff --git a/samples/irs-demo/src/integration-test/kotlin/net/corda/test/spring/SpringDriver.kt b/samples/irs-demo/src/integration-test/kotlin/net/corda/test/spring/SpringDriver.kt index 9d83d481ba..cba6ba4da3 100644 --- a/samples/irs-demo/src/integration-test/kotlin/net/corda/test/spring/SpringDriver.kt +++ b/samples/irs-demo/src/integration-test/kotlin/net/corda/test/spring/SpringDriver.kt @@ -110,7 +110,7 @@ data class SpringBootDriverDSL(private val driverDSL: DriverDSLImpl) : InternalD arguments = listOf( "--base-directory", handle.configuration.baseDirectory.toString(), "--server.port=${handle.webAddress.port}", - "--corda.host=${handle.configuration.rpcAddress}", + "--corda.host=${handle.configuration.rpcOptions.address}", "--corda.user=${handle.configuration.rpcUsers.first().username}", "--corda.password=${handle.configuration.rpcUsers.first().password}" ), diff --git a/samples/trader-demo/src/integration-test/kotlin/net/corda/traderdemo/TraderDemoTest.kt b/samples/trader-demo/src/integration-test/kotlin/net/corda/traderdemo/TraderDemoTest.kt index ad2aa5c637..10e20e070c 100644 --- a/samples/trader-demo/src/integration-test/kotlin/net/corda/traderdemo/TraderDemoTest.kt +++ b/samples/trader-demo/src/integration-test/kotlin/net/corda/traderdemo/TraderDemoTest.kt @@ -40,11 +40,11 @@ class TraderDemoTest { ).map { (it.getOrThrow() as NodeHandle.InProcess).node } nodeA.registerInitiatedFlow(BuyerFlow::class.java) val (nodeARpc, nodeBRpc) = listOf(nodeA, nodeB).map { - val client = CordaRPCClient(it.internals.configuration.rpcAddress!!) + val client = CordaRPCClient(it.internals.configuration.rpcOptions.address!!) client.start(demoUser.username, demoUser.password).proxy } val nodeBankRpc = let { - val client = CordaRPCClient(bankNode.internals.configuration.rpcAddress!!) + val client = CordaRPCClient(bankNode.internals.configuration.rpcOptions.address!!) client.start(bankUser.username, bankUser.password).proxy } diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/driver/Driver.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/driver/Driver.kt index fa64dbee15..85b863ba6f 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/driver/Driver.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/driver/Driver.kt @@ -14,9 +14,10 @@ import net.corda.node.internal.Node import net.corda.node.internal.StartedNode import net.corda.node.services.config.NodeConfiguration import net.corda.node.services.config.VerifierType +import net.corda.nodeapi.internal.config.SSLConfiguration import net.corda.testing.core.DUMMY_NOTARY_NAME -import net.corda.testing.node.User import net.corda.testing.node.NotarySpec +import net.corda.testing.node.User import net.corda.testing.node.internal.DriverDSLImpl import net.corda.testing.node.internal.genericDriver import net.corda.testing.node.internal.getTimestampAsDirectoryName @@ -32,7 +33,7 @@ import java.util.concurrent.atomic.AtomicInteger data class NotaryHandle(val identity: Party, val validating: Boolean, val nodeHandles: CordaFuture>) @DoNotImplement -sealed class NodeHandle { +sealed class NodeHandle : AutoCloseable { abstract val nodeInfo: NodeInfo /** * Interface to the node's RPC system. The first RPC user will be used to login if are any, otherwise a default one @@ -48,6 +49,11 @@ sealed class NodeHandle { */ abstract fun stop() + /** + * Closes and stops the node. + */ + override fun close() = stop() + data class OutOfProcess( override val nodeInfo: NodeInfo, override val rpc: CordaRPCOps, @@ -87,7 +93,13 @@ sealed class NodeHandle { } } - fun rpcClientToNode(): CordaRPCClient = CordaRPCClient(configuration.rpcAddress!!) + /** + * Connects to node through RPC. + * + * @param sslConfiguration specifies SSL options. + */ + @JvmOverloads + fun rpcClientToNode(sslConfiguration: SSLConfiguration? = null): CordaRPCClient = CordaRPCClient(configuration.rpcOptions.address!!, sslConfiguration = sslConfiguration) } data class WebserverHandle( diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/DriverDSLImpl.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/DriverDSLImpl.kt index 5f5933c031..bfb34c1f76 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/DriverDSLImpl.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/DriverDSLImpl.kt @@ -145,14 +145,18 @@ class DriverDSLImpl( } private fun establishRpc(config: NodeConfig, processDeathFuture: CordaFuture): CordaFuture { - val rpcAddress = config.corda.rpcAddress!! - val client = CordaRPCClient(rpcAddress) + val rpcAddress = config.corda.rpcOptions.address!! + val client = if (config.corda.rpcOptions.useSsl) { + CordaRPCClient(rpcAddress, sslConfiguration = config.corda.rpcOptions.sslConfig) + } else { + CordaRPCClient(rpcAddress) + } val connectionFuture = poll(executorService, "RPC connection") { try { config.corda.rpcUsers[0].run { client.start(username, password) } } catch (e: Exception) { if (processDeathFuture.isDone) throw e - log.error("Exception $e, Retrying RPC connection at $rpcAddress") + log.error("Exception while connecting to RPC, retrying to connect at $rpcAddress", e) null } } @@ -202,6 +206,7 @@ class DriverDSLImpl( maximumHeapSize: String = "200m", p2pAddress: NetworkHostAndPort = portAllocation.nextHostAndPort()): CordaFuture { val rpcAddress = portAllocation.nextHostAndPort() + val rpcAdminAddress = portAllocation.nextHostAndPort() val webAddress = portAllocation.nextHostAndPort() val users = rpcUsers.map { it.copy(permissions = it.permissions + DRIVER_REQUIRED_PERMISSIONS) } val czUrlConfig = if (compatibilityZone != null) mapOf("compatibilityZoneURL" to compatibilityZone.url.toString()) else emptyMap() @@ -211,7 +216,8 @@ class DriverDSLImpl( configOverrides = configOf( "myLegalName" to name.toString(), "p2pAddress" to p2pAddress.toString(), - "rpcAddress" to rpcAddress.toString(), + "rpcSettings.address" to rpcAddress.toString(), + "rpcSettings.adminAddress" to rpcAdminAddress.toString(), "webAddress" to webAddress.toString(), "useTestClock" to useTestClock, "rpcUsers" to if (users.isEmpty()) defaultRpcUserList else users.map { it.toConfig().root().unwrapped() }, @@ -312,7 +318,23 @@ class DriverDSLImpl( private fun startCordformNode(cordform: CordformNode, localNetworkMap: LocalNetworkMap): CordaFuture { val name = CordaX500Name.parse(cordform.name) // TODO We shouldn't have to allocate an RPC or web address if they're not specified. We're having to do this because of startNodeInternal - val rpcAddress = if (cordform.rpcAddress == null) mapOf("rpcAddress" to portAllocation.nextHostAndPort().toString()) else emptyMap() + val rpcAddress = if (cordform.rpcAddress == null) { + val overrides = mutableMapOf("rpcSettings.address" to portAllocation.nextHostAndPort().toString()) + cordform.config.apply { + if (!hasPath("rpcSettings.useSsl") || !getBoolean("rpcSettings.useSsl")) { + overrides += "rpcSettings.adminAddress" to portAllocation.nextHostAndPort().toString() + } + } + overrides + } else { + val overrides = mutableMapOf() + cordform.config.apply { + if ((!hasPath("rpcSettings.useSsl") || !getBoolean("rpcSettings.useSsl")) && !hasPath("rpcSettings.adminAddress")) { + overrides += "rpcSettings.adminAddress" to portAllocation.nextHostAndPort().toString() + } + } + overrides + } val webAddress = cordform.webAddress?.let { NetworkHostAndPort.parse(it) } ?: portAllocation.nextHostAndPort() val notary = if (cordform.notary != null) mapOf("notary" to cordform.notary) else emptyMap() val rpcUsers = cordform.rpcUsers diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/NodeBasedTest.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/NodeBasedTest.kt index 5239e6998e..70cfb2c7ea 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/NodeBasedTest.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/NodeBasedTest.kt @@ -69,7 +69,7 @@ abstract class NodeBasedTest(private val cordappPackages: List = emptyLi val portNotBoundChecks = nodes.flatMap { listOf( it.internals.configuration.p2pAddress.let { addressMustNotBeBoundFuture(shutdownExecutor, it) }, - it.internals.configuration.rpcAddress?.let { addressMustNotBeBoundFuture(shutdownExecutor, it) } + it.internals.configuration.rpcOptions.address?.let { addressMustNotBeBoundFuture(shutdownExecutor, it) } ) }.filterNotNull() nodes.clear() @@ -85,7 +85,7 @@ abstract class NodeBasedTest(private val cordappPackages: List = emptyLi rpcUsers: List = emptyList(), configOverrides: Map = emptyMap()): StartedNode { val baseDirectory = baseDirectory(legalName).createDirectories() - val localPort = getFreeLocalPorts("localhost", 2) + val localPort = getFreeLocalPorts("localhost", 3) val p2pAddress = configOverrides["p2pAddress"] ?: localPort[0].toString() val config = ConfigHelper.loadConfig( baseDirectory = baseDirectory, @@ -93,7 +93,8 @@ abstract class NodeBasedTest(private val cordappPackages: List = emptyLi configOverrides = configOf( "myLegalName" to legalName.toString(), "p2pAddress" to p2pAddress, - "rpcAddress" to localPort[1].toString(), + "rpcSettings.address" to localPort[1].toString(), + "rpcSettings.adminAddress" to localPort[2].toString(), "rpcUsers" to rpcUsers.map { it.toConfig().root().unwrapped() } ) + configOverrides ) diff --git a/testing/test-utils/src/main/kotlin/net/corda/testing/internal/InternalTestUtils.kt b/testing/test-utils/src/main/kotlin/net/corda/testing/internal/InternalTestUtils.kt index 1d73ec5363..be8cd9a435 100644 --- a/testing/test-utils/src/main/kotlin/net/corda/testing/internal/InternalTestUtils.kt +++ b/testing/test-utils/src/main/kotlin/net/corda/testing/internal/InternalTestUtils.kt @@ -4,7 +4,9 @@ import com.nhaarman.mockito_kotlin.doAnswer import net.corda.core.crypto.Crypto import net.corda.core.crypto.Crypto.generateKeyPair import net.corda.core.identity.CordaX500Name +import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.loggerFor +import net.corda.node.services.config.SslOptions import net.corda.node.services.config.configureDevKeyAndTrustStores import net.corda.nodeapi.internal.config.SSLConfiguration import net.corda.nodeapi.internal.createDevNodeCa @@ -115,3 +117,22 @@ fun createDevNodeCaCertPath( /** Application of [doAnswer] that gets a value from the given [map] using the arg at [argIndex] as key. */ fun doLookup(map: Map<*, *>, argIndex: Int = 0) = doAnswer { map[it.arguments[argIndex]] } + +fun SslOptions.useSslRpcOverrides(): Map { + return mapOf( + "rpcSettings.useSsl" to "true", + "rpcSettings.ssl.certificatesDirectory" to certificatesDirectory.toString(), + "rpcSettings.ssl.keyStorePassword" to keyStorePassword, + "rpcSettings.ssl.trustStorePassword" to trustStorePassword + ) +} + +fun SslOptions.noSslRpcOverrides(rpcAdminAddress: NetworkHostAndPort): Map { + return mapOf( + "rpcSettings.adminAddress" to rpcAdminAddress.toString(), + "rpcSettings.useSsl" to "false", + "rpcSettings.ssl.certificatesDirectory" to certificatesDirectory.toString(), + "rpcSettings.ssl.keyStorePassword" to keyStorePassword, + "rpcSettings.ssl.trustStorePassword" to trustStorePassword + ) +} diff --git a/tools/demobench/src/test/kotlin/net/corda/demobench/model/NodeConfigTest.kt b/tools/demobench/src/test/kotlin/net/corda/demobench/model/NodeConfigTest.kt index 0c9caca94a..1c82a08654 100644 --- a/tools/demobench/src/test/kotlin/net/corda/demobench/model/NodeConfigTest.kt +++ b/tools/demobench/src/test/kotlin/net/corda/demobench/model/NodeConfigTest.kt @@ -41,7 +41,7 @@ class NodeConfigTest { val fullConfig = nodeConfig.parseAsNodeConfiguration() assertEquals(myLegalName, fullConfig.myLegalName) - assertEquals(localPort(40002), fullConfig.rpcAddress) + assertEquals(localPort(40002), fullConfig.rpcOptions.address) assertEquals(localPort(10001), fullConfig.p2pAddress) assertEquals(listOf(user("jenny")), fullConfig.rpcUsers) assertThat(fullConfig.dataSourceProperties["dataSource.url"] as String).contains("AUTO_SERVER_PORT=30001") diff --git a/tools/explorer/src/main/kotlin/net/corda/explorer/ExplorerSimulation.kt b/tools/explorer/src/main/kotlin/net/corda/explorer/ExplorerSimulation.kt index f0729b9524..9480d13b6b 100644 --- a/tools/explorer/src/main/kotlin/net/corda/explorer/ExplorerSimulation.kt +++ b/tools/explorer/src/main/kotlin/net/corda/explorer/ExplorerSimulation.kt @@ -83,7 +83,7 @@ class ExplorerSimulation(private val options: OptionSet) { issuerNodeUSD = issuerUSD.get() arrayOf(notaryNode, aliceNode, bobNode, issuerNodeGBP, issuerNodeUSD).forEach { - println("${it.nodeInfo.legalIdentities.first()} started on ${it.configuration.rpcAddress}") + println("${it.nodeInfo.legalIdentities.first()} started on ${it.configuration.rpcOptions.address}") } when { diff --git a/webserver/src/main/kotlin/net/corda/webserver/WebServerConfig.kt b/webserver/src/main/kotlin/net/corda/webserver/WebServerConfig.kt index b3be74b11b..fab23b80bc 100644 --- a/webserver/src/main/kotlin/net/corda/webserver/WebServerConfig.kt +++ b/webserver/src/main/kotlin/net/corda/webserver/WebServerConfig.kt @@ -2,8 +2,8 @@ package net.corda.webserver import com.typesafe.config.Config import net.corda.core.utilities.NetworkHostAndPort -import net.corda.nodeapi.internal.config.User import net.corda.nodeapi.internal.config.NodeSSLConfiguration +import net.corda.nodeapi.internal.config.User import net.corda.nodeapi.internal.config.getValue import java.nio.file.Path @@ -16,7 +16,15 @@ class WebServerConfig(override val baseDirectory: Path, val config: Config) : No val exportJMXto: String get() = "http" val useHTTPS: Boolean by config val myLegalName: String by config - val rpcAddress: NetworkHostAndPort by config + val rpcAddress: NetworkHostAndPort by lazy { + if (config.hasPath("rpcSettings.address")) { + return@lazy NetworkHostAndPort.parse(config.getString("rpcSettings.address")) + } + if (config.hasPath("rpcAddress")) { + return@lazy NetworkHostAndPort.parse(config.getString("rpcAddress")) + } + throw Exception("Missing rpc address property. Either 'rpcSettings' or 'rpcAddress' must be specified.") + } val webAddress: NetworkHostAndPort by config val rpcUsers: List by config } From 5df50c0e81fae9b763d07dab5fc03ca67d768b22 Mon Sep 17 00:00:00 2001 From: Joel Dudley Date: Tue, 23 Jan 2018 17:28:24 +0000 Subject: [PATCH 22/36] Fixes a bug in the deserialisation of UniqueIdentifiers in the CRaSH shell. --- .../src/main/kotlin/net/corda/node/shell/InteractiveShell.kt | 2 +- .../net/corda/node/shell/CustomTypeJsonParsingTests.kt | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/node/src/main/kotlin/net/corda/node/shell/InteractiveShell.kt b/node/src/main/kotlin/net/corda/node/shell/InteractiveShell.kt index ea6779f983..57fb06dee1 100644 --- a/node/src/main/kotlin/net/corda/node/shell/InteractiveShell.kt +++ b/node/src/main/kotlin/net/corda/node/shell/InteractiveShell.kt @@ -528,7 +528,7 @@ object InteractiveShell { return UniqueIdentifier(ids[0], uuid) } //Any other string used as externalId. - return UniqueIdentifier(p.text) + return UniqueIdentifier.fromString(p.text) } } diff --git a/node/src/test/kotlin/net/corda/node/shell/CustomTypeJsonParsingTests.kt b/node/src/test/kotlin/net/corda/node/shell/CustomTypeJsonParsingTests.kt index 1622f839f8..bd9eee844d 100644 --- a/node/src/test/kotlin/net/corda/node/shell/CustomTypeJsonParsingTests.kt +++ b/node/src/test/kotlin/net/corda/node/shell/CustomTypeJsonParsingTests.kt @@ -34,10 +34,11 @@ class CustomTypeJsonParsingTests { @Test fun `Deserializing UniqueIdentifier by parsing string`() { - val json = """{"linearId":"26b37265-a1fd-4c77-b2e0-715917ef619f"}""" + val id = "26b37265-a1fd-4c77-b2e0-715917ef619f" + val json = """{"linearId":"$id"}""" val state = objectMapper.readValue(json) - assertEquals("26b37265-a1fd-4c77-b2e0-715917ef619f", state.linearId.externalId) + assertEquals(id, state.linearId.id.toString()) } @Test From 61c7de22d6717bd3f11dd059bc1acc2c5a3febde Mon Sep 17 00:00:00 2001 From: Shams Asari Date: Wed, 24 Jan 2018 07:51:55 +0000 Subject: [PATCH 23/36] Replaced KeyStoreWrapper with X509KeyStore, which is still a wrapper but assumes only X509 certs and has better APIs (#2411) --- .../net/corda/core/internal/CertRole.kt | 43 ++++------- .../corda/core/crypto/CompositeKeyTests.kt | 14 ++-- .../core/identity/PartyAndCertificateTest.kt | 29 ++++--- .../nodeapi/internal/DevIdentityGenerator.kt | 26 +++---- .../nodeapi/internal/KeyStoreConfigHelpers.kt | 77 +++++++++---------- .../internal/config/SSLConfiguration.kt | 13 ++++ .../internal/crypto/KeyStoreUtilities.kt | 19 ++--- .../internal/crypto/KeyStoreWrapper.kt | 40 ---------- .../nodeapi/internal/crypto/X509KeyStore.kt | 74 ++++++++++++++++++ .../nodeapi/internal/crypto/X509Utilities.kt | 5 +- .../internal/crypto/X509UtilitiesTest.kt | 8 +- .../net/corda/node/NodeKeystoreCheckTest.kt | 20 ++--- .../net/corda/node/amqp/AMQPBridgeTest.kt | 13 ++-- .../net/corda/node/amqp/ProtonWrapperTests.kt | 13 ++-- .../messaging/MQSecurityAsNodeTest.kt | 30 ++++---- .../net/corda/node/internal/AbstractNode.kt | 56 +++++++------- .../node/services/config/ConfigUtilities.kt | 23 +++--- .../services/messaging/AMQPBridgeManager.kt | 5 +- .../messaging/ArtemisMessagingServer.kt | 6 +- .../services/messaging/CoreBridgeManager.kt | 3 +- .../services/messaging/RPCMessagingClient.kt | 4 +- .../HTTPNetworkRegistrationService.kt | 8 +- .../registration/NetworkRegistrationHelper.kt | 56 +++++++------- .../NetworkRegistrationService.kt | 4 +- .../NetworkRegistrationHelperTest.kt | 46 +++++------ .../testing/node/internal/DriverDSLImpl.kt | 12 +-- 26 files changed, 330 insertions(+), 317 deletions(-) delete mode 100644 node-api/src/main/kotlin/net/corda/nodeapi/internal/crypto/KeyStoreWrapper.kt create mode 100644 node-api/src/main/kotlin/net/corda/nodeapi/internal/crypto/X509KeyStore.kt diff --git a/core/src/main/kotlin/net/corda/core/internal/CertRole.kt b/core/src/main/kotlin/net/corda/core/internal/CertRole.kt index c2224b3c5a..f14633c883 100644 --- a/core/src/main/kotlin/net/corda/core/internal/CertRole.kt +++ b/core/src/main/kotlin/net/corda/core/internal/CertRole.kt @@ -1,6 +1,7 @@ package net.corda.core.internal import net.corda.core.CordaOID +import net.corda.core.utilities.NonEmptySet import org.bouncycastle.asn1.ASN1Encodable import org.bouncycastle.asn1.ASN1Integer import org.bouncycastle.asn1.ASN1Primitive @@ -23,40 +24,38 @@ import java.security.cert.X509Certificate // NOTE: The order of the entries in the enum MUST NOT be changed, as their ordinality is used as an identifier. Please // also note that IDs are numbered from 1 upwards, matching numbering of other enum types in ASN.1 specifications. // TODO: Link to the specification once it has a permanent URL -enum class CertRole(val validParents: Set, val isIdentity: Boolean, val isWellKnown: Boolean) : ASN1Encodable { +enum class CertRole(val validParents: NonEmptySet, val isIdentity: Boolean, val isWellKnown: Boolean) : ASN1Encodable { /** * Intermediate CA (Doorman service). */ - INTERMEDIATE_CA(setOf(null), false, false), + INTERMEDIATE_CA(NonEmptySet.of(null), false, false), /** Signing certificate for the network map. */ - NETWORK_MAP(setOf(null), false, false), + NETWORK_MAP(NonEmptySet.of(null), false, false), /** Well known (publicly visible) identity of a service (such as notary). */ - SERVICE_IDENTITY(setOf(INTERMEDIATE_CA), true, true), + SERVICE_IDENTITY(NonEmptySet.of(INTERMEDIATE_CA), true, true), /** Node level CA from which the TLS and well known identity certificates are issued. */ - NODE_CA(setOf(INTERMEDIATE_CA), false, false), + NODE_CA(NonEmptySet.of(INTERMEDIATE_CA), false, false), /** Transport layer security certificate for a node. */ - TLS(setOf(NODE_CA), false, false), + TLS(NonEmptySet.of(NODE_CA), false, false), /** Well known (publicly visible) identity of a legal entity. */ - LEGAL_IDENTITY(setOf(INTERMEDIATE_CA, NODE_CA), true, true), + LEGAL_IDENTITY(NonEmptySet.of(INTERMEDIATE_CA, NODE_CA), true, true), /** Confidential (limited visibility) identity of a legal entity. */ - CONFIDENTIAL_LEGAL_IDENTITY(setOf(LEGAL_IDENTITY), true, false); + CONFIDENTIAL_LEGAL_IDENTITY(NonEmptySet.of(LEGAL_IDENTITY), true, false); companion object { - private var cachedRoles: Array? = null + private val values by lazy(LazyThreadSafetyMode.NONE, CertRole::values) + /** * Get a role from its ASN.1 encoded form. * * @throws IllegalArgumentException if the encoded data is not a valid role. */ fun getInstance(id: ASN1Integer): CertRole { - if (cachedRoles == null) { - cachedRoles = CertRole.values() - } val idVal = id.value - require(idVal.compareTo(BigInteger.ZERO) > 0) { "Invalid role ID" } + require(idVal > BigInteger.ZERO) { "Invalid role ID" } return try { val ordinal = idVal.intValueExact() - 1 - cachedRoles!![ordinal] + values[ordinal] } catch (ex: ArithmeticException) { throw IllegalArgumentException("Invalid role ID") } catch (ex: ArrayIndexOutOfBoundsException) { @@ -77,14 +76,7 @@ enum class CertRole(val validParents: Set, val isIdentity: Boolean, v * @return the role if the extension is present, or null otherwise. * @throws IllegalArgumentException if the extension is present but is invalid. */ - fun extract(cert: Certificate): CertRole? { - val x509Cert = cert as? X509Certificate - return if (x509Cert != null) { - extract(x509Cert) - } else { - null - } - } + fun extract(cert: Certificate): CertRole? = (cert as? X509Certificate)?.let { extract(it) } /** * Get a role from a certificate. @@ -93,12 +85,9 @@ enum class CertRole(val validParents: Set, val isIdentity: Boolean, v * @throws IllegalArgumentException if the extension is present but is invalid. */ fun extract(cert: X509Certificate): CertRole? { - val extensionData: ByteArray? = cert.getExtensionValue(CordaOID.X509_EXTENSION_CORDA_ROLE) - return if (extensionData != null) { - val extensionString = DEROctetString.getInstance(extensionData) + return cert.getExtensionValue(CordaOID.X509_EXTENSION_CORDA_ROLE)?.let { + val extensionString = DEROctetString.getInstance(it) getInstance(extensionString.octets) - } else { - null } } } diff --git a/core/src/test/kotlin/net/corda/core/crypto/CompositeKeyTests.kt b/core/src/test/kotlin/net/corda/core/crypto/CompositeKeyTests.kt index 9e749e3451..4c730ddf4a 100644 --- a/core/src/test/kotlin/net/corda/core/crypto/CompositeKeyTests.kt +++ b/core/src/test/kotlin/net/corda/core/crypto/CompositeKeyTests.kt @@ -6,9 +6,13 @@ import net.corda.core.internal.div import net.corda.core.serialization.serialize import net.corda.core.utilities.OpaqueBytes import net.corda.core.utilities.toBase58String -import net.corda.nodeapi.internal.crypto.* +import net.corda.nodeapi.internal.crypto.CertificateType +import net.corda.nodeapi.internal.crypto.X509KeyStore +import net.corda.nodeapi.internal.crypto.X509Utilities +import net.corda.nodeapi.internal.crypto.loadKeyStore import net.corda.testing.core.SerializationEnvironmentRule import net.corda.testing.internal.kryoSpecific +import org.assertj.core.api.Assertions.assertThat import org.junit.Rule import org.junit.Test import org.junit.rules.TemporaryFolder @@ -341,9 +345,9 @@ class CompositeKeyTests { // Store certificate to keystore. val keystorePath = tempFolder.root.toPath() / "keystore.jks" - val keystore = loadOrCreateKeyStore(keystorePath, "password") - keystore.setCertificateEntry("CompositeKey", compositeKeyCert) - keystore.save(keystorePath, "password") + X509KeyStore.fromFile(keystorePath, "password", createNew = true).update { + setCertificate("CompositeKey", compositeKeyCert) + } // Load keystore from disk. val keystore2 = loadKeyStore(keystorePath, "password") @@ -352,7 +356,7 @@ class CompositeKeyTests { val key = keystore2.getCertificate("CompositeKey").publicKey // Convert sun public key to Composite key. val compositeKey2 = Crypto.toSupportedPublicKey(key) - assertTrue { compositeKey2 is CompositeKey } + assertThat(compositeKey2).isInstanceOf(CompositeKey::class.java) // Run the same composite key test again. assertTrue { compositeKey2.isFulfilledBy(signatures.byKeys()) } diff --git a/core/src/test/kotlin/net/corda/core/identity/PartyAndCertificateTest.kt b/core/src/test/kotlin/net/corda/core/identity/PartyAndCertificateTest.kt index faca71eeba..39cdbbe8ba 100644 --- a/core/src/test/kotlin/net/corda/core/identity/PartyAndCertificateTest.kt +++ b/core/src/test/kotlin/net/corda/core/identity/PartyAndCertificateTest.kt @@ -1,21 +1,19 @@ package net.corda.core.identity +import com.google.common.jimfs.Configuration.unix +import com.google.common.jimfs.Jimfs import net.corda.core.crypto.entropyToKeyPair -import net.corda.core.internal.read import net.corda.core.serialization.deserialize import net.corda.core.serialization.serialize -import net.corda.nodeapi.internal.crypto.KEYSTORE_TYPE import net.corda.nodeapi.internal.crypto.X509CertificateFactory -import net.corda.nodeapi.internal.crypto.save +import net.corda.nodeapi.internal.crypto.X509KeyStore import net.corda.testing.core.DEV_ROOT_CA import net.corda.testing.core.SerializationEnvironmentRule import net.corda.testing.core.getTestPartyAndCertificate import org.assertj.core.api.Assertions.assertThat import org.junit.Rule import org.junit.Test -import java.io.File import java.math.BigInteger -import java.security.KeyStore import kotlin.test.assertFailsWith class PartyAndCertificateTest { @@ -46,17 +44,18 @@ class PartyAndCertificateTest { CordaX500Name(organisation = "Test Corp", locality = "Madrid", country = "ES"), entropyToKeyPair(BigInteger.valueOf(83)).public)) val original = identity.certificate + val alias = identity.name.toString() val storePassword = "test" - val keyStoreFilePath = File.createTempFile("serialization_test", "jks").toPath() - var keyStore = KeyStore.getInstance(KEYSTORE_TYPE) - keyStore.load(null, storePassword.toCharArray()) - keyStore.setCertificateEntry(identity.name.toString(), original) - keyStore.save(keyStoreFilePath, storePassword) + Jimfs.newFileSystem(unix()).use { + val keyStoreFile = it.getPath("/serialization_test.jks") - // Load the key store back in again - keyStore = KeyStore.getInstance(KEYSTORE_TYPE) - keyStoreFilePath.read { keyStore.load(it, storePassword.toCharArray()) } - val copy = keyStore.getCertificate(identity.name.toString()) - assertThat(copy).isEqualTo(original) // .isNotSameAs(original) + X509KeyStore.fromFile(keyStoreFile, storePassword, createNew = true).update { + setCertificate(alias, original) + } + + // Load the key store back in again + val copy = X509KeyStore.fromFile(keyStoreFile, storePassword).getCertificate(alias) + assertThat(copy).isEqualTo(original) + } } } diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/DevIdentityGenerator.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/DevIdentityGenerator.kt index 0965db3a42..cc0563bef3 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/DevIdentityGenerator.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/DevIdentityGenerator.kt @@ -1,7 +1,6 @@ package net.corda.nodeapi.internal import net.corda.core.crypto.CompositeKey -import net.corda.core.crypto.Crypto import net.corda.core.crypto.generateKeyPair import net.corda.core.identity.CordaX500Name import net.corda.core.identity.Party @@ -9,7 +8,9 @@ import net.corda.core.internal.createDirectories import net.corda.core.internal.div import net.corda.core.utilities.trace import net.corda.nodeapi.internal.config.NodeSSLConfiguration -import net.corda.nodeapi.internal.crypto.* +import net.corda.nodeapi.internal.crypto.CertificateType +import net.corda.nodeapi.internal.crypto.X509KeyStore +import net.corda.nodeapi.internal.crypto.X509Utilities import org.slf4j.LoggerFactory import java.nio.file.Path import java.security.KeyPair @@ -37,10 +38,9 @@ object DevIdentityGenerator { } nodeSslConfig.certificatesDirectory.createDirectories() - nodeSslConfig.createDevKeyStores(legalName) + val (nodeKeyStore) = nodeSslConfig.createDevKeyStores(legalName) - val keyStoreWrapper = KeyStoreWrapper(nodeSslConfig.nodeKeystore, nodeSslConfig.keyStorePassword) - val identity = keyStoreWrapper.storeLegalIdentity(legalName, "$NODE_IDENTITY_ALIAS_PREFIX-private-key", Crypto.generateKeyPair()) + val identity = nodeKeyStore.storeLegalIdentity("$NODE_IDENTITY_ALIAS_PREFIX-private-key") return identity.party } @@ -78,13 +78,13 @@ object DevIdentityGenerator { publicKey) } val distServKeyStoreFile = (nodeDir / "certificates").createDirectories() / "distributedService.jks" - val keystore = loadOrCreateKeyStore(distServKeyStoreFile, "cordacadevpass") - keystore.setCertificateEntry("$DISTRIBUTED_NOTARY_ALIAS_PREFIX-composite-key", compositeKeyCert) - keystore.setKeyEntry( - "$DISTRIBUTED_NOTARY_ALIAS_PREFIX-private-key", - keyPair.private, - "cordacadevkeypass".toCharArray(), - arrayOf(serviceKeyCert, DEV_INTERMEDIATE_CA.certificate, DEV_ROOT_CA.certificate)) - keystore.save(distServKeyStoreFile, "cordacadevpass") + X509KeyStore.fromFile(distServKeyStoreFile, "cordacadevpass", createNew = true).update { + setCertificate("$DISTRIBUTED_NOTARY_ALIAS_PREFIX-composite-key", compositeKeyCert) + setPrivateKey( + "$DISTRIBUTED_NOTARY_ALIAS_PREFIX-private-key", + keyPair.private, + listOf(serviceKeyCert, DEV_INTERMEDIATE_CA.certificate, DEV_ROOT_CA.certificate), + "cordacadevkeypass") + } } } diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/KeyStoreConfigHelpers.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/KeyStoreConfigHelpers.kt index bbbde7aec4..2ecc461771 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/KeyStoreConfigHelpers.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/KeyStoreConfigHelpers.kt @@ -1,7 +1,9 @@ package net.corda.nodeapi.internal +import net.corda.core.crypto.Crypto import net.corda.core.crypto.Crypto.generateKeyPair import net.corda.core.identity.CordaX500Name +import net.corda.core.identity.PartyAndCertificate import net.corda.core.internal.x500Name import net.corda.nodeapi.internal.config.SSLConfiguration import net.corda.nodeapi.internal.crypto.* @@ -20,50 +22,47 @@ import javax.security.auth.x500.X500Principal */ fun SSLConfiguration.createDevKeyStores(legalName: CordaX500Name, rootCert: X509Certificate = DEV_ROOT_CA.certificate, - intermediateCa: CertificateAndKeyPair = DEV_INTERMEDIATE_CA) { + intermediateCa: CertificateAndKeyPair = DEV_INTERMEDIATE_CA): Pair { val (nodeCaCert, nodeCaKeyPair) = createDevNodeCa(intermediateCa, legalName) - createDevKeyStores(rootCert, intermediateCa, nodeCaCert, nodeCaKeyPair, legalName) -} - -/** - * Create the node and SSL key stores needed by a node. The node key store will be populated with a node CA cert (using - * the given legal name), and the SSL key store will store the TLS cert which is a sub-cert of the node CA. - */ -fun SSLConfiguration.createDevKeyStores(rootCert: X509Certificate, intermediateCa: CertificateAndKeyPair, nodeCaCert: X509Certificate, nodeCaKeyPair: KeyPair, legalName: CordaX500Name) { - createNodeKeyStore(nodeCaCert, nodeCaKeyPair, intermediateCa, rootCert) - createSslKeyStore(nodeCaCert, nodeCaKeyPair, legalName, intermediateCa, rootCert) -} - -/** - * Create the SSL key store needed by a node. - */ -fun SSLConfiguration.createSslKeyStore(nodeCaCert: X509Certificate, nodeCaKeyPair: KeyPair, legalName: CordaX500Name, intermediateCa: CertificateAndKeyPair, rootCert: X509Certificate) { - val tlsKeyPair = generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) - val tlsCert = X509Utilities.createCertificate(CertificateType.TLS, nodeCaCert, nodeCaKeyPair, legalName.x500Principal, tlsKeyPair.public) - - loadOrCreateKeyStore(sslKeystore, keyStorePassword).apply { - addOrReplaceKey( - X509Utilities.CORDA_CLIENT_TLS, - tlsKeyPair.private, - keyStorePassword.toCharArray(), - arrayOf(tlsCert, nodeCaCert, intermediateCa.certificate, rootCert)) - save(sslKeystore, keyStorePassword) - } -} - -/** - * Create the node key store needed by a node. - */ -fun SSLConfiguration.createNodeKeyStore(nodeCaCert: X509Certificate, nodeCaKeyPair: KeyPair, intermediateCa: CertificateAndKeyPair, rootCert: X509Certificate) { - loadOrCreateKeyStore(nodeKeystore, keyStorePassword).apply { - addOrReplaceKey( + val nodeKeyStore = loadNodeKeyStore(createNew = true) + nodeKeyStore.update { + setPrivateKey( X509Utilities.CORDA_CLIENT_CA, nodeCaKeyPair.private, - keyStorePassword.toCharArray(), - arrayOf(nodeCaCert, intermediateCa.certificate, rootCert)) - save(nodeKeystore, keyStorePassword) + listOf(nodeCaCert, intermediateCa.certificate, rootCert)) } + + val sslKeyStore = loadSslKeyStore(createNew = true) + sslKeyStore.update { + val tlsKeyPair = generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) + val tlsCert = X509Utilities.createCertificate(CertificateType.TLS, nodeCaCert, nodeCaKeyPair, legalName.x500Principal, tlsKeyPair.public) + setPrivateKey( + X509Utilities.CORDA_CLIENT_TLS, + tlsKeyPair.private, + listOf(tlsCert, nodeCaCert, intermediateCa.certificate, rootCert)) + } + + return Pair(nodeKeyStore, sslKeyStore) +} + +fun X509KeyStore.storeLegalIdentity(alias: String, keyPair: KeyPair = Crypto.generateKeyPair()): PartyAndCertificate { + val nodeCaCertPath = getCertificateChain(X509Utilities.CORDA_CLIENT_CA) + // Assume key password = store password. + val nodeCaCertAndKeyPair = getCertificateAndKeyPair(X509Utilities.CORDA_CLIENT_CA) + // Create new keys and store in keystore. + val identityCert = X509Utilities.createCertificate( + CertificateType.LEGAL_IDENTITY, + nodeCaCertAndKeyPair.certificate, + nodeCaCertAndKeyPair.keyPair, + nodeCaCertAndKeyPair.certificate.subjectX500Principal, + keyPair.public) + // TODO: X509Utilities.validateCertificateChain() + // Assume key password = store password. + val identityCertPath = listOf(identityCert) + nodeCaCertPath + setPrivateKey(alias, keyPair.private, identityCertPath) + save() + return PartyAndCertificate(X509CertificateFactory().generateCertPath(identityCertPath)) } fun createDevNetworkMapCa(rootCa: CertificateAndKeyPair = DEV_ROOT_CA): CertificateAndKeyPair { diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/config/SSLConfiguration.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/config/SSLConfiguration.kt index fb5424e19d..f44b190d5a 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/config/SSLConfiguration.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/config/SSLConfiguration.kt @@ -1,6 +1,7 @@ package net.corda.nodeapi.internal.config import net.corda.core.internal.div +import net.corda.nodeapi.internal.crypto.X509KeyStore import java.nio.file.Path interface SSLConfiguration { @@ -11,6 +12,18 @@ interface SSLConfiguration { // TODO This looks like it should be in NodeSSLConfiguration val nodeKeystore: Path get() = certificatesDirectory / "nodekeystore.jks" val trustStoreFile: Path get() = certificatesDirectory / "truststore.jks" + + fun loadTrustStore(createNew: Boolean = false): X509KeyStore { + return X509KeyStore.fromFile(trustStoreFile, trustStorePassword, createNew) + } + + fun loadNodeKeyStore(createNew: Boolean = false): X509KeyStore { + return X509KeyStore.fromFile(nodeKeystore, keyStorePassword, createNew) + } + + fun loadSslKeyStore(createNew: Boolean = false): X509KeyStore { + return X509KeyStore.fromFile(sslKeystore, keyStorePassword, createNew) + } } interface NodeSSLConfiguration : SSLConfiguration { diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/crypto/KeyStoreUtilities.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/crypto/KeyStoreUtilities.kt index d00f994d9c..981d16cf94 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/crypto/KeyStoreUtilities.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/crypto/KeyStoreUtilities.kt @@ -9,7 +9,6 @@ import net.corda.core.internal.read import net.corda.core.internal.write import java.io.IOException import java.io.InputStream -import java.io.OutputStream import java.nio.file.Path import java.security.* import java.security.cert.Certificate @@ -103,17 +102,11 @@ fun KeyStore.addOrReplaceCertificate(alias: String, cert: Certificate) { * @param storePassword password to access the store in future. This does not have to be the same password as any keys stored, * but for SSL purposes this is recommended. */ -fun KeyStore.save(keyStoreFilePath: Path, storePassword: String) = keyStoreFilePath.write { store(it, storePassword) } - -fun KeyStore.store(out: OutputStream, password: String) = store(out, password.toCharArray()) - -/** - * Extract public and private keys from a KeyStore file assuming storage alias is known. - * @param alias The name to lookup the Key and Certificate chain from. - * @param keyPassword Password to unlock the private key entries. - * @return The KeyPair found in the KeyStore under the specified alias. - */ -fun KeyStore.getKeyPair(alias: String, keyPassword: String): KeyPair = getCertificateAndKeyPair(alias, keyPassword).keyPair +fun KeyStore.save(keyStoreFilePath: Path, storePassword: String) { + keyStoreFilePath.write { + store(it, storePassword.toCharArray()) + } +} /** * Helper method to load a Certificate and KeyPair from their KeyStore. @@ -135,7 +128,7 @@ fun KeyStore.getCertificateAndKeyPair(alias: String, keyPassword: String): Certi */ fun KeyStore.getX509Certificate(alias: String): X509Certificate { val certificate = getCertificate(alias) ?: throw IllegalArgumentException("No certificate under alias \"$alias\".") - return certificate as? X509Certificate ?: throw IllegalArgumentException("Certificate under alias \"$alias\" is not an X.509 certificate.") + return certificate as? X509Certificate ?: throw IllegalStateException("Certificate under alias \"$alias\" is not an X.509 certificate: $certificate") } /** diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/crypto/KeyStoreWrapper.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/crypto/KeyStoreWrapper.kt deleted file mode 100644 index f7cae5b441..0000000000 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/crypto/KeyStoreWrapper.kt +++ /dev/null @@ -1,40 +0,0 @@ -package net.corda.nodeapi.internal.crypto - -import net.corda.core.identity.CordaX500Name -import net.corda.core.identity.PartyAndCertificate -import net.corda.core.internal.read -import java.nio.file.Path -import java.security.KeyPair -import java.security.cert.Certificate - -class KeyStoreWrapper(private val storePath: Path, private val storePassword: String) { - private val keyStore = storePath.read { loadKeyStore(it, storePassword) } - - // TODO This method seems misplaced in this class. - fun storeLegalIdentity(legalName: CordaX500Name, alias: String, keyPair: KeyPair): PartyAndCertificate { - val nodeCaCertChain = keyStore.getCertificateChain(X509Utilities.CORDA_CLIENT_CA) - val nodeCa = getCertificateAndKeyPair(X509Utilities.CORDA_CLIENT_CA) - val identityCert = X509Utilities.createCertificate( - CertificateType.LEGAL_IDENTITY, - nodeCa.certificate, - nodeCa.keyPair, - legalName.x500Principal, - keyPair.public) - val identityCertPath = X509CertificateFactory().generateCertPath(identityCert, *nodeCaCertChain) - // Assume key password = store password. - keyStore.addOrReplaceKey(alias, keyPair.private, storePassword.toCharArray(), identityCertPath.certificates.toTypedArray()) - keyStore.save(storePath, storePassword) - return PartyAndCertificate(identityCertPath) - } - - // Delegate methods to keystore. Sadly keystore doesn't have an interface. - fun containsAlias(alias: String) = keyStore.containsAlias(alias) - - fun getX509Certificate(alias: String) = keyStore.getX509Certificate(alias) - - fun getCertificateChain(alias: String): Array = keyStore.getCertificateChain(alias) - - fun getCertificate(alias: String): Certificate = keyStore.getCertificate(alias) - - fun getCertificateAndKeyPair(alias: String): CertificateAndKeyPair = keyStore.getCertificateAndKeyPair(alias, storePassword) -} \ No newline at end of file diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/crypto/X509KeyStore.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/crypto/X509KeyStore.kt new file mode 100644 index 0000000000..53eb020224 --- /dev/null +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/crypto/X509KeyStore.kt @@ -0,0 +1,74 @@ +package net.corda.nodeapi.internal.crypto + +import net.corda.core.crypto.Crypto +import net.corda.core.internal.uncheckedCast +import java.nio.file.Path +import java.security.KeyPair +import java.security.KeyStore +import java.security.PrivateKey +import java.security.cert.X509Certificate + +/** + * Wrapper around a [KeyStore] object but only dealing with [X509Certificate]s and with a better API. + */ +class X509KeyStore private constructor(val internal: KeyStore, private val storePassword: String, private val keyStoreFile: Path? = null) { + /** Wrap an existing [KeyStore]. [save] is not supported. */ + constructor(internal: KeyStore, storePassword: String) : this(internal, storePassword, null) + + companion object { + /** + * Read a [KeyStore] from the given file. If the key store doesn't exist and [createNew] is true then a blank + * key store will be written out. Changes to the returned [X509KeyStore] can be persisted with [save]. + */ + fun fromFile(keyStoreFile: Path, storePassword: String, createNew: Boolean = false): X509KeyStore { + val internal: KeyStore = if (createNew) loadOrCreateKeyStore(keyStoreFile, storePassword) else loadKeyStore(keyStoreFile, storePassword) + return X509KeyStore(internal, storePassword, keyStoreFile) + } + } + + operator fun contains(alias: String): Boolean = internal.containsAlias(alias) + + fun aliases(): Iterator = internal.aliases().iterator() + + fun getCertificate(alias: String): X509Certificate = internal.getX509Certificate(alias) + + fun getCertificateChain(alias: String): List { + val certArray = requireNotNull(internal.getCertificateChain(alias)) { "No certificate chain under the alias $alias" } + check(certArray.all { it is X509Certificate }) { "Certificate chain under alias $alias is not X.509" } + return uncheckedCast(certArray.asList()) + } + + fun getCertificateAndKeyPair(alias: String, keyPassword: String = storePassword): CertificateAndKeyPair { + val cert = getCertificate(alias) + val publicKey = Crypto.toSupportedPublicKey(cert.publicKey) + return CertificateAndKeyPair(cert, KeyPair(publicKey, getPrivateKey(alias, keyPassword))) + } + + fun getPrivateKey(alias: String, keyPassword: String = storePassword): PrivateKey { + return internal.getSupportedKey(alias, keyPassword) + } + + fun setPrivateKey(alias: String, key: PrivateKey, certificates: List, keyPassword: String = storePassword) { + checkWritableToFile() + internal.setKeyEntry(alias, key, keyPassword.toCharArray(), certificates.toTypedArray()) + } + + fun setCertificate(alias: String, certificate: X509Certificate) { + checkWritableToFile() + internal.setCertificateEntry(alias, certificate) + } + + fun save() { + internal.save(checkWritableToFile(), storePassword) + } + + fun update(action: X509KeyStore.() -> Unit) { + checkWritableToFile() + action(this) + save() + } + + private fun checkWritableToFile(): Path { + return keyStoreFile ?: throw IllegalStateException("This key store cannot be written to") + } +} diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/crypto/X509Utilities.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/crypto/X509Utilities.kt index fb2e512d38..959fc83a21 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/crypto/X509Utilities.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/crypto/X509Utilities.kt @@ -7,8 +7,6 @@ import net.corda.core.crypto.random63BitValue import net.corda.core.internal.CertRole import net.corda.core.internal.reader import net.corda.core.internal.writer -import net.corda.core.identity.CordaX500Name -import net.corda.core.internal.* import net.corda.core.utilities.days import net.corda.core.utilities.millis import org.bouncycastle.asn1.* @@ -95,6 +93,7 @@ object X509Utilities { return createCertificate(CertificateType.ROOT_CA, subject, keyPair, subject, keyPair.public, window) } + // TODO Provide an overload which takes in a List or a CertPath @Throws(CertPathValidatorException::class) fun validateCertificateChain(trustedRoot: X509Certificate, vararg certificates: Certificate) { require(certificates.isNotEmpty()) { "Certificate path must contain at least one certificate" } @@ -287,10 +286,12 @@ class X509CertificateFactory { return delegate.generateCertificate(input) as X509Certificate } + // TODO X509Certificate fun generateCertPath(certificates: List): CertPath { return delegate.generateCertPath(certificates) } + // TODO X509Certificate fun generateCertPath(vararg certificates: Certificate): CertPath { return delegate.generateCertPath(certificates.asList()) } diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/crypto/X509UtilitiesTest.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/crypto/X509UtilitiesTest.kt index 512e0a096d..2f12970874 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/crypto/X509UtilitiesTest.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/crypto/X509UtilitiesTest.kt @@ -222,7 +222,8 @@ class X509UtilitiesTest { val clientSocketFactory = context.socketFactory val serverSocket = serverSocketFactory.createServerSocket(0) as SSLServerSocket // use 0 to get first free socket - val serverParams = SSLParameters(CIPHER_SUITES, arrayOf("TLSv1.2")) + val serverParams = SSLParameters(CIPHER_SUITES, + arrayOf("TLSv1.2")) serverParams.wantClientAuth = true serverParams.needClientAuth = true serverParams.endpointIdentificationAlgorithm = null // Reconfirm default no server name indication, use our own validator. @@ -230,7 +231,8 @@ class X509UtilitiesTest { serverSocket.useClientMode = false val clientSocket = clientSocketFactory.createSocket() as SSLSocket - val clientParams = SSLParameters(CIPHER_SUITES, arrayOf("TLSv1.2")) + val clientParams = SSLParameters(CIPHER_SUITES, + arrayOf("TLSv1.2")) clientParams.endpointIdentificationAlgorithm = null // Reconfirm default no server name indication, use our own validator. clientSocket.sslParameters = clientParams clientSocket.useClientMode = true @@ -267,7 +269,7 @@ class X509UtilitiesTest { val peerChain = clientSocket.session.peerCertificates val peerX500Principal = (peerChain[0] as X509Certificate).subjectX500Principal assertEquals(MEGA_CORP.name.x500Principal, peerX500Principal) - X509Utilities.validateCertificateChain(trustStore.getX509Certificate(X509Utilities.CORDA_ROOT_CA), *peerChain) + X509Utilities.validateCertificateChain(rootCa.certificate, *peerChain) val output = DataOutputStream(clientSocket.outputStream) output.writeUTF("Hello World") var timeout = 0 diff --git a/node/src/integration-test/kotlin/net/corda/node/NodeKeystoreCheckTest.kt b/node/src/integration-test/kotlin/net/corda/node/NodeKeystoreCheckTest.kt index f36d543cc2..af572b8296 100644 --- a/node/src/integration-test/kotlin/net/corda/node/NodeKeystoreCheckTest.kt +++ b/node/src/integration-test/kotlin/net/corda/node/NodeKeystoreCheckTest.kt @@ -5,7 +5,8 @@ import net.corda.core.internal.div import net.corda.core.utilities.getOrThrow import net.corda.node.services.config.configureDevKeyAndTrustStores import net.corda.nodeapi.internal.config.SSLConfiguration -import net.corda.nodeapi.internal.crypto.* +import net.corda.nodeapi.internal.crypto.CertificateType +import net.corda.nodeapi.internal.crypto.X509Utilities import net.corda.testing.core.ALICE_NAME import net.corda.testing.driver.driver import org.assertj.core.api.Assertions.assertThatThrownBy @@ -45,15 +46,14 @@ class NodeKeystoreCheckTest { node.stop() // Fiddle with node keystore. - val keystore = loadKeyStore(config.nodeKeystore, config.keyStorePassword) - - // Self signed root - val badRootKeyPair = Crypto.generateKeyPair() - val badRoot = X509Utilities.createSelfSignedCACertificate(X500Principal("O=Bad Root,L=Lodnon,C=GB"), badRootKeyPair) - val nodeCA = keystore.getCertificateAndKeyPair(X509Utilities.CORDA_CLIENT_CA, config.keyStorePassword) - val badNodeCACert = X509Utilities.createCertificate(CertificateType.NODE_CA, badRoot, badRootKeyPair, ALICE_NAME.x500Principal, nodeCA.keyPair.public) - keystore.setKeyEntry(X509Utilities.CORDA_CLIENT_CA, nodeCA.keyPair.private, config.keyStorePassword.toCharArray(), arrayOf(badNodeCACert, badRoot)) - keystore.save(config.nodeKeystore, config.keyStorePassword) + config.loadNodeKeyStore().update { + // Self signed root + val badRootKeyPair = Crypto.generateKeyPair() + val badRoot = X509Utilities.createSelfSignedCACertificate(X500Principal("O=Bad Root,L=Lodnon,C=GB"), badRootKeyPair) + val nodeCA = getCertificateAndKeyPair(X509Utilities.CORDA_CLIENT_CA) + val badNodeCACert = X509Utilities.createCertificate(CertificateType.NODE_CA, badRoot, badRootKeyPair, ALICE_NAME.x500Principal, nodeCA.keyPair.public) + setPrivateKey(X509Utilities.CORDA_CLIENT_CA, nodeCA.keyPair.private, listOf(badNodeCACert, badRoot)) + } assertThatThrownBy { startNode(providedName = ALICE_NAME, customOverrides = mapOf("devMode" to false)).getOrThrow() diff --git a/node/src/integration-test/kotlin/net/corda/node/amqp/AMQPBridgeTest.kt b/node/src/integration-test/kotlin/net/corda/node/amqp/AMQPBridgeTest.kt index dea8798d4a..484c84687a 100644 --- a/node/src/integration-test/kotlin/net/corda/node/amqp/AMQPBridgeTest.kt +++ b/node/src/integration-test/kotlin/net/corda/node/amqp/AMQPBridgeTest.kt @@ -15,7 +15,6 @@ import net.corda.node.services.config.* import net.corda.node.services.messaging.ArtemisMessagingClient import net.corda.node.services.messaging.ArtemisMessagingServer import net.corda.nodeapi.internal.ArtemisMessagingComponent -import net.corda.nodeapi.internal.crypto.loadKeyStore import net.corda.testing.core.* import net.corda.testing.internal.rigorousMock import org.apache.activemq.artemis.api.core.Message.HDR_DUPLICATE_DETECTION_ID @@ -229,16 +228,14 @@ class AMQPBridgeTest { } serverConfig.configureWithDevSSLCertificate() - val serverTruststore = loadKeyStore(serverConfig.trustStoreFile, serverConfig.trustStorePassword) - val serverKeystore = loadKeyStore(serverConfig.sslKeystore, serverConfig.keyStorePassword) - val amqpServer = AMQPServer("0.0.0.0", + return AMQPServer("0.0.0.0", amqpPort, ArtemisMessagingComponent.PEER_USER, ArtemisMessagingComponent.PEER_USER, - serverKeystore, + serverConfig.loadSslKeyStore().internal, serverConfig.keyStorePassword, - serverTruststore, - trace = true) - return amqpServer + serverConfig.loadTrustStore().internal, + trace = true + ) } } \ No newline at end of file diff --git a/node/src/integration-test/kotlin/net/corda/node/amqp/ProtonWrapperTests.kt b/node/src/integration-test/kotlin/net/corda/node/amqp/ProtonWrapperTests.kt index 5d198758c8..13206aad4d 100644 --- a/node/src/integration-test/kotlin/net/corda/node/amqp/ProtonWrapperTests.kt +++ b/node/src/integration-test/kotlin/net/corda/node/amqp/ProtonWrapperTests.kt @@ -21,7 +21,6 @@ import net.corda.node.services.messaging.ArtemisMessagingClient import net.corda.node.services.messaging.ArtemisMessagingServer import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.P2P_PREFIX import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.PEER_USER -import net.corda.nodeapi.internal.crypto.loadKeyStore import net.corda.testing.core.* import net.corda.testing.internal.rigorousMock import org.apache.activemq.artemis.api.core.RoutingType @@ -253,8 +252,8 @@ class ProtonWrapperTests { } clientConfig.configureWithDevSSLCertificate() - val clientTruststore = loadKeyStore(clientConfig.trustStoreFile, clientConfig.trustStorePassword) - val clientKeystore = loadKeyStore(clientConfig.sslKeystore, clientConfig.keyStorePassword) + val clientTruststore = clientConfig.loadTrustStore().internal + val clientKeystore = clientConfig.loadSslKeyStore().internal return AMQPClient( listOf(NetworkHostAndPort("localhost", serverPort), NetworkHostAndPort("localhost", serverPort2), @@ -276,8 +275,8 @@ class ProtonWrapperTests { } clientConfig.configureWithDevSSLCertificate() - val clientTruststore = loadKeyStore(clientConfig.trustStoreFile, clientConfig.trustStorePassword) - val clientKeystore = loadKeyStore(clientConfig.sslKeystore, clientConfig.keyStorePassword) + val clientTruststore = clientConfig.loadTrustStore().internal + val clientKeystore = clientConfig.loadSslKeyStore().internal return AMQPClient( listOf(NetworkHostAndPort("localhost", serverPort)), setOf(ALICE_NAME), @@ -297,8 +296,8 @@ class ProtonWrapperTests { } serverConfig.configureWithDevSSLCertificate() - val serverTruststore = loadKeyStore(serverConfig.trustStoreFile, serverConfig.trustStorePassword) - val serverKeystore = loadKeyStore(serverConfig.sslKeystore, serverConfig.keyStorePassword) + val serverTruststore = serverConfig.loadTrustStore().internal + val serverKeystore = serverConfig.loadSslKeyStore().internal return AMQPServer( "0.0.0.0", port, diff --git a/node/src/integration-test/kotlin/net/corda/services/messaging/MQSecurityAsNodeTest.kt b/node/src/integration-test/kotlin/net/corda/services/messaging/MQSecurityAsNodeTest.kt index bc6d9128d3..d24b4b1811 100644 --- a/node/src/integration-test/kotlin/net/corda/services/messaging/MQSecurityAsNodeTest.kt +++ b/node/src/integration-test/kotlin/net/corda/services/messaging/MQSecurityAsNodeTest.kt @@ -12,7 +12,8 @@ import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.PEER_USER import net.corda.nodeapi.internal.DEV_INTERMEDIATE_CA import net.corda.nodeapi.internal.DEV_ROOT_CA import net.corda.nodeapi.internal.config.SSLConfiguration -import net.corda.nodeapi.internal.crypto.* +import net.corda.nodeapi.internal.crypto.CertificateType +import net.corda.nodeapi.internal.crypto.X509Utilities import org.apache.activemq.artemis.api.config.ActiveMQDefaultConfiguration import org.apache.activemq.artemis.api.core.ActiveMQClusterSecurityException import org.apache.activemq.artemis.api.core.ActiveMQNotConnectedException @@ -115,22 +116,19 @@ class MQSecurityAsNodeTest : P2PMQSecurityTest() { CordaX500Name("MiniCorp", "London", "GB").x500Principal, tlsKeyPair.public) - val keyPass = keyStorePassword.toCharArray() - val clientCAKeystore = loadOrCreateKeyStore(nodeKeystore, keyStorePassword) - clientCAKeystore.addOrReplaceKey( - X509Utilities.CORDA_CLIENT_CA, - clientKeyPair.private, - keyPass, - arrayOf(clientCACert, DEV_INTERMEDIATE_CA.certificate, DEV_ROOT_CA.certificate)) - clientCAKeystore.save(nodeKeystore, keyStorePassword) + loadNodeKeyStore(createNew = true).update { + setPrivateKey( + X509Utilities.CORDA_CLIENT_CA, + clientKeyPair.private, + listOf(clientCACert, DEV_INTERMEDIATE_CA.certificate, DEV_ROOT_CA.certificate)) + } - val tlsKeystore = loadOrCreateKeyStore(sslKeystore, keyStorePassword) - tlsKeystore.addOrReplaceKey( - X509Utilities.CORDA_CLIENT_TLS, - tlsKeyPair.private, - keyPass, - arrayOf(clientTLSCert, clientCACert, DEV_INTERMEDIATE_CA.certificate, DEV_ROOT_CA.certificate)) - tlsKeystore.save(sslKeystore, keyStorePassword) + loadSslKeyStore(createNew = true).update { + setPrivateKey( + X509Utilities.CORDA_CLIENT_TLS, + tlsKeyPair.private, + listOf(clientTLSCert, clientCACert, DEV_INTERMEDIATE_CA.certificate, DEV_ROOT_CA.certificate)) + } } } diff --git a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt index 5affcadaf8..df4a449772 100644 --- a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt +++ b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt @@ -36,7 +36,10 @@ import net.corda.node.services.ContractUpgradeHandler import net.corda.node.services.FinalityHandler import net.corda.node.services.NotaryChangeHandler import net.corda.node.services.api.* -import net.corda.node.services.config.* +import net.corda.node.services.config.BFTSMaRtConfiguration +import net.corda.node.services.config.NodeConfiguration +import net.corda.node.services.config.NotaryConfig +import net.corda.node.services.config.configureWithDevSSLCertificate import net.corda.node.services.events.NodeSchedulerService import net.corda.node.services.events.ScheduledActivityObserver import net.corda.node.services.identity.PersistentIdentityService @@ -55,16 +58,15 @@ import net.corda.node.shell.InteractiveShell import net.corda.node.utilities.AffinityExecutor import net.corda.nodeapi.internal.DevIdentityGenerator import net.corda.nodeapi.internal.SignedNodeInfo -import net.corda.nodeapi.internal.crypto.KeyStoreWrapper import net.corda.nodeapi.internal.crypto.X509CertificateFactory import net.corda.nodeapi.internal.crypto.X509Utilities -import net.corda.nodeapi.internal.crypto.loadKeyStore import net.corda.nodeapi.internal.network.NETWORK_PARAMS_FILE_NAME import net.corda.nodeapi.internal.network.NetworkParameters import net.corda.nodeapi.internal.network.verifiedNetworkMapCert import net.corda.nodeapi.internal.persistence.CordaPersistence import net.corda.nodeapi.internal.persistence.DatabaseConfig import net.corda.nodeapi.internal.persistence.HibernateConfiguration +import net.corda.nodeapi.internal.storeLegalIdentity import org.apache.activemq.artemis.utils.ReusableLatch import org.hibernate.type.descriptor.java.JavaTypeDescriptorRegistry import org.slf4j.Logger @@ -140,7 +142,7 @@ abstract class AbstractNode(val configuration: NodeConfiguration, private val _nodeReadyFuture = openFuture() protected var networkMapClient: NetworkMapClient? = null - lateinit var securityManager: RPCSecurityManager get + lateinit var securityManager: RPCSecurityManager /** Completes once the node has successfully registered with the network map service * or has loaded network map data from local database */ @@ -568,9 +570,9 @@ abstract class AbstractNode(val configuration: NodeConfiguration, private fun validateKeystore() { val containCorrectKeys = try { // This will throw IOException if key file not found or KeyStoreException if keystore password is incorrect. - val sslKeystore = loadKeyStore(configuration.sslKeystore, configuration.keyStorePassword) - val identitiesKeystore = loadKeyStore(configuration.nodeKeystore, configuration.keyStorePassword) - sslKeystore.containsAlias(X509Utilities.CORDA_CLIENT_TLS) && identitiesKeystore.containsAlias(X509Utilities.CORDA_CLIENT_CA) + val sslKeystore = configuration.loadSslKeyStore() + val identitiesKeystore = configuration.loadNodeKeyStore() + X509Utilities.CORDA_CLIENT_TLS in sslKeystore && X509Utilities.CORDA_CLIENT_CA in identitiesKeystore } catch (e: KeyStoreException) { log.warn("Certificate key store found but key store password does not match configuration.") false @@ -585,15 +587,12 @@ abstract class AbstractNode(val configuration: NodeConfiguration, } // Check all cert path chain to the trusted root - val sslKeystore = loadKeyStore(configuration.sslKeystore, configuration.keyStorePassword) - val identitiesKeystore = loadKeyStore(configuration.nodeKeystore, configuration.keyStorePassword) - val trustStore = loadKeyStore(configuration.trustStoreFile, configuration.trustStorePassword) - val sslRoot = sslKeystore.getCertificateChain(X509Utilities.CORDA_CLIENT_TLS).last() - val clientCARoot = identitiesKeystore.getCertificateChain(X509Utilities.CORDA_CLIENT_CA).last() - val trustRoot = trustStore.getCertificate(X509Utilities.CORDA_ROOT_CA) + val sslCertChainRoot = configuration.loadSslKeyStore().getCertificateChain(X509Utilities.CORDA_CLIENT_TLS).last() + val nodeCaCertChainRoot = configuration.loadNodeKeyStore().getCertificateChain(X509Utilities.CORDA_CLIENT_CA).last() + val trustRoot = configuration.loadTrustStore().getCertificate(X509Utilities.CORDA_ROOT_CA) - require(sslRoot == trustRoot) { "TLS certificate must chain to the trusted root." } - require(clientCARoot == trustRoot) { "Client CA certificate must chain to the trusted root." } + require(sslCertChainRoot == trustRoot) { "TLS certificate must chain to the trusted root." } + require(nodeCaCertChainRoot == trustRoot) { "Client CA certificate must chain to the trusted root." } } // Specific class so that MockNode can catch it. @@ -684,12 +683,9 @@ abstract class AbstractNode(val configuration: NodeConfiguration, } private fun makeIdentityService(identityCert: X509Certificate): PersistentIdentityService { - val trustStore = KeyStoreWrapper(configuration.trustStoreFile, configuration.trustStorePassword) - val caKeyStore = KeyStoreWrapper(configuration.nodeKeystore, configuration.keyStorePassword) - val trustRoot = trustStore.getX509Certificate(X509Utilities.CORDA_ROOT_CA) - val clientCa = caKeyStore.getCertificateAndKeyPair(X509Utilities.CORDA_CLIENT_CA) - val caCertificates = arrayOf(identityCert, clientCa.certificate) - return PersistentIdentityService(trustRoot, *caCertificates) + val trustRoot = configuration.loadTrustStore().getCertificate(X509Utilities.CORDA_ROOT_CA) + val nodeCa = configuration.loadNodeKeyStore().getCertificate(X509Utilities.CORDA_CLIENT_CA) + return PersistentIdentityService(trustRoot, identityCert, nodeCa) } protected abstract fun makeTransactionVerifierService(): TransactionVerifierService @@ -713,7 +709,7 @@ abstract class AbstractNode(val configuration: NodeConfiguration, protected abstract fun startMessagingService(rpcOps: RPCOps) private fun obtainIdentity(notaryConfig: NotaryConfig?): Pair { - val keyStore = KeyStoreWrapper(configuration.nodeKeystore, configuration.keyStorePassword) + val keyStore = configuration.loadNodeKeyStore() val (id, singleName) = if (notaryConfig == null || !notaryConfig.isClusterConfig) { // Node's main identity or if it's a single node notary @@ -725,19 +721,20 @@ abstract class AbstractNode(val configuration: NodeConfiguration, // TODO: Integrate with Key management service? val privateKeyAlias = "$id-private-key" - if (!keyStore.containsAlias(privateKeyAlias)) { + if (privateKeyAlias !in keyStore) { singleName ?: throw IllegalArgumentException( - "Unable to find in the key store the identity of the distributed notary ($id) the node is part of") - // TODO: Remove use of [IdentityGenerator.generateToDisk]. + "Unable to find in the key store the identity of the distributed notary the node is part of") log.info("$privateKeyAlias not found in key store ${configuration.nodeKeystore}, generating fresh key!") - keyStore.storeLegalIdentity(singleName, privateKeyAlias, generateKeyPair()) + // TODO This check shouldn't be needed + check(singleName == configuration.myLegalName) + keyStore.storeLegalIdentity(privateKeyAlias, generateKeyPair()) } val (x509Cert, keyPair) = keyStore.getCertificateAndKeyPair(privateKeyAlias) // TODO: Use configuration to indicate composite key should be used instead of public key for the identity. val compositeKeyAlias = "$id-composite-key" - val certificates = if (keyStore.containsAlias(compositeKeyAlias)) { + val certificates = if (compositeKeyAlias in keyStore) { // Use composite key instead if it exists val certificate = keyStore.getCertificate(compositeKeyAlias) // We have to create the certificate chain for the composite key manually, this is because we don't have a keystore @@ -747,12 +744,11 @@ abstract class AbstractNode(val configuration: NodeConfiguration, } else { keyStore.getCertificateChain(privateKeyAlias).let { check(it[0] == x509Cert) { "Certificates from key store do not line up!" } - it.asList() + it } } - val nodeCert = certificates[0] as? X509Certificate ?: throw ConfigurationException("Node certificate must be an X.509 certificate") - val subject = CordaX500Name.build(nodeCert.subjectX500Principal) + val subject = CordaX500Name.build(certificates[0].subjectX500Principal) // TODO Include the name of the distributed notary, which the node is part of, in the notary config so that we // can cross-check the identity we get from the key store if (singleName != null && subject != singleName) { diff --git a/node/src/main/kotlin/net/corda/node/services/config/ConfigUtilities.kt b/node/src/main/kotlin/net/corda/node/services/config/ConfigUtilities.kt index be5428675a..0b2ba8360b 100644 --- a/node/src/main/kotlin/net/corda/node/services/config/ConfigUtilities.kt +++ b/node/src/main/kotlin/net/corda/node/services/config/ConfigUtilities.kt @@ -10,7 +10,9 @@ import net.corda.core.internal.div import net.corda.core.internal.exists import net.corda.nodeapi.internal.config.SSLConfiguration import net.corda.nodeapi.internal.createDevKeyStores -import net.corda.nodeapi.internal.crypto.* +import net.corda.nodeapi.internal.crypto.X509KeyStore +import net.corda.nodeapi.internal.crypto.loadKeyStore +import net.corda.nodeapi.internal.crypto.save import org.slf4j.LoggerFactory import java.nio.file.Path @@ -52,22 +54,21 @@ fun SSLConfiguration.configureDevKeyAndTrustStores(myLegalName: CordaX500Name) { loadKeyStore(javaClass.classLoader.getResourceAsStream("certificates/cordatruststore.jks"), "trustpass").save(trustStoreFile, trustStorePassword) } if (!sslKeystore.exists() || !nodeKeystore.exists()) { - createDevKeyStores(myLegalName) + val (nodeKeyStore) = createDevKeyStores(myLegalName) // Move distributed service composite key (generated by IdentityGenerator.generateToDisk) to keystore if exists. val distributedServiceKeystore = certificatesDirectory / "distributedService.jks" if (distributedServiceKeystore.exists()) { - val serviceKeystore = loadKeyStore(distributedServiceKeystore, "cordacadevpass") - val cordaNodeKeystore = loadKeyStore(nodeKeystore, keyStorePassword) - - serviceKeystore.aliases().iterator().forEach { - if (serviceKeystore.isKeyEntry(it)) { - cordaNodeKeystore.setKeyEntry(it, serviceKeystore.getKey(it, "cordacadevkeypass".toCharArray()), keyStorePassword.toCharArray(), serviceKeystore.getCertificateChain(it)) - } else { - cordaNodeKeystore.setCertificateEntry(it, serviceKeystore.getCertificate(it)) + val serviceKeystore = X509KeyStore.fromFile(distributedServiceKeystore, "cordacadevpass") + nodeKeyStore.update { + serviceKeystore.aliases().forEach { + if (serviceKeystore.internal.isKeyEntry(it)) { + setPrivateKey(it, serviceKeystore.getPrivateKey(it, "cordacadevkeypass"), serviceKeystore.getCertificateChain(it)) + } else { + setCertificate(it, serviceKeystore.getCertificate(it)) + } } } - cordaNodeKeystore.save(nodeKeystore, keyStorePassword) } } } diff --git a/node/src/main/kotlin/net/corda/node/services/messaging/AMQPBridgeManager.kt b/node/src/main/kotlin/net/corda/node/services/messaging/AMQPBridgeManager.kt index 93b1137a00..1a0417d1ba 100644 --- a/node/src/main/kotlin/net/corda/node/services/messaging/AMQPBridgeManager.kt +++ b/node/src/main/kotlin/net/corda/node/services/messaging/AMQPBridgeManager.kt @@ -14,7 +14,6 @@ import net.corda.nodeapi.internal.ArtemisMessagingComponent import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.NODE_USER import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.PEER_USER import net.corda.nodeapi.internal.ArtemisMessagingComponent.RemoteInboxAddress.Companion.translateLocalQueueToInboxAddress -import net.corda.nodeapi.internal.crypto.loadKeyStore import org.apache.activemq.artemis.api.core.SimpleString import org.apache.activemq.artemis.api.core.client.ActiveMQClient.DEFAULT_ACK_BATCH_SIZE import org.apache.activemq.artemis.api.core.client.ClientConsumer @@ -38,9 +37,9 @@ internal class AMQPBridgeManager(val config: NodeConfiguration, val p2pAddress: private val lock = ReentrantLock() private val bridgeNameToBridgeMap = mutableMapOf() private var sharedEventLoopGroup: EventLoopGroup? = null - private val keyStore = loadKeyStore(config.sslKeystore, config.keyStorePassword) + private val keyStore = config.loadSslKeyStore().internal private val keyStorePrivateKeyPassword: String = config.keyStorePassword - private val trustStore = loadKeyStore(config.trustStoreFile, config.trustStorePassword) + private val trustStore = config.loadTrustStore().internal private var artemis: ArtemisMessagingClient? = null companion object { diff --git a/node/src/main/kotlin/net/corda/node/services/messaging/ArtemisMessagingServer.kt b/node/src/main/kotlin/net/corda/node/services/messaging/ArtemisMessagingServer.kt index b568685c14..cb6609d2ee 100644 --- a/node/src/main/kotlin/net/corda/node/services/messaging/ArtemisMessagingServer.kt +++ b/node/src/main/kotlin/net/corda/node/services/messaging/ArtemisMessagingServer.kt @@ -34,6 +34,8 @@ import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.P2P_PREFIX import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.PEERS_PREFIX import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.PEER_USER import net.corda.nodeapi.internal.ArtemisMessagingComponent.NodeAddress +import net.corda.nodeapi.internal.crypto.X509Utilities.CORDA_CLIENT_TLS +import net.corda.nodeapi.internal.crypto.X509Utilities.CORDA_ROOT_CA import net.corda.nodeapi.internal.crypto.loadKeyStore import net.corda.nodeapi.internal.requireOnDefaultFileSystem import org.apache.activemq.artemis.api.core.SimpleString @@ -205,8 +207,8 @@ class ArtemisMessagingServer(private val config: NodeConfiguration, @Throws(IOException::class, KeyStoreException::class) private fun createArtemisSecurityManager(loginListener: LoginListener): ActiveMQJAASSecurityManager { - val keyStore = loadKeyStore(config.sslKeystore, config.keyStorePassword) - val trustStore = loadKeyStore(config.trustStoreFile, config.trustStorePassword) + val keyStore = config.loadSslKeyStore().internal + val trustStore = config.loadTrustStore().internal val defaultCertPolicies = mapOf( PEER_ROLE to CertificateChainCheckPolicy.RootMustMatch, diff --git a/node/src/main/kotlin/net/corda/node/services/messaging/CoreBridgeManager.kt b/node/src/main/kotlin/net/corda/node/services/messaging/CoreBridgeManager.kt index b821d13cff..74e4a711c6 100644 --- a/node/src/main/kotlin/net/corda/node/services/messaging/CoreBridgeManager.kt +++ b/node/src/main/kotlin/net/corda/node/services/messaging/CoreBridgeManager.kt @@ -23,6 +23,7 @@ import org.apache.activemq.artemis.core.server.ActiveMQServer import org.apache.activemq.artemis.core.server.cluster.Transformer import org.apache.activemq.artemis.spi.core.remoting.* import org.apache.activemq.artemis.utils.ConfigurationHelper +import java.security.cert.X509Certificate import java.time.Duration import java.util.concurrent.Executor import java.util.concurrent.ScheduledExecutorService @@ -161,7 +162,7 @@ class VerifyingNettyConnectorFactory : NettyConnectorFactory() { "Peer has wrong subject name in the certificate - expected $expectedLegalNames but got $peerCertificateName. This is either a fatal " + "misconfiguration by the remote peer or an SSL man-in-the-middle attack!" } - X509Utilities.validateCertificateChain(session.localCertificates.last() as java.security.cert.X509Certificate, *session.peerCertificates) + X509Utilities.validateCertificateChain(session.localCertificates.last() as X509Certificate, *session.peerCertificates) } catch (e: IllegalArgumentException) { connection.close() log.error(e.message) diff --git a/node/src/main/kotlin/net/corda/node/services/messaging/RPCMessagingClient.kt b/node/src/main/kotlin/net/corda/node/services/messaging/RPCMessagingClient.kt index 3c5f4127a1..874c030641 100644 --- a/node/src/main/kotlin/net/corda/node/services/messaging/RPCMessagingClient.kt +++ b/node/src/main/kotlin/net/corda/node/services/messaging/RPCMessagingClient.kt @@ -8,8 +8,6 @@ import net.corda.node.internal.security.RPCSecurityManager import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.NODE_USER import net.corda.nodeapi.internal.config.SSLConfiguration import net.corda.nodeapi.internal.crypto.X509Utilities -import net.corda.nodeapi.internal.crypto.getX509Certificate -import net.corda.nodeapi.internal.crypto.loadKeyStore import org.apache.activemq.artemis.api.core.management.ActiveMQServerControl class RPCMessagingClient(private val config: SSLConfiguration, serverAddress: NetworkHostAndPort, maxMessageSize: Int) : SingletonSerializeAsToken(), AutoCloseable { @@ -18,7 +16,7 @@ class RPCMessagingClient(private val config: SSLConfiguration, serverAddress: Ne fun start(rpcOps: RPCOps, securityManager: RPCSecurityManager) = synchronized(this) { val locator = artemis.start().sessionFactory.serverLocator - val myCert = loadKeyStore(config.sslKeystore, config.keyStorePassword).getX509Certificate(X509Utilities.CORDA_CLIENT_TLS) + val myCert = config.loadSslKeyStore().getCertificate(X509Utilities.CORDA_CLIENT_TLS) rpcServer = RPCServer(rpcOps, NODE_USER, NODE_USER, locator, securityManager, CordaX500Name.build(myCert.subjectX500Principal)) } diff --git a/node/src/main/kotlin/net/corda/node/utilities/registration/HTTPNetworkRegistrationService.kt b/node/src/main/kotlin/net/corda/node/utilities/registration/HTTPNetworkRegistrationService.kt index 49dde51b6d..201cab875d 100644 --- a/node/src/main/kotlin/net/corda/node/utilities/registration/HTTPNetworkRegistrationService.kt +++ b/node/src/main/kotlin/net/corda/node/utilities/registration/HTTPNetworkRegistrationService.kt @@ -9,7 +9,7 @@ import java.io.IOException import java.net.HttpURLConnection import java.net.HttpURLConnection.* import java.net.URL -import java.security.cert.Certificate +import java.security.cert.X509Certificate import java.util.* import java.util.zip.ZipInputStream @@ -22,19 +22,19 @@ class HTTPNetworkRegistrationService(compatibilityZoneURL: URL) : NetworkRegistr } @Throws(CertificateRequestException::class) - override fun retrieveCertificates(requestId: String): Array? { + override fun retrieveCertificates(requestId: String): List? { // Poll server to download the signed certificate once request has been approved. val conn = URL("$registrationURL/$requestId").openHttpConnection() conn.requestMethod = "GET" return when (conn.responseCode) { HTTP_OK -> ZipInputStream(conn.inputStream).use { - val certificates = ArrayList() + val certificates = ArrayList() val factory = X509CertificateFactory() while (it.nextEntry != null) { certificates += factory.generateCertificate(it) } - certificates.toTypedArray() + certificates } HTTP_NO_CONTENT -> null HTTP_UNAUTHORIZED -> throw CertificateRequestException("Certificate signing request has been rejected: ${conn.errorMessage}") diff --git a/node/src/main/kotlin/net/corda/node/utilities/registration/NetworkRegistrationHelper.kt b/node/src/main/kotlin/net/corda/node/utilities/registration/NetworkRegistrationHelper.kt index 090eef335c..ac49461b05 100644 --- a/node/src/main/kotlin/net/corda/node/utilities/registration/NetworkRegistrationHelper.kt +++ b/node/src/main/kotlin/net/corda/node/utilities/registration/NetworkRegistrationHelper.kt @@ -5,7 +5,8 @@ import net.corda.core.identity.CordaX500Name import net.corda.core.internal.* import net.corda.core.utilities.seconds import net.corda.node.services.config.NodeConfiguration -import net.corda.nodeapi.internal.crypto.* +import net.corda.nodeapi.internal.crypto.CertificateType +import net.corda.nodeapi.internal.crypto.X509Utilities import net.corda.nodeapi.internal.crypto.X509Utilities.CORDA_CLIENT_CA import net.corda.nodeapi.internal.crypto.X509Utilities.CORDA_CLIENT_TLS import net.corda.nodeapi.internal.crypto.X509Utilities.CORDA_ROOT_CA @@ -14,7 +15,6 @@ import org.bouncycastle.util.io.pem.PemObject import java.io.StringWriter import java.security.KeyPair import java.security.KeyStore -import java.security.cert.Certificate import java.security.cert.X509Certificate /** @@ -28,10 +28,8 @@ class NetworkRegistrationHelper(private val config: NodeConfiguration, private v } private val requestIdStore = config.certificatesDirectory / "certificate-request-id.txt" - private val keystorePassword = config.keyStorePassword // TODO: Use different password for private key. private val privateKeyPassword = config.keyStorePassword - private val trustStore: KeyStore private val rootCert: X509Certificate init { @@ -39,8 +37,7 @@ class NetworkRegistrationHelper(private val config: NodeConfiguration, private v "${config.trustStoreFile} does not exist. This file must contain the root CA cert of your compatibility zone. " + "Please contact your CZ operator." } - trustStore = loadKeyStore(config.trustStoreFile, config.trustStorePassword) - val rootCert = trustStore.getCertificate(CORDA_ROOT_CA) + val rootCert = config.loadTrustStore().internal.getCertificate(CORDA_ROOT_CA) require(rootCert != null) { "${config.trustStoreFile} does not contain a certificate with the key $CORDA_ROOT_CA." + "This file must contain the root CA cert of your compatibility zone. " + @@ -62,24 +59,23 @@ class NetworkRegistrationHelper(private val config: NodeConfiguration, private v */ fun buildKeystore() { config.certificatesDirectory.createDirectories() - val nodeKeyStore = loadOrCreateKeyStore(config.nodeKeystore, keystorePassword) - if (nodeKeyStore.containsAlias(CORDA_CLIENT_CA)) { + val nodeKeyStore = config.loadNodeKeyStore(createNew = true) + if (CORDA_CLIENT_CA in nodeKeyStore) { println("Certificate already exists, Corda node will now terminate...") return } // Create or load self signed keypair from the key store. // We use the self sign certificate to store the key temporarily in the keystore while waiting for the request approval. - if (!nodeKeyStore.containsAlias(SELF_SIGNED_PRIVATE_KEY)) { + if (SELF_SIGNED_PRIVATE_KEY !in nodeKeyStore) { val keyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) val selfSignCert = X509Utilities.createSelfSignedCACertificate(config.myLegalName.x500Principal, keyPair) // Save to the key store. - nodeKeyStore.addOrReplaceKey(SELF_SIGNED_PRIVATE_KEY, keyPair.private, privateKeyPassword.toCharArray(), - arrayOf(selfSignCert)) - nodeKeyStore.save(config.nodeKeystore, keystorePassword) + nodeKeyStore.setPrivateKey(SELF_SIGNED_PRIVATE_KEY, keyPair.private, listOf(selfSignCert), keyPassword = privateKeyPassword) + nodeKeyStore.save() } - val keyPair = nodeKeyStore.getKeyPair(SELF_SIGNED_PRIVATE_KEY, privateKeyPassword) + val keyPair = nodeKeyStore.getCertificateAndKeyPair(SELF_SIGNED_PRIVATE_KEY, privateKeyPassword).keyPair val requestId = submitOrResumeCertificateSigningRequest(keyPair) val certificates = try { @@ -92,7 +88,7 @@ class NetworkRegistrationHelper(private val config: NodeConfiguration, private v throw certificateRequestException } - val nodeCaCert = certificates[0] as X509Certificate + val nodeCaCert = certificates[0] val nodeCaSubject = try { CordaX500Name.build(nodeCaCert.subjectX500Principal) @@ -113,26 +109,26 @@ class NetworkRegistrationHelper(private val config: NodeConfiguration, private v } println("Checking root of the certificate path is what we expect.") - X509Utilities.validateCertificateChain(rootCert, *certificates) + X509Utilities.validateCertificateChain(rootCert, *certificates.toTypedArray()) println("Certificate signing request approved, storing private key with the certificate chain.") // Save private key and certificate chain to the key store. - nodeKeyStore.addOrReplaceKey(CORDA_CLIENT_CA, keyPair.private, privateKeyPassword.toCharArray(), certificates) - nodeKeyStore.deleteEntry(SELF_SIGNED_PRIVATE_KEY) - nodeKeyStore.save(config.nodeKeystore, keystorePassword) + nodeKeyStore.setPrivateKey(CORDA_CLIENT_CA, keyPair.private, certificates, keyPassword = privateKeyPassword) + nodeKeyStore.internal.deleteEntry(SELF_SIGNED_PRIVATE_KEY) + nodeKeyStore.save() println("Node private key and certificate stored in ${config.nodeKeystore}.") - println("Generating SSL certificate for node messaging service.") - val sslKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) - val sslCert = X509Utilities.createCertificate( - CertificateType.TLS, - nodeCaCert, - keyPair, - config.myLegalName.x500Principal, - sslKeyPair.public) - val sslKeyStore = loadOrCreateKeyStore(config.sslKeystore, keystorePassword) - sslKeyStore.addOrReplaceKey(CORDA_CLIENT_TLS, sslKeyPair.private, privateKeyPassword.toCharArray(), arrayOf(sslCert, *certificates)) - sslKeyStore.save(config.sslKeystore, config.keyStorePassword) + config.loadSslKeyStore(createNew = true).update { + println("Generating SSL certificate for node messaging service.") + val sslKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) + val sslCert = X509Utilities.createCertificate( + CertificateType.TLS, + nodeCaCert, + keyPair, + config.myLegalName.x500Principal, + sslKeyPair.public) + setPrivateKey(CORDA_CLIENT_TLS, sslKeyPair.private, listOf(sslCert) + certificates) + } println("SSL private key and certificate stored in ${config.sslKeystore}.") // All done, clean up temp files. @@ -145,7 +141,7 @@ class NetworkRegistrationHelper(private val config: NodeConfiguration, private v * @param requestId Certificate signing request ID. * @return Map of certificate chain. */ - private fun pollServerForCertificates(requestId: String): Array { + private fun pollServerForCertificates(requestId: String): List { println("Start polling server for certificate signing approval.") // Poll server to download the signed certificate once request has been approved. var certificates = certService.retrieveCertificates(requestId) diff --git a/node/src/main/kotlin/net/corda/node/utilities/registration/NetworkRegistrationService.kt b/node/src/main/kotlin/net/corda/node/utilities/registration/NetworkRegistrationService.kt index ffe8bece0f..beea4635d5 100644 --- a/node/src/main/kotlin/net/corda/node/utilities/registration/NetworkRegistrationService.kt +++ b/node/src/main/kotlin/net/corda/node/utilities/registration/NetworkRegistrationService.kt @@ -3,7 +3,7 @@ package net.corda.node.utilities.registration import net.corda.core.CordaException import net.corda.core.serialization.CordaSerializable import org.bouncycastle.pkcs.PKCS10CertificationRequest -import java.security.cert.Certificate +import java.security.cert.X509Certificate interface NetworkRegistrationService { /** Submits a CSR to the signing service and returns an opaque request ID. */ @@ -11,7 +11,7 @@ interface NetworkRegistrationService { /** Poll Certificate Signing Server for the request and returns a chain of certificates if request has been approved, null otherwise. */ @Throws(CertificateRequestException::class) - fun retrieveCertificates(requestId: String): Array? + fun retrieveCertificates(requestId: String): List? } @CordaSerializable diff --git a/node/src/test/kotlin/net/corda/node/utilities/registration/NetworkRegistrationHelperTest.kt b/node/src/test/kotlin/net/corda/node/utilities/registration/NetworkRegistrationHelperTest.kt index 4ce5df1eb4..b2788fcd25 100644 --- a/node/src/test/kotlin/net/corda/node/utilities/registration/NetworkRegistrationHelperTest.kt +++ b/node/src/test/kotlin/net/corda/node/utilities/registration/NetworkRegistrationHelperTest.kt @@ -12,7 +12,8 @@ import net.corda.core.identity.CordaX500Name import net.corda.core.internal.createDirectories import net.corda.core.internal.x500Name import net.corda.node.services.config.NodeConfiguration -import net.corda.nodeapi.internal.crypto.* +import net.corda.nodeapi.internal.crypto.CertificateType +import net.corda.nodeapi.internal.crypto.X509Utilities import net.corda.testing.core.ALICE_NAME import net.corda.testing.internal.createDevIntermediateCaCertPath import net.corda.testing.internal.rigorousMock @@ -27,7 +28,6 @@ import java.security.cert.CertPathValidatorException import java.security.cert.X509Certificate import javax.security.auth.x500.X500Principal import kotlin.test.assertFalse -import kotlin.test.assertTrue class NetworkRegistrationHelperTest { private val fs = Jimfs.newFileSystem(unix()) @@ -65,34 +65,31 @@ class NetworkRegistrationHelperTest { saveTrustStoreWithRootCa(nodeCaCertPath.last()) createRegistrationHelper(nodeCaCertPath).buildKeystore() - val nodeKeystore = loadKeyStore(config.nodeKeystore, config.keyStorePassword) - val sslKeystore = loadKeyStore(config.sslKeystore, config.keyStorePassword) - val trustStore = loadKeyStore(config.trustStoreFile, config.trustStorePassword) + val nodeKeystore = config.loadNodeKeyStore() + val sslKeystore = config.loadSslKeyStore() + val trustStore = config.loadTrustStore() nodeKeystore.run { - assertTrue(containsAlias(X509Utilities.CORDA_CLIENT_CA)) - assertFalse(containsAlias(X509Utilities.CORDA_INTERMEDIATE_CA)) - assertFalse(containsAlias(X509Utilities.CORDA_ROOT_CA)) - assertFalse(containsAlias(X509Utilities.CORDA_CLIENT_TLS)) - assertThat(getCertificateChain(X509Utilities.CORDA_CLIENT_CA)).containsExactly(*nodeCaCertPath) + assertFalse(contains(X509Utilities.CORDA_INTERMEDIATE_CA)) + assertFalse(contains(X509Utilities.CORDA_ROOT_CA)) + assertFalse(contains(X509Utilities.CORDA_CLIENT_TLS)) + assertThat(getCertificateChain(X509Utilities.CORDA_CLIENT_CA)).containsExactlyElementsOf(nodeCaCertPath) } sslKeystore.run { - assertFalse(containsAlias(X509Utilities.CORDA_CLIENT_CA)) - assertFalse(containsAlias(X509Utilities.CORDA_INTERMEDIATE_CA)) - assertFalse(containsAlias(X509Utilities.CORDA_ROOT_CA)) - assertTrue(containsAlias(X509Utilities.CORDA_CLIENT_TLS)) + assertFalse(contains(X509Utilities.CORDA_CLIENT_CA)) + assertFalse(contains(X509Utilities.CORDA_INTERMEDIATE_CA)) + assertFalse(contains(X509Utilities.CORDA_ROOT_CA)) val nodeTlsCertChain = getCertificateChain(X509Utilities.CORDA_CLIENT_TLS) assertThat(nodeTlsCertChain).hasSize(4) // The TLS cert has the same subject as the node CA cert - assertThat(CordaX500Name.build((nodeTlsCertChain[0] as X509Certificate).subjectX500Principal)).isEqualTo(nodeLegalName) - assertThat(nodeTlsCertChain.drop(1)).containsExactly(*nodeCaCertPath) + assertThat(CordaX500Name.build(nodeTlsCertChain[0].subjectX500Principal)).isEqualTo(nodeLegalName) + assertThat(nodeTlsCertChain.drop(1)).containsExactlyElementsOf(nodeCaCertPath) } trustStore.run { - assertFalse(containsAlias(X509Utilities.CORDA_CLIENT_CA)) - assertFalse(containsAlias(X509Utilities.CORDA_INTERMEDIATE_CA)) - assertTrue(containsAlias(X509Utilities.CORDA_ROOT_CA)) + assertFalse(contains(X509Utilities.CORDA_CLIENT_CA)) + assertFalse(contains(X509Utilities.CORDA_INTERMEDIATE_CA)) assertThat(getCertificate(X509Utilities.CORDA_ROOT_CA)).isEqualTo(nodeCaCertPath.last()) } } @@ -139,7 +136,7 @@ class NetworkRegistrationHelperTest { } private fun createNodeCaCertPath(type: CertificateType = CertificateType.NODE_CA, - legalName: CordaX500Name = nodeLegalName): Array { + legalName: CordaX500Name = nodeLegalName): List { val (rootCa, intermediateCa) = createDevIntermediateCaCertPath() val keyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) val nameConstraints = NameConstraints(arrayOf(GeneralSubtree(GeneralName(GeneralName.directoryName, legalName.x500Name))), arrayOf()) @@ -150,10 +147,10 @@ class NetworkRegistrationHelperTest { legalName.x500Principal, keyPair.public, nameConstraints = nameConstraints) - return arrayOf(nodeCaCert, intermediateCa.certificate, rootCa.certificate) + return listOf(nodeCaCert, intermediateCa.certificate, rootCa.certificate) } - private fun createRegistrationHelper(response: Array): NetworkRegistrationHelper { + private fun createRegistrationHelper(response: List): NetworkRegistrationHelper { val certService = rigorousMock().also { doReturn(requestId).whenever(it).submitRequest(any()) doReturn(response).whenever(it).retrieveCertificates(eq(requestId)) @@ -163,9 +160,8 @@ class NetworkRegistrationHelperTest { private fun saveTrustStoreWithRootCa(rootCert: X509Certificate) { config.certificatesDirectory.createDirectories() - loadOrCreateKeyStore(config.trustStoreFile, config.trustStorePassword).also { - it.addOrReplaceCertificate(X509Utilities.CORDA_ROOT_CA, rootCert) - it.save(config.trustStoreFile, config.trustStorePassword) + config.loadTrustStore(createNew = true).update { + setCertificate(X509Utilities.CORDA_ROOT_CA, rootCert) } } } diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/DriverDSLImpl.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/DriverDSLImpl.kt index bfb34c1f76..a2d00fa9b1 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/DriverDSLImpl.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/DriverDSLImpl.kt @@ -34,16 +34,14 @@ import net.corda.nodeapi.internal.addShutdownHook import net.corda.nodeapi.internal.config.parseAs import net.corda.nodeapi.internal.config.toConfig import net.corda.nodeapi.internal.crypto.X509Utilities -import net.corda.nodeapi.internal.crypto.addOrReplaceCertificate -import net.corda.nodeapi.internal.crypto.loadOrCreateKeyStore -import net.corda.nodeapi.internal.crypto.save import net.corda.nodeapi.internal.network.NetworkParametersCopier import net.corda.nodeapi.internal.network.NodeInfoFilesCopier import net.corda.nodeapi.internal.network.NotaryInfo +import net.corda.testing.common.internal.testNetworkParameters import net.corda.testing.core.ALICE_NAME import net.corda.testing.core.BOB_NAME import net.corda.testing.core.DUMMY_BANK_A_NAME -import net.corda.testing.common.internal.testNetworkParameters +import net.corda.testing.core.setGlobalSerialization import net.corda.testing.driver.* import net.corda.testing.node.ClusterSpec import net.corda.testing.node.MockServices.Companion.MOCK_VERSION_INFO @@ -51,7 +49,6 @@ import net.corda.testing.node.NotarySpec import net.corda.testing.node.User import net.corda.testing.node.internal.DriverDSLImpl.ClusterType.NON_VALIDATING_RAFT import net.corda.testing.node.internal.DriverDSLImpl.ClusterType.VALIDATING_RAFT -import net.corda.testing.core.setGlobalSerialization import okhttp3.OkHttpClient import okhttp3.Request import rx.Observable @@ -239,9 +236,8 @@ class DriverDSLImpl( )) config.corda.certificatesDirectory.createDirectories() - loadOrCreateKeyStore(config.corda.trustStoreFile, config.corda.trustStorePassword).apply { - addOrReplaceCertificate(X509Utilities.CORDA_ROOT_CA, rootCert) - save(config.corda.trustStoreFile, config.corda.trustStorePassword) + config.corda.loadTrustStore(createNew = true).update { + setCertificate(X509Utilities.CORDA_ROOT_CA, rootCert) } return if (startNodesInProcess) { From 3c0e006456baa8428b6a5e8611263e12f2f54754 Mon Sep 17 00:00:00 2001 From: Michele Sollecito Date: Wed, 24 Jan 2018 14:42:07 +0000 Subject: [PATCH 24/36] Reverted incorrect additional advertised RPC address in NodeInfo (#2417) --- .../net/corda/node/amqp/AMQPBridgeTest.kt | 7 ++-- .../net/corda/node/amqp/ProtonWrapperTests.kt | 4 +-- .../kotlin/net/corda/node/internal/Node.kt | 14 ++------ .../services/messaging/AMQPBridgeManager.kt | 2 +- .../messaging/ArtemisMessagingServer.kt | 33 +++++-------------- .../services/messaging/CoreBridgeManager.kt | 2 +- .../services/messaging/P2PMessagingClient.kt | 2 +- .../messaging/ArtemisMessagingTest.kt | 2 +- .../net/corda/testing/driver/DriverTests.kt | 4 +-- 9 files changed, 21 insertions(+), 49 deletions(-) diff --git a/node/src/integration-test/kotlin/net/corda/node/amqp/AMQPBridgeTest.kt b/node/src/integration-test/kotlin/net/corda/node/amqp/AMQPBridgeTest.kt index 484c84687a..faca545bdc 100644 --- a/node/src/integration-test/kotlin/net/corda/node/amqp/AMQPBridgeTest.kt +++ b/node/src/integration-test/kotlin/net/corda/node/amqp/AMQPBridgeTest.kt @@ -9,7 +9,6 @@ import net.corda.core.node.NodeInfo import net.corda.core.node.services.NetworkMapCache import net.corda.core.utilities.NetworkHostAndPort import net.corda.node.internal.protonwrapper.netty.AMQPServer -import net.corda.node.internal.security.RPCSecurityManager import net.corda.node.services.api.NetworkMapCacheInternal import net.corda.node.services.config.* import net.corda.node.services.messaging.ArtemisMessagingClient @@ -178,8 +177,7 @@ class AMQPBridgeTest { doReturn(Observable.never()).whenever(it).changed doReturn(listOf(NodeInfo(listOf(amqpAddress), listOf(BOB.identity), 1, 1L))).whenever(it).getNodesByOwningKeyIndex(any()) } - val userService = rigorousMock() - val artemisServer = ArtemisMessagingServer(artemisConfig, artemisPort, networkMap, userService, MAX_MESSAGE_SIZE) + val artemisServer = ArtemisMessagingServer(artemisConfig, artemisPort, networkMap, MAX_MESSAGE_SIZE) val artemisClient = ArtemisMessagingClient(artemisConfig, artemisAddress, MAX_MESSAGE_SIZE) artemisServer.start() artemisClient.start() @@ -208,8 +206,7 @@ class AMQPBridgeTest { doReturn(Observable.never()).whenever(it).changed doReturn(listOf(NodeInfo(listOf(artemisAddress), listOf(ALICE.identity), 1, 1L))).whenever(it).getNodesByOwningKeyIndex(any()) } - val userService = rigorousMock() - val artemisServer = ArtemisMessagingServer(artemisConfig, artemisPort2, networkMap, userService, MAX_MESSAGE_SIZE) + val artemisServer = ArtemisMessagingServer(artemisConfig, artemisPort2, networkMap, MAX_MESSAGE_SIZE) val artemisClient = ArtemisMessagingClient(artemisConfig, artemisAddress2, MAX_MESSAGE_SIZE) artemisServer.start() artemisClient.start() diff --git a/node/src/integration-test/kotlin/net/corda/node/amqp/ProtonWrapperTests.kt b/node/src/integration-test/kotlin/net/corda/node/amqp/ProtonWrapperTests.kt index 13206aad4d..002eb72725 100644 --- a/node/src/integration-test/kotlin/net/corda/node/amqp/ProtonWrapperTests.kt +++ b/node/src/integration-test/kotlin/net/corda/node/amqp/ProtonWrapperTests.kt @@ -12,7 +12,6 @@ import net.corda.core.utilities.NetworkHostAndPort import net.corda.node.internal.protonwrapper.messages.MessageStatus import net.corda.node.internal.protonwrapper.netty.AMQPClient import net.corda.node.internal.protonwrapper.netty.AMQPServer -import net.corda.node.internal.security.RPCSecurityManager import net.corda.node.services.api.NetworkMapCacheInternal import net.corda.node.services.config.CertChainPolicyConfig import net.corda.node.services.config.NodeConfiguration @@ -235,8 +234,7 @@ class ProtonWrapperTests { val networkMap = rigorousMock().also { doReturn(never()).whenever(it).changed } - val userService = rigorousMock() - val server = ArtemisMessagingServer(artemisConfig, artemisPort, networkMap, userService, MAX_MESSAGE_SIZE) + val server = ArtemisMessagingServer(artemisConfig, artemisPort, networkMap, MAX_MESSAGE_SIZE) val client = ArtemisMessagingClient(artemisConfig, NetworkHostAndPort("localhost", artemisPort), MAX_MESSAGE_SIZE) server.start() client.start() diff --git a/node/src/main/kotlin/net/corda/node/internal/Node.kt b/node/src/main/kotlin/net/corda/node/internal/Node.kt index d706aaffd0..d854e5e215 100644 --- a/node/src/main/kotlin/net/corda/node/internal/Node.kt +++ b/node/src/main/kotlin/net/corda/node/internal/Node.kt @@ -151,7 +151,7 @@ open class Node(configuration: NodeConfiguration, val serverAddress = configuration.messagingServerAddress ?: makeLocalMessageBroker() val rpcServerAddresses = if (configuration.rpcOptions.standAloneBroker) BrokerAddresses(configuration.rpcOptions.address!!, configuration.rpcOptions.adminAddress) else startLocalRpcBroker() - val advertisedAddress = info.addresses.first() + val advertisedAddress = info.addresses.single() printBasicNodeInfo("Incoming connection address", advertisedAddress.toString()) rpcMessagingClient = RPCMessagingClient(configuration.rpcOptions.sslConfig, rpcServerAddresses.admin, networkParameters.maxMessageSize) @@ -190,21 +190,13 @@ open class Node(configuration: NodeConfiguration, private fun makeLocalMessageBroker(): NetworkHostAndPort { with(configuration) { - messageBroker = ArtemisMessagingServer(this, p2pAddress.port, services.networkMapCache, securityManager, networkParameters.maxMessageSize) + messageBroker = ArtemisMessagingServer(this, p2pAddress.port, services.networkMapCache, networkParameters.maxMessageSize) return NetworkHostAndPort("localhost", p2pAddress.port) } } override fun myAddresses(): List { - val addresses = mutableListOf() - addresses.add(configuration.messagingServerAddress ?: getAdvertisedAddress()) - rpcBroker?.addresses?.let { - addresses.add(it.primary) - if (it.admin != it.primary) { - addresses.add(it.admin) - } - } - return addresses + return listOf(configuration.messagingServerAddress ?: getAdvertisedAddress()) } private fun getAdvertisedAddress(): NetworkHostAndPort { diff --git a/node/src/main/kotlin/net/corda/node/services/messaging/AMQPBridgeManager.kt b/node/src/main/kotlin/net/corda/node/services/messaging/AMQPBridgeManager.kt index 1a0417d1ba..95289fe763 100644 --- a/node/src/main/kotlin/net/corda/node/services/messaging/AMQPBridgeManager.kt +++ b/node/src/main/kotlin/net/corda/node/services/messaging/AMQPBridgeManager.kt @@ -153,7 +153,7 @@ internal class AMQPBridgeManager(val config: NodeConfiguration, val p2pAddress: } private fun gatherAddresses(node: NodeInfo): Sequence { - val address = node.addresses.first() + val address = node.addresses.single() return node.legalIdentitiesAndCerts.map { ArtemisMessagingComponent.NodeAddress(it.party.owningKey, address) }.asSequence() } diff --git a/node/src/main/kotlin/net/corda/node/services/messaging/ArtemisMessagingServer.kt b/node/src/main/kotlin/net/corda/node/services/messaging/ArtemisMessagingServer.kt index cb6609d2ee..21b98e5538 100644 --- a/node/src/main/kotlin/net/corda/node/services/messaging/ArtemisMessagingServer.kt +++ b/node/src/main/kotlin/net/corda/node/services/messaging/ArtemisMessagingServer.kt @@ -17,7 +17,6 @@ import net.corda.node.internal.artemis.ArtemisBroker import net.corda.node.internal.artemis.BrokerAddresses import net.corda.node.internal.artemis.CertificateChainCheckPolicy import net.corda.node.internal.artemis.SecureArtemisConfiguration -import net.corda.node.internal.security.RPCSecurityManager import net.corda.node.services.api.NetworkMapCacheInternal import net.corda.node.services.config.NodeConfiguration import net.corda.node.services.messaging.NodeLoginModule.Companion.NODE_ROLE @@ -34,9 +33,6 @@ import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.P2P_PREFIX import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.PEERS_PREFIX import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.PEER_USER import net.corda.nodeapi.internal.ArtemisMessagingComponent.NodeAddress -import net.corda.nodeapi.internal.crypto.X509Utilities.CORDA_CLIENT_TLS -import net.corda.nodeapi.internal.crypto.X509Utilities.CORDA_ROOT_CA -import net.corda.nodeapi.internal.crypto.loadKeyStore import net.corda.nodeapi.internal.requireOnDefaultFileSystem import org.apache.activemq.artemis.api.core.SimpleString import org.apache.activemq.artemis.api.core.management.ActiveMQServerControl @@ -84,7 +80,6 @@ import javax.security.auth.spi.LoginModule class ArtemisMessagingServer(private val config: NodeConfiguration, private val p2pPort: Int, val networkMapCache: NetworkMapCacheInternal, - val securityManager: RPCSecurityManager, val maxMessageSize: Int) : ArtemisBroker, SingletonSerializeAsToken() { companion object { private val log = contextLogger() @@ -132,8 +127,8 @@ class ArtemisMessagingServer(private val config: NodeConfiguration, // Artemis IO errors @Throws(IOException::class, KeyStoreException::class) private fun configureAndStartServer() { - val (artemisConfig, securityPlugin) = createArtemisConfig() - val securityManager = createArtemisSecurityManager(securityPlugin) + val artemisConfig = createArtemisConfig() + val securityManager = createArtemisSecurityManager() activeMQServer = ActiveMQServerImpl(artemisConfig, securityManager).apply { // Throw any exceptions which are detected during startup registerActivationFailureListener { exception -> throw exception } @@ -188,14 +183,13 @@ class ArtemisMessagingServer(private val config: NodeConfiguration, * 3. RPC users. These are only given sufficient access to perform RPC with us. * 4. Verifiers. These are given read access to the verification request queue and write access to the response queue. */ - private fun ConfigurationImpl.configureAddressSecurity(): Pair { + private fun ConfigurationImpl.configureAddressSecurity(): Configuration { val nodeInternalRole = Role(NODE_ROLE, true, true, true, true, true, true, true, true) securityRoles["$INTERNAL_PREFIX#"] = setOf(nodeInternalRole) // Do not add any other roles here as it's only for the node securityRoles["$P2P_PREFIX#"] = setOf(nodeInternalRole, restrictedRole(PEER_ROLE, send = true)) securityRoles[VerifierApi.VERIFICATION_REQUESTS_QUEUE_NAME] = setOf(nodeInternalRole, restrictedRole(VERIFIER_ROLE, consume = true)) securityRoles["${VerifierApi.VERIFICATION_RESPONSES_QUEUE_NAME_PREFIX}.#"] = setOf(nodeInternalRole, restrictedRole(VERIFIER_ROLE, send = true)) - val onLoginListener = { _: String -> } - return Pair(this, onLoginListener) + return this } private fun restrictedRole(name: String, send: Boolean = false, consume: Boolean = false, createDurableQueue: Boolean = false, @@ -206,7 +200,7 @@ class ArtemisMessagingServer(private val config: NodeConfiguration, } @Throws(IOException::class, KeyStoreException::class) - private fun createArtemisSecurityManager(loginListener: LoginListener): ActiveMQJAASSecurityManager { + private fun createArtemisSecurityManager(): ActiveMQJAASSecurityManager { val keyStore = config.loadSslKeyStore().internal val trustStore = config.loadTrustStore().internal @@ -222,10 +216,7 @@ class ArtemisMessagingServer(private val config: NodeConfiguration, val securityConfig = object : SecurityConfiguration() { // Override to make it work with our login module override fun getAppConfigurationEntry(name: String): Array { - val options = mapOf( - LoginListener::javaClass.name to loginListener, - RPCSecurityManager::class.java.name to securityManager, - NodeLoginModule.CERT_CHAIN_CHECKS_OPTION_NAME to certChecks) + val options = mapOf(NodeLoginModule.CERT_CHAIN_CHECKS_OPTION_NAME to certChecks) return arrayOf(AppConfigurationEntry(name, REQUIRED, options)) } } @@ -236,7 +227,7 @@ class ArtemisMessagingServer(private val config: NodeConfiguration, log.debug { "Queue created: $queueName, deploying bridge(s)" } fun deployBridgeToPeer(nodeInfo: NodeInfo) { log.debug("Deploying bridge for $queueName to $nodeInfo") - val address = nodeInfo.addresses.first() + val address = nodeInfo.addresses.single() bridgeManager.deployBridge(queueName, address, nodeInfo.legalIdentitiesAndCerts.map { it.name }.toSet()) } @@ -265,7 +256,7 @@ class ArtemisMessagingServer(private val config: NodeConfiguration, private fun updateBridgesOnNetworkChange(change: MapChange) { log.debug { "Updating bridges on network map change: ${change.node}" } fun gatherAddresses(node: NodeInfo): Sequence { - val address = node.addresses.first() + val address = node.addresses.single() return node.legalIdentitiesAndCerts.map { NodeAddress(it.party.owningKey, address) }.asSequence() } @@ -329,8 +320,6 @@ class NodeLoginModule : LoginModule { private var loginSucceeded: Boolean = false private lateinit var subject: Subject private lateinit var callbackHandler: CallbackHandler - private lateinit var securityManager: RPCSecurityManager - private lateinit var loginListener: LoginListener private lateinit var peerCertCheck: CertificateChainCheckPolicy.Check private lateinit var nodeCertCheck: CertificateChainCheckPolicy.Check private lateinit var verifierCertCheck: CertificateChainCheckPolicy.Check @@ -339,8 +328,6 @@ class NodeLoginModule : LoginModule { override fun initialize(subject: Subject, callbackHandler: CallbackHandler, sharedState: Map, options: Map) { this.subject = subject this.callbackHandler = callbackHandler - securityManager = options[RPCSecurityManager::class.java.name] as RPCSecurityManager - loginListener = options[LoginListener::javaClass.name] as LoginListener val certChainChecks: Map = uncheckedCast(options[CERT_CHAIN_CHECKS_OPTION_NAME]) peerCertCheck = certChainChecks[PEER_ROLE]!! nodeCertCheck = certChainChecks[NODE_ROLE]!! @@ -439,6 +426,4 @@ class NodeLoginModule : LoginModule { private fun clear() { loginSucceeded = false } -} - -typealias LoginListener = (String) -> Unit \ No newline at end of file +} \ No newline at end of file diff --git a/node/src/main/kotlin/net/corda/node/services/messaging/CoreBridgeManager.kt b/node/src/main/kotlin/net/corda/node/services/messaging/CoreBridgeManager.kt index 74e4a711c6..208bc3c946 100644 --- a/node/src/main/kotlin/net/corda/node/services/messaging/CoreBridgeManager.kt +++ b/node/src/main/kotlin/net/corda/node/services/messaging/CoreBridgeManager.kt @@ -43,7 +43,7 @@ internal class CoreBridgeManager(val config: NodeConfiguration, val activeMQServ } private fun gatherAddresses(node: NodeInfo): Sequence { - val address = node.addresses.first() + val address = node.addresses.single() return node.legalIdentitiesAndCerts.map { ArtemisMessagingComponent.NodeAddress(it.party.owningKey, address) }.asSequence() } diff --git a/node/src/main/kotlin/net/corda/node/services/messaging/P2PMessagingClient.kt b/node/src/main/kotlin/net/corda/node/services/messaging/P2PMessagingClient.kt index 766e67b780..9deabfaccd 100644 --- a/node/src/main/kotlin/net/corda/node/services/messaging/P2PMessagingClient.kt +++ b/node/src/main/kotlin/net/corda/node/services/messaging/P2PMessagingClient.kt @@ -523,7 +523,7 @@ class P2PMessagingClient(config: NodeConfiguration, // TODO Rethink PartyInfo idea and merging PeerAddress/ServiceAddress (the only difference is that Service address doesn't hold host and port) override fun getAddressOfParty(partyInfo: PartyInfo): MessageRecipients { return when (partyInfo) { - is PartyInfo.SingleNode -> NodeAddress(partyInfo.party.owningKey, partyInfo.addresses.first()) + is PartyInfo.SingleNode -> NodeAddress(partyInfo.party.owningKey, partyInfo.addresses.single()) is PartyInfo.DistributedNode -> ServiceAddress(partyInfo.party.owningKey) } } diff --git a/node/src/test/kotlin/net/corda/node/services/messaging/ArtemisMessagingTest.kt b/node/src/test/kotlin/net/corda/node/services/messaging/ArtemisMessagingTest.kt index 8ab4fb33b6..7d9ee8a42d 100644 --- a/node/src/test/kotlin/net/corda/node/services/messaging/ArtemisMessagingTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/messaging/ArtemisMessagingTest.kt @@ -184,7 +184,7 @@ class ArtemisMessagingTest { } private fun createMessagingServer(local: Int = serverPort, maxMessageSize: Int = MAX_MESSAGE_SIZE): ArtemisMessagingServer { - return ArtemisMessagingServer(config, local, networkMapCache, securityManager, maxMessageSize).apply { + return ArtemisMessagingServer(config, local, networkMapCache, maxMessageSize).apply { config.configureWithDevSSLCertificate() messagingServer = this } diff --git a/testing/node-driver/src/integration-test/kotlin/net/corda/testing/driver/DriverTests.kt b/testing/node-driver/src/integration-test/kotlin/net/corda/testing/driver/DriverTests.kt index e30d1b73e1..9778fbd44e 100644 --- a/testing/node-driver/src/integration-test/kotlin/net/corda/testing/driver/DriverTests.kt +++ b/testing/node-driver/src/integration-test/kotlin/net/corda/testing/driver/DriverTests.kt @@ -28,13 +28,13 @@ class DriverTests { val DUMMY_REGULATOR_NAME = CordaX500Name("Regulator A", "Paris", "FR") val executorService: ScheduledExecutorService = Executors.newScheduledThreadPool(2) fun nodeMustBeUp(handleFuture: CordaFuture) = handleFuture.getOrThrow().apply { - val hostAndPort = nodeInfo.addresses.first() + val hostAndPort = nodeInfo.addresses.single() // Check that the port is bound addressMustBeBound(executorService, hostAndPort, (this as? NodeHandle.OutOfProcess)?.process) } fun nodeMustBeDown(handle: NodeHandle) { - val hostAndPort = handle.nodeInfo.addresses.first() + val hostAndPort = handle.nodeInfo.addresses.single() // Check that the port is bound addressMustNotBeBound(executorService, hostAndPort) } From d17670c747d16b7f6e06e19bbbd25eb06e45cb93 Mon Sep 17 00:00:00 2001 From: Ben Wyeth Date: Wed, 24 Jan 2018 15:19:24 +0000 Subject: [PATCH 25/36] Provide an API to register callback on app shutdown (#2402) Provide an API to register callback on app shutdown. --- .../kotlin/net/corda/core/node/ServiceHub.kt | 15 ++++++ docs/source/changelog.rst | 3 ++ .../net/corda/node/NodePerformanceTests.kt | 12 ----- .../net/corda/node/NodeUnloadHandlerTests.kt | 48 +++++++++++++++++++ .../net/corda/node/internal/AbstractNode.kt | 5 ++ .../net/corda/testing/node/MockServices.kt | 3 ++ .../testing/node/internal/DriverDSLImpl.kt | 4 ++ 7 files changed, 78 insertions(+), 12 deletions(-) create mode 100644 node/src/integration-test/kotlin/net/corda/node/NodeUnloadHandlerTests.kt diff --git a/core/src/main/kotlin/net/corda/core/node/ServiceHub.kt b/core/src/main/kotlin/net/corda/core/node/ServiceHub.kt index 19fd97cec5..1832ac1cb5 100644 --- a/core/src/main/kotlin/net/corda/core/node/ServiceHub.kt +++ b/core/src/main/kotlin/net/corda/core/node/ServiceHub.kt @@ -354,4 +354,19 @@ interface ServiceHub : ServicesForResolution { * @return A new [Connection] */ fun jdbcSession(): Connection + + /** + * Allows the registration of a callback that may inform services when the app is shutting down. + * + * The intent is to allow the cleaning up of resources - e.g. releasing ports. + * + * You should not rely on this to clean up executing flows - that's what quasar is for. + * + * Please note that the shutdown handler is not guaranteed to be called. In production the node process may crash, + * be killed by the operating system and other forms of fatal termination may occur that result in this code never + * running. So you should use this functionality only for unit/integration testing or for code that can optimise + * this shutdown e.g. by cleaning up things that would otherwise trigger a slow recovery process next time the + * node starts. + */ + fun registerUnloadHandler(runOnStop: () -> Unit) } diff --git a/docs/source/changelog.rst b/docs/source/changelog.rst index 7a1eb2d3bc..210e6a99d2 100644 --- a/docs/source/changelog.rst +++ b/docs/source/changelog.rst @@ -162,6 +162,9 @@ UNRELEASED However, assuming a clean reset of the artemis data and that the nodes are consistent versions, data persisted via the AMQP serializer will be forward compatible. +* The ability for CordaServices to register callbacks so they can be notified of shutdown and clean up resource such as + open ports. + .. _changelog_v1: Release 1.0 diff --git a/node/src/integration-test/kotlin/net/corda/node/NodePerformanceTests.kt b/node/src/integration-test/kotlin/net/corda/node/NodePerformanceTests.kt index b0868815c8..594c402ba2 100644 --- a/node/src/integration-test/kotlin/net/corda/node/NodePerformanceTests.kt +++ b/node/src/integration-test/kotlin/net/corda/node/NodePerformanceTests.kt @@ -31,13 +31,6 @@ import java.util.* import java.util.concurrent.TimeUnit import kotlin.streams.toList - -private fun checkQuasarAgent() { - if (!(ManagementFactory.getRuntimeMXBean().inputArguments.any { it.contains("quasar") })) { - throw IllegalStateException("No quasar agent") - } -} - @Ignore("Run these locally") class NodePerformanceTests { @StartableByRPC @@ -52,11 +45,6 @@ class NodePerformanceTests { val averageMs: Double ) - @Before - fun before() { - checkQuasarAgent() - } - @Test fun `empty flow per second`() { driver(startNodesInProcess = true) { diff --git a/node/src/integration-test/kotlin/net/corda/node/NodeUnloadHandlerTests.kt b/node/src/integration-test/kotlin/net/corda/node/NodeUnloadHandlerTests.kt new file mode 100644 index 0000000000..51ab0e4b1c --- /dev/null +++ b/node/src/integration-test/kotlin/net/corda/node/NodeUnloadHandlerTests.kt @@ -0,0 +1,48 @@ +package net.corda.node + +import net.corda.core.node.ServiceHub +import net.corda.core.node.services.CordaService +import net.corda.core.serialization.SingletonSerializeAsToken +import net.corda.core.utilities.contextLogger +import net.corda.core.utilities.getOrThrow +import net.corda.testing.DUMMY_BANK_A_NAME +import net.corda.testing.driver.driver +import org.junit.Assert +import org.junit.Test +import java.util.concurrent.CountDownLatch +import java.util.concurrent.TimeUnit + +class NodeUnloadHandlerTests { + + companion object { + val latch = CountDownLatch(1) + } + + @Test + fun `should be able to register run on stop lambda`() { + driver(startNodesInProcess = true, extraCordappPackagesToScan = listOf("net.corda.node"), isDebug = true) { + startNode(providedName = DUMMY_BANK_A_NAME).getOrThrow() + // just want to fall off the end of this for the mo... + } + Assert.assertTrue("Timed out waiting for AbstractNode to invoke the test service shutdown callback",latch.await(30, TimeUnit.SECONDS)) + } + + @CordaService + class RunOnStopTestService(serviceHub: ServiceHub) : SingletonSerializeAsToken() { + + companion object { + private val log = contextLogger() + } + + init { + serviceHub.registerUnloadHandler(this::shutdown) + } + + fun shutdown() { + log.info("shutting down") + latch.countDown() + } + + } + +} \ No newline at end of file diff --git a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt index df4a449772..899d6618e7 100644 --- a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt +++ b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt @@ -804,6 +804,11 @@ abstract class AbstractNode(val configuration: NodeConfiguration, } override fun jdbcSession(): Connection = database.createSession() + + // allows services to register handlers to be informed when the node stop method is called + override fun registerUnloadHandler(handler: () -> Unit) { + runOnStop += handler + } } } diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockServices.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockServices.kt index 7ff6788ba4..ca7a2fa1f9 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockServices.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockServices.kt @@ -57,6 +57,7 @@ open class MockServices private constructor( private val initialIdentity: TestIdentity, private val moreKeys: Array ) : ServiceHub, StateLoader by validatedTransactions { + companion object { @JvmStatic val MOCK_VERSION_INFO = VersionInfo(1, "Mock release", "Mock revision", "Mock Vendor") @@ -157,6 +158,8 @@ open class MockServices private constructor( } override fun jdbcSession(): Connection = throw UnsupportedOperationException() + + override fun registerUnloadHandler(runOnStop: () -> Unit) = throw UnsupportedOperationException() } class MockKeyManagementService(val identityService: IdentityServiceInternal, diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/DriverDSLImpl.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/DriverDSLImpl.kt index a2d00fa9b1..dab5cd7a68 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/DriverDSLImpl.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/DriverDSLImpl.kt @@ -54,6 +54,7 @@ import okhttp3.Request import rx.Observable import rx.observables.ConnectableObservable import rx.schedulers.Schedulers +import java.lang.management.ManagementFactory import java.net.ConnectException import java.net.URL import java.net.URLClassLoader @@ -737,6 +738,9 @@ class DriverDSLImpl( ): CordaFuture, Thread>> { return executorService.fork { log.info("Starting in-process Node ${config.corda.myLegalName.organisation}") + if (!(ManagementFactory.getRuntimeMXBean().inputArguments.any { it.contains("quasar") })) { + throw IllegalStateException("No quasar agent: -javaagent:lib/quasar.jar and working directory project root might fix") + } // Write node.conf writeConfig(config.corda.baseDirectory, "node.conf", config.typesafe) // TODO pass the version in? From cfb8997f231d2877844e6d6bb591398a38765048 Mon Sep 17 00:00:00 2001 From: Mike Hearn Date: Wed, 24 Jan 2018 17:14:03 +0100 Subject: [PATCH 26/36] Fix build break due to an API change between PR check and merge --- .../kotlin/net/corda/node/NodeUnloadHandlerTests.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/node/src/integration-test/kotlin/net/corda/node/NodeUnloadHandlerTests.kt b/node/src/integration-test/kotlin/net/corda/node/NodeUnloadHandlerTests.kt index 51ab0e4b1c..ef2cb6f2d4 100644 --- a/node/src/integration-test/kotlin/net/corda/node/NodeUnloadHandlerTests.kt +++ b/node/src/integration-test/kotlin/net/corda/node/NodeUnloadHandlerTests.kt @@ -5,7 +5,7 @@ import net.corda.core.node.services.CordaService import net.corda.core.serialization.SingletonSerializeAsToken import net.corda.core.utilities.contextLogger import net.corda.core.utilities.getOrThrow -import net.corda.testing.DUMMY_BANK_A_NAME +import net.corda.testing.core.DUMMY_BANK_A_NAME import net.corda.testing.driver.driver import org.junit.Assert import org.junit.Test From 51d6209c97ac1177abc077f591e67518a976a107 Mon Sep 17 00:00:00 2001 From: Katelyn Baker Date: Wed, 24 Jan 2018 15:51:52 +0000 Subject: [PATCH 27/36] Upgrade gradle to 4.4.1 --- gradle/wrapper/gradle-wrapper.jar | Bin 54727 -> 54333 bytes gradle/wrapper/gradle-wrapper.properties | 3 +- gradlew.bat | 168 +++++++++++------------ 3 files changed, 85 insertions(+), 86 deletions(-) diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 27768f1bbac3ce2d055b20d521f12da78d331e8e..99340b4ad18d3c7e764794d300ffd35017036793 100644 GIT binary patch delta 16166 zcmZ9zV_;p;)-D{|wr$(CZQJ&4Z0^`jV;gO3+l`aPPMS3Ra{Au;o!fW)S!2%mtY@vc z_m~X_1 zOe!V;7EB`m^0f55(KRtXs1UO_a5vCw7TvJnN?{$B^hESTY1-|WW$fuC!))2gLl+e? z6*{6~p3j+o409h1L%YD%Oz1Hc1l(A^h%a3$lmlg{l9`D8tO_PSp1hwvo@_tf-&S`) zdcItntp!8kIuD}^i1D`_nDJw|%IY}B?zs_M|Bxd9Xcxtp%1SXM2yozDPtXj_sXppq zr8CvotM*_YtfBzZXkyGpF~nsNR>RvVBbpHOr6fZf(jB%L(kVTZjMSaKuE*5kuj@_| zrOgRdW5-k(HgdrFpL>s1sf#mWH8r=DVZ)bw(oGwkx@7=wZ{jc2m{qOvhJdLmu@2^o6(HSbhXTmCY6zQU~VqsgHbX^t#!+ zQF$@tK$r3z%DD?7M!F2$XWisT9nog%m9S=7PL!mXzL8ce(}PL7*RX0_Sn|p*CQvKD z402Fm(kjo+r-~I%h1=W1G=85y>OuWZy4}vb7Ubz#bvQk`#wO1elH7OMD&M7AH5Ot3 zue7xWTJSY`T+4SiW-L)O(xoxqos4wB&q((8A|gY+o=>WTcng~194i9AHq^i!zSr)o zU^rK1AWVBp%*z(880IRy{L(HuU)Os}fOu@GgHkHRE2Um=rFj%?Akdx7#-y*vo0q(Q zfKWn&)qsR{4K1#h}Tk3ho-4W))8x@e1O zo3mO3)cimXm{?dYq<&-@20D;JL%uZ__wxpnlZ0Xb{VJdG4YgdLtT73`IxX!eIRzxR zLK^>&-+N07slSa6ah5#Yo|V%eyv-`J*PHPgV)-Vuxt_`GsU;xbefw^kPV-U+kcs+v z1wSURnzr-IMio-6!Q&}dyKT1QGXJ{dZIqT`Q^g0FRFX|ycBOGsgK}rGcx*jWqz}9gmP3F;pWA1romxkNy}DarU<01?fXAiiETU zawW1EWmR0vXs4$Dpb=u;y!B262)pa~&GCnA#t(ZH$t>xfbS6j|f8f0@JqV2UH%3vc z^4t^5-oxzL4NUSzOs=k^#=ug)W{Z4Bq$7tJ+V6wKGh-AYgYW@jmghgO=cMx+Nk<*f zstNqj1rpSL^P!H1z3?T69uX@L$^x_Kw)r@cx!)q57TJgMuvry?EW$Hb0AM^TN(5y0 zgDsdr)%4OmZ@miVG^$nY6K+&94k$}RJkU)_Xv6zL)HawFq4n=cp~j+O zO4broL_W_kyp!M_O8hE<%jQJd>$M8;)jDU-9Yo#(Jrk;7yi=I2<$}Gqx2+vYCjt*B63~$_B356cyvoyKHU@}BTr^`K8d$z|-*U z*Un|C=UJR~)j1^A|1rfqJnkch>*{*w@q&sLJjrdjQqgbo%3)~G^{p};PMsJ-;9VKpDVG36;gmRHZ2Z{_`)iaa<3uyGm>IN`Yk zxrW5IB>=4Uk_9NCtD=0&v^6v^s-V#Apwb5$tzk)gC(~15Qqz-3gZRT}SqpEO;edUM z833{ij*dtdeOnNnxG#=#a)HHw)fJxfax#^h#k-r8m6s(b3;_IwA_=yHtxd|Yjh43w zWk_}yi!|LrXC>PgT8)!MVP)D6j)H|1Qjkl!kr^^ar3FMtowsVi4dePPM(8HNmp2({ z90jNXE8=SzC5@HsyShpdD*S11M@zIDHT!)o*C9bn>Z82FLLwW1lp#WZ%| zP+NKIZdognlF*W@9YY^ec^jyEdUVC+QEsGL#aog^N#EYA6=88Rd){TIgq@})OIfX| zO)O5mrvk|BdpDj$S5qg16^|`CVHj>{+Dx}PB`e3>Al&JUNyRely-;?hamMKUdJ4&F zot14Qqa_cuC0IW|MJy1b`y;w~poH0a?decM(q_G>W~JTRA%!fT<%{XPm{5Oz<%RC@hJ_urtX}^+~Jrk4lWKq=Oma%Z+0TZ((W zI@|*)T@^$K4OAE-`X)$|4L4QpSN7KBlA|c`G)Kk!y%&5szj)Q;vOjWNiy4;G8MPRn z;0UzR_oVy!{`_6PBsm)rL-cF1ect~IBv?Qp>PhhT6a49OrH$$N9q}dNgn$r~1c~Rp zR}p_u!EPoY2H+a+xNQvoWF!SIhj27xJ*#8>w%F zqTjtatjvVxTtQqp#j?v;Hl^=(Kua~9yg_#DDCEch7b@#b-iLC~&=#>a*0>&x3}O!J zUep%`9*0VB?ep?ZE>}pdV335;Fy4ExE~y_)vAS^a1sIo^>${owucln;piD%;u{b1Z zJXY?e{B|>vD6P$S&8$$a;injug=t0x+Z3I$59!)s#HtY4rxIW#Ka^`BQc9mu17SX7 z_gEqdZ?b@@L zed(L-7csOn33TBv#1lSF+R~Q#a3dUNf+t(ovrPi{6aBBSGJT z&AOzYRVeB0`!`a9WXl40ylCt)1NgCZ6*c#sA3>XX&z&dS@d)C;GVOOpZbBxz-f;iMl%id3sy<8vo5Fk-`;5y9nKadJSRWkADn@D{070IH)y=O3p`zzC*&-HW>7 zaXmsh|1uUaGoI|QClpH!(Lv_H_;4X+YA^3A9a|isHEd|x)s-LSc@d+&dB?Q17FL!d zlR?p-1R} zHz3Wl_)?-fPn&cmaZkL$=npa?y5lfFv7M1=+r+3=XzgCm=y;}X`G{OaD!4DxcI=5b zRZvn58Drhr;*9(f)fO%uH;+(cKAu_T0$5d8u=87HZsO$BG-qxN%gqAcsf#>OU4%rI zq2z=kLhKYQenDO~z@bqsDdR>mtf&PC4xl>&5r>+KjGJf+DJgx$pOO>tfeV{uI$J^h zT@HkS!%+Xb0Zi%wWC}(K^S)b@5z^=Ew)_&48YHuPh_ES|y`HSmilu9xa^G|_pX6N- zVDX;nMDXR3c-)0N#k6Wzc2BOsQ4MdsNQ@Or6a+@k@0}ZX4D?h?N!am^n7m@Tv+IIOCnST@iM$OCb#usM^MoeJ;?Pfd^ zc*Q##__WphT>rTaAd282TjmZeu-dV)4e0ETjVCoQkjKh{8jN5jC=XeLn`?d-N+Ug> z8*L>uIO!lgpb`QCSmQhsdg4&Hc;E=yaiK1+AEZ1IOv#+}mNk~u+U@nK)E3q4t$OS5 zHhs6}oT|0No87{URuH%Qrd$Fy@IR;qSe?CpxUuITEM#}qvgV>h#9Q^&=z1@?#WNK3 z=&IW1RF%d0PSU`3A?#^h7TMaadQ_WY=NzRgLmicy+N|gS6dgG@LKuBh;+{Mts1Y@u z3I&JGgy07oY%v^{o8XllwmEqEOmJLBof!05##dK7K9Ya+W>-~8acZ_OZht-QVr1W0 z>JC7M{${5c?qT?Gx|xNPFX0A#1*Qo9R$dtd-cFe4A){$C%)^jlppZ~91`;uPO_A24 zTao8+FPMW4P}JkNN0<+KVjYugw$-Fjlt4=GGPMfdf>Sb8=%?8^=@?8BNQpDMH-yZ2 zNjad0q;z1F=%LVXL4L+%%TK<-OGLb{oF~UsCTuDF!9WF3?ToVz4Xx8yHQi;krd^=f zvQi4s}tCfOv_5tZYfTsc0n#31g%EDV1ntv7rOc+m=uX^PS@?Kt%6O)}5bog>9m!6iensWIq=48SYxe6>_hJ zFLYT2pjqm<+V)0~N~52a^PFxx<*fgHXHTxR+OuQS2=%(4oo0~e8`L%6`yWcDeHtUw zB52Lb9a9DzjH5iMpcjeOif^mX9NnVs!jEzcAMgQ;l2Oio0Tt^AEeAZ7}3x}?0~S2nGG!zMG_gBiJ6O+sVv@u+luRJVUVu}G~k58 z2wF?Y1A?AgtBv&ol!TasnuMGY9_oB-HmnqKN9Yk2n}l!?t#uwl_ki&Z<{tB%cG{Hd zE^wy;7v(B{0tKl0(%FW^O8G52|RfioRP?|>0Oq7I_ac~^qHR>!Ni5VU+R z!&x@QL}W)(BnMkGuc^GEs$VJoeK#@kH24U=NP1N9-^#9Ng-^<-2`A%{;$LMt#d)uE zNenCM9;QI85)))2N$)^4DJoIq-lL3C7`{2-6MSarxy% zi_((14_TB&mAv!Qg*me6Tp*Pb{5HPOTS?BzXT4a_pl^q&2Cdo_8ES`@bV@Nsv8< z**Oh!5|fDvljv(#21!&&{eds70N4uPr;P<`IFC{!j@dQZwdr`0f=&IB-{bN+^#$uD z{XlvW^T%Et$mhg!&8l+P!_qy}3Ih9A$D9p#zwJVSfHa^2NwcYeFS7)IZJo3KSe5?t za$Sabd`F|vQHfhv;M#@{fzgtetZV^g48d$C#a-<8D4$W-2%)FD;*UyQhLkiAb(Du( z4ecQK^LcK5k^cHTqp(03+!F9Gne%+rz4^B7zZ*Dy{U_j_@QYL&lnSIJ6yhcr3^^Ss z))!SYbbkQ&5Ho)Gu*H`GOabx)AVO?3CbNMTnW-C@)}4 zgothr=0WUkbU+dhV0V1kz8Lz%;1)a`;Fe!74K({CM4nLgeBWfgdy<#*!R@B(87Quo z^HCf)2Wy(Qw~yGd&B-RpxS>+ziYu9GQe4$+<}A`lw}V}3$G7SM2szO;hUpP2+C0`u zDJK?>Zg3iIf6Yv{OOMYmrc^oAL+LmsE3QLu#g-h6(v=`UZ*&czP&4_oWy!6HFVw;3 zDWH6vygcU#@@iF2Dbb@Y*MQZ~UcV;~pC(>+(T+y(K zsJ82>+}B?C6_WK6!ix1N##VX>G)DoRLqlnEq^85JUcH7DFndp5edUEDp3)1Orb_Fg zrbVJmkDkfT#JuUStl<#KUOm>(t?3@B9pP4wzOlqczRpH=Z5b8T3x@LY_9rR7vlBlL zV+P;mM^SPl6;b+KLtLc&D^X?$9-K}^GDpczM!bsG9)x^8r0h7WEgEx9ryuM3VD2Mg z>t@*#NPX5m0Q-Wt^9D63&YzYXg{o5M=@U8;m~0#Kq$+`?1!`F>UL;k1d&WN?KV%)09FxWNdOYyzNw20KiV~FG|0|>Vh&2hh(ZhCRb<@d(_zSt>@1LvV>=#oUP z6zqLUXwms<+$h#tK3_4sq;o1dh#<*q(xo&&iW})F0g!??wkQ|5;WZa@6$?=dLmrWdKTcUbq&2t-V;G6(6UPBe%CFoW^0_Oi%Y0Cu2N#S`$Gy!x8%CmgL$?AbcpF zeEOA2N-bpLU0BuVrPHVuKIfO{Sp+ir#x;twR9rg*(Vy6i(P8riPPj|Tl+${Xo<8$K zF0-@^o^U1>Ppg$+I;9dNRhDU-O!kHPt)3HY3jjer_!J;=+C0wbkyK$>*LI-pm)Y$= zHooU@KZ;&621n|312#@jw*<4cb$?GN`f9e_Jop7sMMKJikn`0ZCgniOkeiMx;;B+K zEf0DuVZ|}i?boT=nh6CuBwYrsr;nuZ41GoN^=`=niaf!7i;4*_c4`!vAr1eyz6aMy zW&qX$#zqcmF|n}eFIUYd5^lb1$j((>V>|De48HvPGAEh(lylqsNX_BlEiuAzfvc}A z?&!5oqy;o0iXDD1zNV}p?>D$FT~aD6VFNDHD5Wl!DDQh}yC_y+^3ixaM>Y;cuBa4# zF9aeY7t+x@QIPv6b!d^QObLa01jBgyx_}6}xQ-8$o3=~&wf@x431k3bS~ZJHB6XAr;l>zu@Mgo#y(_%0G9XbT z6^QwVMGi`J;z^2C*#w1s)+7i!)c(Gs2-lwCL9Y3kIk{M%*dUn^Q>V9`EwxzMWe44VEB<&t$JdV8~`CS$FEq- zVpL}-JWzW-vp3X?ZTn${DrHqS4s;Ap?#h62BZeCJ16iKFkYoo=^&%Dyk8lzrf=aoO4l^0boM*n!@Z?9nD(nKXHgfu~0AS9C(M$bgzP-mO4^lM|UOl4nT zIm6GtjBx+54Iqt97{U$$5l+3`YMVk#M@Xl-s7ipi<4d8Tkj+;8 zsYpmWRn=Uaa=f3Jcz_jpeBqN$zOQ3~6SOzU-07j%3Al2dwP4AIGo6T;HeB_<#i~lE z!9Gt}b!hHz)E|ynv~f|b)*C>0FHaEFqM4|oWVj|0=iNjmY41m==HZ;MmcgyN+J0>D|@(MIqq$x*`7a1Ki4ZJBDtRQ(^*!#X6W)$qdkNA(_xuH)%$}` za$IZo79eQU()ks8RZ!N6zEd%RCc`srw=n}DG~ral!AsO5_-XZbD)|b5D-PW<>n`-R z+cjZ1yT}HrBUPeAyxr_E*M7$|NCbBvVsE?hxN+QAB(Enut3uvTxTvwW*HPQ+np!NI z*OZmk>zE|CQ}kzGq|bX29guqPhS|M} zp#vJ78S|l9m;4Q8$%+d<*=7;s9V&;8L$ZY9sMX3 z6nUjE`YhUGpZH((4gG~une};IsDk?3%P|5S6LA3RPKzoig50hLL$0zkjye-+ z3#n9BDO_JunHb87zp7~@g^;DS=L|t$k4mi~O;+4M_S}G=K}t{xeMuCXQdE>lBgu%# zh_gPP;sx@%UlI#{3xsZn>V~CYtrV435>m8%a914UxWj{E(U`JPVXZJT5(k5)!_rK} zN%3r7-d6&YIPg-5w_U_oHPZSFa>aUX(h2Pku;b?dSnLs4|70B?Ml6QEezD2X8+MHC>a-j`7=I`_DGN!!Sm0AK zmmOtMI_n5_#hm1GGLsyxO5m(vX9%Zr7I;)_w1l0lZ?PB6HuX@^soFNno28)R|jxcc@4S?|{E_+gn< z^;;wGCx^RHeNKLczFuK0zh_)EO~%73ptEUIFy~UJ73gj`=sE$>OX}(8EyY5oO(0Wo zw4hD_7ye`uM&%#T!$w+cu{bg%l-b`$Vm1Z1BLBtu6>?cE-?FGtwp0hn9Ai=?!EP)c znon7cTr3xxg@~m-g;|x={FpuU*{~1iQ18g!$ILJvhiM<}2$#v6?8o|`3bZDh5UHGo zxW&5m&<>e9h^Mw6n|R*nE~)32A*Ua9#9i6h7AEdd&96k;YTE4>r&2Dh>cC;%CMa5F z>a#aDt>`&jEEmaS<18>M*klrYik)+dmaf;A_wRPGNnlT&)geywKA{Dg;PHf zAnG~+7sn=o5(6`1lg?nmCcZz)mhCIMn$kx8fdGIpeg~668?^otulfyd)(}6}K~14; z$9>j!#(n0~aQk##KWhwvaVsW@#}4;vxCFVG?HEHQQ+uq9#9VbGC8TtXx{<+L@_WIU zPFf?Cxb>;-34rUF8)x6wGeeY)_l;^$Bz*JOtGUs-%Q;RkmO~t~!b+z*VscnWL+*qB ziq*mkDe!8IvclD-BjUOPp|Z&_$7!5RV3uOYY}oZ&Z=3UpQN3E%fZSoQ;aM_sHR3$W z>52{dh-y`jAMSw;04e&TQkr~;&6SU_bBqP|(goDG5YWukaf4SyfFm7Yh3jQl{fnvn z+|Pu3OrvkQquJYJC7@TmB;B%xy)ZnHL0%QxF}k}ssqJ|?Ky=SDc48b(1@_v2x=iEf}1V^f? zH35p(kKwDQ=OS{*oF~ClwWGNTXW;LNW(Wp^lFM7hSU0%SeU1-}r+~7Y-nZ{(b|)4_ zT_T?btEI1&Q;YgF1x|Ayz2KBXLu2z!QC@hp9)QlIOd-h&a#&V`#b#-mb?&&NTsRP% zVZ|tW8AiA~VHDkvm7^^5p;?k%%L$Z2wP&o6GIW|z?D=1))ce;s zR>Ca9L2boMkW;1_q}v0RT2zxA;wd))U%E3%c4_(61XD_k6` zXBdZ!Nuy8<#!D_!Rsu~-{*CzW#GFU&(E{X8bA#C`M^Y_wnrbmH%yJ|F8Y zk~ykyVehM6_LCj=tEu;ow*%}iaQV*%a4~qe>f%t8s57OiD=hHx!?{qHl%{k8Qx&et zxrQpYBE#Yl5Xg84>uH1^(fUl!cz_i@3?3u%Y!~a90OMYf6w+#OX%yDbo*GPp!jZl0T&4ZYT$euI@_szlW8|Xc)j2(QWX=jc#c;s zrm&4p{Fhb3&8p4>&Bq+Yt7Xa9b^(}65&)hu7#kwL-&q)A20-^n@h7_h4ZsUMJK?>p zejgPaXU9Os+F1yb7yjrzmj%=G821jlpB3RoZyISZz|u6~$MOK>vNvW`?OV6#bkI>$ zQKFYdCMF7@w(04ZnM~#=ACxJ|y_@bZI$^Ai84kbkui?6bl}nLPb%yhfXjmw%6<2U{ zx_s7DX34z%7h`~<{b-#e0zkdjTFSYTWW+JoTDog63R?ALC%O$+s>{IX7QEMO$8Oyy z@6?NbE;)vCQNz8rgH&3WN&K5;QS<`O_+$+2Vs=U=sh|5GhND_)q34`{*Z>0|iS5j% zD?t7lOmBw}{(=_+WN?f9l*{5{%_?M1I{)Q=!onD9*1utQg6h+re_(ybB-FABmB62o#X$*I{@SaD}XhEsEcZ=A>z!;(h`w=PYj z_Mf`ag{-(}<>@oi+bF)c`clGeUj-V3M(DQ;$ov8SS9d$prTK$^1lg(pug&nX@E8?L zMlvlM+9nJXF}bMHXj!0?0tLXTd-)Vk!C>Z?Q~nQTFc1b z%p2>j|8)+x^V4~IVgYDsqn$ZQGnYfJ4vkJ`v#FU8b0QO4riqnk`+{qjKObw>$QObuTfIc z8vaq-m5e~!N3kNb=D{z9Gv@Zd!E(9}+NHBsLJJ;DK_OUb0(W!S9Vz!7Hk=ChvExxun_D zzHSHQPDsL3F}O2~^hUEi*ulj+2lzXO0}d*RND%@rMgK z<&*YrcCy#zlbN;&_Xi9!CDEYrt@kkq$O7PQAR>Ixc%4iE5y*w#9aCRX(tY`;S{7(G zC`()QI|zYW1B44a$@(J@S7^NicOBB%S?gr`olLi!8>}+rCvwIn(~I5-!B56Y1y>l8 zHvUyP7I_rcZ3%|ET@KJL;~c@zndO1t$mRh=iDtZ^ITX}m!ck;WvN;sdQy|+~sFVr; z(Ep>5nE%&@8?CGO2K~9s?fx{2v;Fms%`Tuarm62bFQ{OADA8iTyF<2Dx~p!fm^duG zTCrryaIlhYqD>J!c?Cy!(zYMBu>(!=C;+QcSo~aWYvUz$2eA2iU-fNJ+?WH;M(bg+ z{0_oQyatM-buND`@b}<63qFcIfIp>n?}R2s(RpUfq_Qa?$RBJaAJ`?MiiLhETP{c*CtsGgRar?R%6@FtXIWT>078rixG| z233qw0@>>e%1$sQDwEpXY>H$-inNRoJm*$6xrBttHU4}aPoD?iId67e{ciEuSsAxg zP_gbHsz3PbF50uPo?_Od$HihB+5rmwTif422_I*BMHPJd-Vb3nV{^K-7`CCFS+H(@ zC>Z~kAcJTg0B9rqYBV`Ty+LzoJZd_OC+U6zAK}(QSbLwgrRJq+Fj4^5sRN^87o`QI zYgQ25$hEXNwc@=eJYL0Tv4@?K!b=&T*dVM4#&IEhM*H!$7S>0AT(`!Fo+H1TGu2j} zsS0;yGZqOP!e)q<)n^}{!6>-EEMOoiN+}go+HZHwIig|k zcY-p6;@Ac%6okRjbN696ZFS5WH?J4eg2D9|IO}Gkf`HW1e8x*M0enp~mOK9hW*sRx zF_A)m#lcC)Q22vTimf4_aLR$U{|%g!bw`b z(6>@6r2l60mVfOz|HRlgTvlu7P^-Liy~WR-CCfzhl(^-7#k>2Q=lpy7TEXb!G^+#z zYZBPG7TUga;P~hx4H)(H(H?a6c)6)d*xmQ$%h+AK@ka!V4HDl{6Y}k>CdUffzr-d4 z1SCa%+aDBoCl*pmG|x`j*Gv9#c$&#Xq}an}70pZ%^jKFZEuwn(^AuZ<#Mf?EC6U&S zQkX=&G8O~EBAY6lPwtjUIsGhqYECu68(eKj)v9WgVVG545D+5w9aiot`loBk!s2lb zklNjni1DycV@WiK6>mgEv8S!Oe6FZy{N#(b&K@T-R# zvwQ550&suw+jA?etRW8_j`FAsXOi7L*GZ;9 zZHuVlA{~}2feuJxzIM%;Kt?c_xN@1Hm|FOp+YU_Zsjamf7)kQlXdRk`UZ$@4)wrx^ z8wKD!v{Ht41b~NH{9l?nQhv!NxijUaQ0rx%s$t}^zwMdZhgFLd~avFwWhM$ z9xPE8z#!2*T5}sSD%|Qwjnlcv<>!d0C8LK>FG*;!e^X~Gw^HHG=`9_1MD&!Nk}!_L z;t(STWorWal5$a+K!L$Z2sDr!nK5sMAr*TAXs>>H5a&@%^Vl zmwe$n=eE&o47PW9#@CCeb?0+&*JeS7`RI={K#>ehquvi%dh68Q^Wwbpg#zlK)lcRDC>wN5oDq=>X zr}WdVsNy966kJn~u_Na#x7J@;nWWllT?5MzSo~+lVonqtw)R^uzSgE=C)2 z_(uDt@e8UE`w=sQ3T_8U9))up{U5}}08LpG8EW`tW&&!m{CM$Ntq_C?tNNN;f`!xG z76#X#tqUuZ(5z(J(@V<;eoe`DmSSBUK61SC7PCq+H?1_iUe@YKBuTq667gEZqK<{7 z=GC9n4hCisZ$4jVV<^mi_RizRHnFx08EF~J_l3!cXd~(dznfh&2lML>WmQ@(01mBb z6^{Z;{X$Eu3w%WyOlqJF7uzl3L`O;W_pBl|q5ISP&xVN6w1uwoZK817nixU>h8zTK zd>2vOWi9YJuTdX#V_7n7je*GFG+WKw2WKjAf}urG`e*5byF07PWoB&tTcY-tgq{W& zzvip~(bAB0H7XIcuI)08N(*0=0S5DDYD?1zNc2R^x?MKOg)I(dC^RP(d?(+FRc9I&`^WjTK3i><8C)V}U;S+0{4|8;04 z&XoK+f*>hKoGEQG3g6Ox|424QlM^$-fJvLCSQ*!?%s|qi<$1YnWB9}s-aq?L@3Af$ z&I$l!SLW9`qQij4erQ+r0I0bgMuqdP>O$S{j5n|!^K|PaEznBn5M^RNWOpkeDo=|J(fqy4_w1txA&xCPdeh2-7|OQjM$>|w$55WiOu>Am&N%?20$;VTKg`%SAfX- zmB2^liuF+5t)1!_@1-$dJ?U+vp`w2y=}mLQSN@;j!{(HD`*vVJS0Xq zOeh$q1Fg<5^R8jNK6n+*Nu1N5bi?_YPMSz7sYJKgd3=}G3&`LMqkWDcJkx!=GQ>QJ z5MWZCt3*FW^S@!IqR74x>J1P>9qrarrrOBc<*Avr4gDb3p_jg;ju*+)buFu zM8*8|7ln7q_>aJ+dXTiy3UAup5Y`SckH;L>aHcBEHP${+I^x}HrExH9xCVY>xoktN zpdKPRpIjuRR=|GqR(;>rI)5~l`9A;hP{zRUlWq7xt4S=H+>Rk^knXYNmM#2fJsPk7 zluxWDiRmAcey9ojMq{hgv~JHL$hWBvFVFaRO10IhVz!JAr|zz=##`1A2j(zpdt{=E zJoUdXh2~!{FHcqbr{kYnM}fTB8Y_nzKqXb%EYkrmYykR4y8YL4WUSZm!BpTSF%y>J zzLCK3M}0V1-U_Iv=+iSdSE+3SxnlZco z?Cc=kz+Pf7aeAy$=mPhttuE))J6du<{FemrRsGup9h%2x?fR}sX7}51ir&Lr`^U|z zapG+Ut~BoeI#S$>FTa~Z6w!a&>gN&TzvuFu9RS`9So}0+6oO$*1n;TCfm?L`9P0sw zdwo2u*?Y%|K|iUoy2oSh?vzXIz0|sk$;In7e;aSf*2X;voLAg%)D2(QUwkd}yg1W~ zuGcCZoVZIK+__+>bxGdJP2uJg&;G>?&04k?6KSSp{j0nbr7#g+4|;LA?WrKn+BGZtFT{Vl-ctmcI09ch~^E9^NV!Q&Wy=^(Oef4>Lj*P^4sW8p2{8K2V^rOSSIRzgPcODQ+j|7 zXOypJP-IgS#&C=lyq##DFLRTxGS#B4MYc10p2MPFpsZR9;;(nxE?;Gneo~C&uCS%Z zB<>Kkh=bJYq;QGHAWN?~;ka<#hi3)K$pV4-6(nKBiMmw>IYwDu&qtUEny)<#W+-Vf z9d0O^b^?_`CRyj2Dm&=TOcbc*y9@9ox)!hq94>Gx(J&j8NrDetB8j7>C%*m_zUtyFbi^6bHW=y zYHhw_<%ZtBZo#2%Heh2H5<7OFZR#TNXL|GBf_UxG8bo~?bb80Z-oq{p0KWwI5EJr5 zC}n}kTNTM47wX5%lOL^-SD4qgkTLK#C_jHP)>Ubr71NE+KPO8?Loi4!6Z1o099yR<*>8j z>0TNxTJ+fn2*5NBWpb?{y)*?Js#SaWbANYt2R6I)gh9(zs6t0_i%jkef~>yVBw62h z&IEi-E%-qyFlA?MSoSliS^Ndc-np$OUiyZn+9UWKZ+EEu^nO$=+*w^PU$YkJRG|MA z_)rhdDBoF3XH%dpt{?Nyq|`msSG%s4TVAQ;#Tl{XUGvIIe0}pXx@;;x-*jLXpNB8$B$dz zIR#El$H(p)^>RYGmc9poIqS)cP#Za6Vcb%PF%=kW)yy;>oidk~F+EX=TU*05IR@a* zF0fb1@X|{=QU^uKZ4%u&lnNAAzPmt7ZqP+Gln^UVHog+X)-R`1`v+eb(ulf zpLVhT>|Ji-{uym53+O4fl8t;JWJQ#l(u-+KPQv`gL}?)y&d~&D;*^$iV&SH>tj?w+ z)zc{q znnm+wWPy%m$NC$9VaERcYrK}|IZ4}G;gKe-|4ap~Bt&m*_$EO~fbOB=PN_uX?b@`$ zAUu(B#g8%th6W#Z-9FKDC*D|ZSamaW`4x+3DV^dsT~qHynJe=w0?UElac7V6PNZpo>T30m>c$1xYWt7(aY@5t+wueNH4=Z0*S=q@;;hIqz+|2 zE}3emhtO(VFhRA;9GGTQX5N>7@{diE0 z4%QpsR#+Q_w2$w#-ONUgsuPApMkMU%u->RpV)dj^XJ_&!@ml1TC zf!V;Bx}4rwPGcT0_L+%@F})abP*BNsQ1}_>jzroX6TH(|D;Id-Rheb|_`PuzXB}R; zzf3BPu@&YxmQZYIocCXG7qTJ%?D=}kHQaah;`8Xy)O>Pf+aOVdg}(B z9<3EK`iFV$IB(T4J@P*Txte~Z^vbwEbZ^2j%VB+~e}tJLZD}4zBZ$sE*!*JBRyA5&~u>aum~6tD6nw^6Q~G^2jrPT0a`$zAbdXie=Z%U&qhLEA`}czdJF|91O5Lt z3PAsDbQlo_%>y=!D1n`0|E(Y#6#?7C`O6B2OY)aBo%sKKFoyVVqX`)l@Eb7(@OqRE zjFs$f70Z|?7%}5tQHc>5m@!5N2FvufxHiTMM#%YBRNzGX`!%sLD4*f~|Mvm_O~w_# z(B%I@q5&+MpM8Pbb z{)#zH$p66*{5K031cd7Ue%#;tu=8JF!88}xl;@{N4*WHZP4G95?K72O0u}^>{~vsZ z*I$sz3?1bEWaR*6Fu@f4{!b+&uy2MC;y;NHz}*=vFtNbD^?0-N`2Ra9`oE+RK$BTr zg8xRef`ADAxA9Y=4*N^;GRr{lH`3?RhWz{)`YP}b$TapZ=rbGz7&M1M@ZXq<|IKEc z02E!I{fx4}0{&P)A^7XR{$#BEWaR#bQ9b!D&?E%~_~jG$A7@|%C#|FBTx z{_W}P0x7|NY^ZymEKblMAj1EE{PX{Um=~D{{%cVE-+z5)(I07?dSh2(t!~S0Ogih3I6{9{rO^?e_rl50L)mX LgBlz7PwoE)%=85}GDzrKaY0UiN=*uEGF z4D1LAIG0Zfe0M+rxGZR*ek9{g)Jvcv4(<_?2K>O-#D+*#rU}s_r4XJkBAYE(EsZNP z!CO0?|NVZ4;d!PvAxC0a8X}W^_7E99{h;&^-t-<4(El?@YxpVFBb13xj z@yG?nbg)6wVk|pEpweVq0jbBb6^PeGTdeciMhqgfA9Tb3IE;;K(Af{={VtU5E@#%2@4F6W=-p!QSCr~|o!B(* za&aJ1Y{vmDz>75nqqxeB+X!l!?YC(JAK4yYUv-te;zbMDa}BF4pxvq&@7)lbP&sa)!Hmtitjnkl zq0=y?z9)>B5lY~(e+>dhxRGDimATtmPbb{cFqv8twuzAkHE%XY(jdC79;(}Q{3tFc z-!y<5va)<)u8&8iMuX@|sb*aMe$R9~d-5|Tw~avauDaBI6}vcUY?2ddJEsQMTu~a@ zb>1Tz&X(y#IMa}T(A@lUQ_F` ze+Lm?F8zWNT&h!&&E0cpe-9EowiY-exI7^8P!mIk{Psux>b&q;F2?c&{=3bKInOH{2tw7vx<}1J90Y%gbL~%48cQYRH<#fvZu+!E^V0 zIk7=QV=SVbkUFaI%HkrzEMt&@_N=dAsK8Cjh`M)pOp#%l3_ z^(#E%&3DQ0pzu{uvWj26w)~F>qQMih>&*A3xxM}ct?s5To1w6|8)riOW!`% z%&ABZ*Kp%g1JQB36E6c5(^g)SjpLNb?RZr$(DdLIj}cr0M%V-=iR?+CN;7zMHYUeJ zV7{|am$K~hzAh+e^NUeuZgUKu1KfndQ&KL_m!}+Kg``-!Lmqd?GOn-n2Ex)^eG7f5 zWB21wZ=CEfQ06u)H!jk?8LJ`fvV6>b(Ze&w=xn<%BXO@2_F0P*G53t{>XA&dc=3R< zaEd)lAA;Ka3T1tSV=s#KaV{W6FB9OxeN*B-zcX^8hM5sBTMtVWq_03iKdIUUzN zbZs7IJU#ZTEEbPy{aGJq3GDIq@OkW6-9-8QB!(cwrtq$Boukj6ut^B)ocr4DXYVW% zFC)fC!-3@;B;L2iz`Nk*sg5#}HbVaSFho$Q#GWo_NN=d}CmbX8Cvyg2%9ai1v-OX| zp%fP(a4$Fmr)=2PEV#3f)^FS9+cv853_lQt*58-Mu-ZVG6XsTOJ?PZ@ws6nOHCs@s zz^Ch=%H$fJ`9t7@+ixhz7T9M2;DoyjKy^VG^+Qn)wg_AnYLL&wERUfH3a!qQR)bNM zMq6U;d;TCk4IB{q7RwiG2hze-Io;{cNlg5{827adJf>Xc0P{g^qmTQuuZ2frCr|H( z?{8$%FbTA&2=;k&tht~F=>`#|Fh{w*x_}LRHagM1v;aPcVF^zsrI3i<5X7+30OrL^ zhec7+@!9P8SbYkM(Mm7Mq^ZipqMmv(n@U;@HEh$uJY1&+Vl9rPt`X*?3)_#{P)b9F z`sil{7SE)&ihq=ra@TCpp6;}l}4eFd5dOL z0^)p339Te66AnDQPAvBuE;n*GVE@4!g&|%>!HWV>p(BOG3?%Q6QBeaY&Dyly<%dP9 zFyOtGsSDg0Pwc8X`ii%p+^pZFKYw!Rg~$22n-WW?q>o4J$FtQyT`DUohhw(X(`|== z^D6-y@xtP6FB=rCGqmT8b0inT6pR?CJIxY-=QlA*2c)Y_i(}0M&mo2b_-6rk#VP}& z(5YA`jdTYPiP>Cv%z}+4=MpW}J4zgEMv{dsD1U~?wM`Ob7OuN>8MQ$8N>NGxVmZuK z5Z%ui21UuPUwfn2YEMeeV8$kVh`VoGNT(zR8zIxe5K<akx=L6It5;*TidZz+?zJh|Y5iX#i6N0G=&UJ-_ zZ3T7sFhLL&VyMU7hqMpQx|Q8AZ%YL4(-*@O5tuPvX{jgf_~-z@(46U4JP=mLl;nE= zv}XiWQ10#|*h-AzG^8@Q}DAe=+bSips`}!XyoPb8j?{F6}x_GHcskY1ca%Nc1+9 zhtX4vCIg(98AXpcK#V*WbsT%OXuEk;ngOkeV+f=1sK}`HFK91JfW5VA1mHL#8>7su z23|uu&I?C|piS720}f4dPCQk8c2j2qAR=TBj#ydS0P|wXhKdzy9zG|)C&6b>ma!n3 z;dv+iZsv8nRAig!)Galkcn7kNCTR=ifAtlICfNrm7ERrO2;NoXa*PDvgZ)p z@!pqM&9Z*P#uPnC9^U0^Fi}X75k(nxB>u*B(|po<@13>1YOKaO?1sg4r#-9?*8by> z6m;p-l52eXvL(XB?WG04E9p>tsJo}5d}Z-HUAp+pH{P#)sXcQaDkhj@xcE>c_59hn z@kB-vNGJO+G%4EhS$9dB^p-a&NIn5(g(9P7IeBJ_5@qizVx6iE75qRf9;LZFJ90jo z77DQ@xp7Ui{{>YRed3fSG7+{zyzoI)m4vSf@sM&eiX|CYjBEl>p1=~;h<1Qhop(y( zEA`f%50I2KUwu9UtHXnkvs*!~Lj^B(jiU%Xyl=zJzmepyNwW5pklZP?b`FCx)uB(R zSSGWvb5_9?B)+{)jE~8kixsHbg2d~IFiSggWNpK=&rGIb#xr;2(dUt%hB|dsq(ekZ zT(QERDLO)#?SrDhmt!wTD+c5R@>uqQ|JzB$=CF^?A;G|W5x~Isf%$` zU{yL;*br8vQWzBhEdf2!E}3(+RY7}U$#iASwgQkZl>a#}-4vWs{-kH|R{U8gRIuRU z@dK8suHUKT`}IWojKE3zOpedf$K#Cy6IhYc_hFsM>;iK`Nn95~JUn!4vv0e)U~+BK zw~=YzT&iGKk?>eGd6uE;rk&J=p#^`k03sCPvSLtMxbFyC@!qa(9=%<|jK5jv{!|}Q z^{CXX&|gy1Y1CAU-O94JY}Kf&(ZLgCIuEmFVyRN{27Q;}KHlQgH@D6d8PDj%2jLm7 zS})3GxJfZ+jG>RGn>BY->vp7Z#U|xVpc>9yXQs52=(%;y(s1~((h!8@>#SAi0%~o( zWZqNYU7bfL;?s}_cSbCDVTXw<(5zONosg_`SULJmGatrnX*XE{YqQ5TAs=BlG&Evm zD~$9yRK{F&OS;NEUZ{}$mnvY-c>m0`vJvq|9wKi-WgMd@sC7fz^AKAD>2_kA^f}wp z5Tyda5m9zz>AV^h`Oc1gSjjV*0G8%>1>KiF5)jNmbZQw9goM|9@(Ar%S)hm@^jANeHWt31j%v9Qb6R1MG zeGM8`i|imnKh@=o+z-(Ov!ZnDF}l64_R|tn!xtC}i2cETCR2$m%`y-808n(YUro~Y zDozqPLU+=a5;VGM7s^? zbQn^by#;u=#8NL*QJ%fVW0&e4t#chRuz7>DtoWn09I5F_hWrC> z@ZKMuf!OKt?(nz481zy`%O-p9{8p)-^T{nzC1HOIFsZT ztR@mB8j|gJtrz$ZXPufyo+HIHXvEZWP^|K@+6He&^MrqxrD{j_gu%(9+p%O6B%**{ zl7@XD@=HFHF~L_w1yJa8c(w6YOE0=eF}UYx_Bw*Fvzb|N=HWpsa_4Lou48j@F^mU4 zPJHy8eD#OI7i$yTEzWp+bH1sZk5Jy~8wzI`4$+>Y5e9fNT96Tg5ZWZ#m>+Xx1br3W zxsv{n{CgQVOl2?Tg#ZIv{sIOj`ST!blJUH8hBNJ((@wVm^I& z`AqV?2f}i9Fd!}|FosvvvmmaN7g?jNs<5K4q;NXHUPdd-E=;`Lc{<_JQ>?#k?q`iB zUpC+&6fpc7Spwd`p)cFzTFxy#c>UG7RN0ajIa!2>Zoh<946^+O{J{ z58J84sUzv362cqS)U0}=ap>gR7(z>i!6lU3c~h&`kCCS1;gCGPvn`ioc5C5u(VdMbc5 zZ36;3=!4fgBTSJ5%WTdfC8Xq7oC|1=t+NhRRFRwS_f<+PZn_EH+G>+(CkwJJ5Mr@r z7WUu}Egxu#(EP@Mezg@ZeKnn&o5hd@51((Au-w4R4q0L@S+(S4K3UOahgm2KwMnT| zNG!hz@}sz`Ejw1!zvn@JfOKA3=Ta{^f9RF;eo7s7xkvX(INwS;gwkQqoYJghoCc5n zQ1y%u@XqP;4*`S(j|vWFms!M0>gUt(c?x5{PAdJ|!Gv%uKSyA|z``+rLLm|W8&Aw5 zEdO3ppbiyeSSVPaW@$!(sS65t^lTGZK~b16fqYEUy0M2mG#A@O`r4PvAz8hvEyGz( zO`m$$b?-n~bkVLw!@7dU!Cbq@+ip)F@B%}e|h015h6XnB~JeJ1JA8)-~|pq?d3%ilY94Uj2FLaA^-ax0RIQ#S?-0h z(o21ymLdyPxvI!U`#8Xc;5Z@#7_yccnr~NYb?&Yt>O#1MW_i_knYfIm7ft=JtUX|P zRX;q;&bDol_U1q3?%GL}2?!T%uNB!npQwA|7dJr5l)^T;RUd4`jVzvY;?PQUjB4}4 zfW{@8Roz_v=FRD*FIV&eNcrg(-t1J(afBbaEC_NsvmFXxF|oEEr|RcGi1T>+9bz8W z1pRz%;&ffMx7cLxaohtX-@U+$)GD_t>gRN4CYj%M63yrBHEoNF3Hzw+bhnHpKc9=w zSF{1sBnCGi{#KQig1cxN{0z=IxQ(vu0kl#`>(Stu+cew^7^pe~=&iAKUL`K3*>^y6 zp5KF(>NeHkZ1B7ieVrgGSD!Pko-U;HRT*YAdu@p-C0#w@k0Cy17={6z5RWSE-H>yTAB+tEnZIlhMV%G`ZaB}9Esd%RwLtSPeogUL=fTF8ST}0f$k)cXCg1HUq+_j zc(vX&tCyE}-K;m~iI$r5mzJ&ZlW%WMRu_SP69RC#%tELXiE&}iuByUJ9KSOT=bsUV(`-@(gVqTEd`7_ zHr&W&KXZ9pkMaQOSc8pI3lz;eZ=_*#Y5hEKjquYy6#HE~B?s=|4f~?u{YUyw*E40? zpqH#AI|>y6r!76EuS|Q-=pT8z@t46ud!lhS?{_M{SUcZmJSF-iy6Ecs$%bl@6C*A- zA6Fp@J1TZL{)DEkKyel=;l<&v09%O3a|f5qzj{N~N_(pH>wWuU_eW!3=gXqW&x&32 zZM3#0$S3Q-ILekd!jdH@$Ek^P5eS6|E7FG&945a&K*xSJt0nv5x)npIE$%|uf&ks%z3Uh${( zDTo$aIIm_Hpx)BHeoAig&s)O^Nctvid5^4&r)f^>H37NhpLk&Kp3Zy4cZNjx2xC@f zr4KC-fHXn{WUo;W9*v&}AcXx2CrPTZKL#J8|N`R+fc%FYCTVr|x;#h^Y| z9x+?QVki23GaD*9{NL84`?^;WW!o-jDPsDbV4i7caLj690*rjt zjC+2fM5GMF1zB$?v2K!O-i!pdrpTXD75lsN1Nt2k%{zX8zZYQfD3Q2VyEf{m^BrPvm~_?cii$Pcc=A4PYI%NYf1lg$#X z_9TqmhkHO%pdQy2d@>=~6|wb*gsDF&CLU}F=Z~mnBENV#P{SjjjPDWRjg1PgvqBzs zG&MW*s={ZC%?V|9MAn+jyBiv|s!}A80^G&JF*;-oWuPM_<~5hHwXTj5qbiA@D*+!% z>vgh3Qa!P|_p3aD-FluTk=|~t5EJYmU>seH4p4QJ4mho4joQ@Akw0Cu zg(j!B9C@`K>3VvMD(;nsTyl7aa4^d-_$i@fdHIX;EMH`ECP!&4x;$y4g|hZ~0hD${ zo&kh1ir8kCqcV$TI;TF>9eFF7zdi^#-*mYiUu{VW=G^c}o&sh<6^k7F&3GZSu;Ti@ zZDIIin%|;n>Phr11)0~{yb)xUs<@oL171z6neu}3l3Z5#!+B*#-lRe71>fxOV-u-J z%teDrM7kJ0dfc$>bl^FQu$rFl0SXZ{2aCTyDU@?i2HY^ABM`L74NI!JrRxD$t7Ree~2{Ii4_|Xg8|gKCXGXqBj9q&hBXq1(?r981_RVN$FHeXeu3FFKphDsUB246yYI4HU>GP4(sNyy^y=kWS_z4rwH}P1m<) ztUBeps7tNC^0bwW;rB&CX=j7s)f~gMj1gmv7B67JO81xr&nb0yTJ~^jLSy0=o-xQi z2rhBW{NU_Mndm^;N;|cIpH_TfBcgYoVE=yLtBS%AmHpjOK12V1FP3@HL;wS&1tZLn z&l%2+>I>66(jHcvFT9S3F8Huh}b2fRB$iq`VL;ES5u!L6ry~JLOU(9eP@dg#_)CI19lL z2lUXo8?!II;@CyA}ha3f;>b2`MEyN{s`DR=0WE6@Ziqpy4R>4;E8~zIrr4 zje`9|J3|84YyGyWq-#zMq z-$h}ggr>w)Gak4`H~9@H+%gu0Jv@quUzC!xC0DFspk|A~02PM_ZsI*0p74jN0NZ^U zq_`k9?h7YNIf!YoC?!17^v^&?BFNBk-sBs*HYH-lD*{f(F)(V%pGBx+ILyQW4$yGJ zlxfZ}En-4kN4O5c2eE_;ZJ2r3`~t!%aWfySN z+E3C$^pLMuH*zCCY&ynvGyw{CA%F;D+;qCDTdgi5mr@m)-viG0mF=a7DvbCIdpQ)b zgKqBnClRDxRcpWBPY3t}w@WnL^$N`Gb?!V_1x|I!^T3Ue%!kNEeZbF8f}gg1B?~}aZyr*cP9Xba zvMApthQy@;Y4P#x7;d#45h1yMvt7Z1_UiuFjb-u--C($9novJ0K36GCzt^35=(-#r zG}w&9(3fa0V)~f)DR8!C`&^ctW5Z8a8Z$?rTIyl(1>`&w=&H*oAW@)f86RfZa574d z_EnbCPW~ns?~?wjq(6XUPDzBJEamPSO6(NrkU5KJJ>pSW*ri0?I|awrK`*#M@O$`I zbS$=#Ug6i-y=ajdjrwTn<-N!~)*9uUJ-T6t909~86^R430&x!E6hE;`B32k>k78=J zMXx^ssxZ=*&^1r>h2=8PEfL&$=kpBiuF-+^Usg6a4%&p5Nsl-w{Ju}qqw!7<*3jP3 z1I2Ps;ToX!yCtyCUL5}IyA%>ts_lP$xBBl=%moZgp#!ERq62J{7L;-Q6<6j|cySL3 zCJ`dkG>wwMgOtS`B^yn_fq3XbDRYxaHI#NzfABs~dtRVoD8gnwX!d=Up{X-5fz>D7 z{5MBksZT3Q@ws5Kp7i9gJb1x>SOUD@5Lm^(jTh-9)qOLTkc_>&-I~_?3#jOld1U zi}%LKFaqC#iw=Nt%N6<78BPUKUUHt?(!B3`5X6V!lxfgtwO{=mX5l1=9@TF#VKI71 z1e0=+ymef^hZlIkLW4EJ(%oS9(jyabRB^%46lOuVUZUB`5L>4J;yE-H#LaQ=e$5o5 z;VR!grkVXczfx#Y5n++5mBE4rQ16(cM^7ZOVT}n1N`?g;H`w68j{c1l! zMtA0w(JCMgNr%(7asQ>{KrhhE#C;?uMua9!INg}8#+oqo2hIEQ!>3z)oeN@Nx2{w$ zp%C_BM;Km+h$T@tg7paQ;-qlzBV#^JSpD=%ivmyRH9Gr;`jcpLYR_}unf0^jwsxLn zX9uM1Xqa?L+KSyT=`RJQ#?sXWyXPMc_y~_4IO+<%kG&$8kn{p=S9yoBbu9 z$c^%WP~vD2lHb;%AxcWzX`<9KMy4S2LrKkh|r%%SK2AI9+_K1r-d(HK7NXm&JDu+%8&6Vp%>lw^5Sx` z&F6A0a56o4BP8SxUbeqRT9cPd^`j(FL?_IJR((D#yx5ow3CYZ=V*Xo+qgIZ=yyJHe zWEl5zCSrTDz(=AUvPULh+5B|F-X-0*&nCR(B&pG))4gq+S?^L{C6m^Ug~HlUjM;R> zR;%i!)ji}h<%V0-{qB5oZNG=%fF>Q*<0?R8M;VFFjK=qTW^CeBKaLn!oPHHlg@v`D>!)n_BZHiI+`8vp_`P8 zh%?6Jb;eCRPK^78Xku7y=~?vyj~)?Yt2p;&Ls9I7H^)e01WBHyE-LOKZT?qszBBDP z;_&Ai0iRC+G3#U&K#0wILW0M>Fq7yES@b%*pO0_T4EMsNz4-ZI+^dse6MF8-Baa%( zs-*mQ<{*s!y$$hw8CM~BSjJFMF@3pcJa#sdpx83AfV*1f9@nYSHzI_`1J*q+?+1KJ z-ZM|gmj#y5GMWjath-laM2t3Q8l@KFabT>;;ewsGonRR*K%1qPJW-C=co(6>UvxuI zQZ_6&*`kS%>>UgE$r(0n>&v&tPHIzcJS29r80*RWidm3@UG2smlx0-wt&H1Q4L-zzijyWJrGXqc`7=rB9e&ATf!YL3v(FCJAD zhNUG-qM}m3XX#HkwW9&Dns&;$w+;fJ6u0?Tqj99!S#sVBV;owreim>LyKl$8Zu{(X zc{P6T_4|XE7vhEhH>p?mVDi<$q(3N7qMM(yzNl4Zohn5k85g=GxpKfROuMIU#z?(O zY>Zh|t;Tm7U`;IjS~(*ARrryiQoeOiU&JrJ z87;aC#(S;_#P+{&R*ENe5SFnViQk1%h;F{~W$yd>xxh7Nal=zr%JME#c5}^(W;1cR^SKC#p!eB0SXTO({8a#ZjmKzeQvdEwnlUMGnl8*POdK7 zHD^${)^m-zOj09o-~?uS==c=?@V`_L?^#S=0o=zgU+#R-DFs6{aw1`?-J;y#tnAR$ z(8h>1$zn0uHB22-Pt6iR0`oD8$Y?X}kq)zpF^99Fw+NFGZ;Z^V9}{QcyhEz%IcHRB z6l0y$Ke$>Mx4`vTVh_O0t%SnD(FDV#60<UddcqI0J zvepb(mO(IoHw&GAGXbpsBm^bq&=`KI?KsRUV}2;oVj{ToZK|_la1kuqRf2# zvy@ft0%ydAF~4ixXx;>UWVeh+|6t_v+bLz4yB>#zY}PC1S9&K#tfqgG7FY82z>1}! z=xqtdW+kGF?YKeroVW(vYLDjEBphm#&ToVaX#1XZ+LoW5svSZl)fwIZa-!w-UPR-v z42$@Athp57beOu}W#BmGzgg_V(g$XVHrC0}P`x2-m%2nv(ss6G;z zboP4bA>Xx`A7DMlk3`2Sdd!(DlalC3eS_=x*MYc>L?|Y^;WT33Hzgdn> z$?VIe3>Q_WAKqc+K5*o+=j5T63dA0?I;NW8(F&Fu4>j==hW-9V(kM>$8|U9E)S<}| zH83_O1nzTpcx51#fUBbsKerkJKD4+A#{wM;Y?$tE;U^;i!Bc&y*^vI88wiA?NCXS! z|B5p9BMD42;1{d7dh!=$*syR0o$^s)lt@3J337H6$s_GjP zyGp+;LED?F$8Eh09|xZeZyUX{-lz2RNqJQ#iglE2DbC+}kwoR9K z>HQGAoLh&oIre=!{>r&)J+uC>i?2SlCXbSN%5Te3X`b(bIH$g3wF%o2_}{anEn>4*E*1qM6TCfLh`lQ{D`%wW85cuDZ52)5wptE(o3LiLM zJsY)8oU>bhVew{mEsabS!2OvkXdJ2*vAE~IH>-x^VD z;>JUR?A#LL6xpnY$yH)oSrls6ek?~hPQs4>f^K&e=`$l}cMP+8?D-bj#>?KYiR!#Uk zLb$O*1f4CSwa9Fn5YQ7<%hHSE_$zH}W&!I4=8NMA(fp$mxPAVyt~M;$_12H;Se5vI z7WTzurk#ck_p9}zRCeZ9EqgYP3=M-@qcS?{W}VJT%}ggtEW=TDIurWND!#EF>iBNm zYj7Ex!$x|mF&+~2priE0=*wI(woOelHtjZ6bh}FH=nMw3$_gILD%ndrGiKYhaYlWJ z94K=d%ry;?O}l1_$)Q1lI!KP3J*G(jW{Y;VO68h*PXHzj3c-3qa3UQ?lOX;evvw|v zf+%f8N)Ap;x}F&Jw`&7TM3@7)G=0kAi?mI*L~J=ugl7B>-WJ`tW>Hubdh#iA%WOQP z@teUszHQ7MNCEJ3}o53@<~T zhh;V{0V=kN4nAPw(IHZJQBpEMhsSo^*vsWk7#boFJrT1K3FYvhDM=(I3&Kz{2a*^ zT?en?=cXDQJCkm)zTjjAplzxF>yzml%=)lc=ru+?Tiu&mqLhY_(3|9^BHA=*Nmyr| z!Na1*O@GK`dr$yDn~SB>r|NX)<}Qs`(F&P-E>sH-N%xVU({!2(qPF0L!imffsK$3h;{ ztR3{0hzDz|(w*TF*3D%Us3c|0IC_@beio%|A`jGsLE^0}_zZ>$!|9oiXEQ?Ow9j`+ zgWzG+wXtj8GoKfJ#!|AM9~UfgnK+F9-Ru|fHa(&cd2VHTML7Qx;f1b_U+nO2#GaJe z1<&68+@lMm55gS)Sk^=wL$JdkWJGWje6Y|A7mEfWEqnJlwhq?;Zduu?=j;B|5%kUg z3}VmEUZY=jeh!XS@x29q4rQbB&j=%w`U$==&-R+$J2PB%wt3?OJzjCAXz z${j)S59-a?w18LyZB(bIJ5Cc&-CxRO^()<};pvsfN>17Hu(JE(2cpq%o~Ay~2ggT& z=k8e`2@Wd(tTunlDz&fd9_UP{l}7aYp*vll)t~Lf^fj`q@1DFs2mgNcf=#dKdVqkS z)ztrnfG9|2#XXD{%i*tb+8g+lvRV>yy(OItv(tcxyGkvT+nUMeVu-C|#2gdm$B;QJ z?CiY3X3$qs+h+ABY^KQcUaMcN6a?1ilaAczOq_bUfGBb+8-*$)YN-sn4DHRB;b;%2 zgR7yxK|^(XSA?yK+X?92fm-W78K#4e@&w^y_1x&lZa7?015RwXVFlS&=C~V?(3#Ph z8+-NUA=-$yv<>QwW|BwL&{bEyvER8~kW@@X$4ZKQINc022q1R43Dmly7&0ehk^F&Z zU20)507`x2X`&>{(UV{(u?FrfxdF%ht=H^8tEbXlDI}OErF%&h4^Kzl(=n5_*Q|HP ze#{s4)Z)EezvDuw*RwMsRi^i@v$&z^I1v=MC#NpQys8=AG7TLe?^y;waL2JlKK8gk zEgr^Y61?TXZbzM~W4h2+$c{c?tUtT#HAN4ufa1bW_Rz{x_T|^%`1C6fO-NXeEV3U? zPH__{ymQm}^E(P|ixv8uD%}woo5$(s3)$|GV~?FN?GC_4Ln zSe2t*mq92kVge2vm-v7T%rX7=A!kZ7z`usGd~(`7N}#k)5fVXTzTGj0JZ-%ov1a;F zBvCI78R_nj%!%vA>7X}(t%$<#5g$eeg3Kf5-n?agu$A%Tx2+9TxPaJ?^!Hcnpb8kIDF>56L=I#Sji0FnTcPp{%TUc#PnQB{c7+iPta}T-bVl)3t!KTfd3rrEo6Gr-r%;+a%7Beb+hl|-hv&7N>k~E0d64bDhuIU zbI_TG&67|`yE{+GB37kP7E)O(Y_bc>oF~TP7e~->OVOHa02YNs7KEWMN~Sx?R&60(R=&E zycejkFvUBN`?Lb=&BGVZPqXZ(m*D-b63S=KhfLS`jg{S;W@t3#u+_&ae_oDyM5`J4 z3lQqc)ERi?<~ju6RO=pnx@bGbq{=Uuu>v~?7sF|5b0L1I4@GN301EJHZ8vVN3dae> zCvo?V6r|PH=Q`oGIWh_s(UX7Ey_Lhqm}5_$=dX$KcZN?Vx?9K2++5`IgqO|U$l2pm z%!1?PZ0wd#GmK*aHH5_P>*@a_`AwD0vFV({rFcgjZHB$K`yMV%NLkXlj>%|ovHT5d zp%ka0o{gK`0zZuyy6M5+VT`R`hOa zYz7Jpt{6Jf^_t>31Ojqb59m;5olUX+&=t=#;v}jt*#*q;d9d-tiaFAJZD|GivQ9Zt z2Ak4+UgTH%c8gQlu9P>$l`mjdds0?RLUB7ImLx(-E4P(RfZUxDf)~h+M1T_4J;|Bo z2mRJC$-)&+(g*r)0qHt>Q;^n~&C9k7@XW~@qFXA#mbFBL4U`kg$e9gorFMGpKygMW zHZ2s2)jn%oneixMZ(pD#Tf4B3w~!ayy&RtX4!-P--fQ#9;|(o<^7EdrZjeRhkF{SG?Mv?PMjk zV6e8k9R#w3vXG1nQ2H?0I){iRPE6XHCw(R)N$-@IP@VbhVf6|@1l>(}Jze?x;>{Zx zSG0GS?IfbI*k0x}bod!#nUfZc%_0~G{fV2l zJ4B?5GZP_n3cgN7IwGc*X6(>}StoLG*_N^WWAjU@+q|4`6CU}ZQUMsbr5D*o$hLntXI@Cz<3^h#i;LU=JCSMHB#=>;w$N4o@KH? zw7Vf3bnzC&8D-N#6n~J?(ukE^Gt3^D93xRz5nHoqwswB}a^;ciOM2kI#3qxn8W6&A zu_IQX35`G(Rr=Zjqeb=0?1sK)dpE5Tv=@-Cf6mbJoGN55rBsc~lM+Hsmocdr?eF;a%a!i zJM(1L<5mBScxCRnJ$8G~;DK>RR_IXcbmR~3dx%ny9QrnLfeAOQk;DpE8l|ZV*>4ta zgf0&0-s~PR2Xg+MQY)}dYL?%8Ay3bXfT*R}N1A~xISx~?WS9mdD1Uqf!dXC+)VTv-KRp2(tX*lewzJI#ExR50J%d5^C9J}M3Z4b=)wxs>#CDdEFESpRQ zkENXeAZ7mk1WZG3nTgr_-jjUjK%l6Yk!d3G?IhJQAI4x&Nvkj2yxT|S)#i`^px@X~ z72i^VL&YwjlD6ncMO?B8HvQ-L5uE2zx99|-;SX$1Gjw0E>b&MdAk|kFiX!aUT5#e| zU)H}U+rQJl6FePD34ypr$T+? zFq@$>*=KpRr-0z)us0O22C1`F09=AyxS8n+_4oxObxK>smc(@=%3O_bqK|v56j>#; zO7zR`^5*h7hD5NO;e*9(IT3a4q{EG*`;Eqvcvb0d3v*(7Ah--^&MAK`u;0V}x|U5ny4X=gK!M70*37MAIA$El3EZ^8j-^DVN0D*RbfP#rr2b!-R ziayL93^C3KYy#8VEUOYT2W2v%zb%t-rIuJSgAq|JXG8$CLGC!0zQsZAT*x74bTwPt zNTtPj@8a<5N)&nT*e_bnU#WM(p13v`Vw1iQh$e^P_tPe-|A3mxm{35EbV+bV-+G-E{^5e6ZF9ET4l$EM^U> z|Fi`^wYzm-U%BDthPVr@&lGeM-%D5ug+DML>kW!vXsLb~giYL2BR` zKwA(Ccn>fK#0_x<2232r0$zdWAPB+#A(g=Kf#pM}KDTz~6&(5SeiQD?$b$ji3T`hwvfJaQ=g$z{UP+iw2Hh4g!YpA69XM2ZDg(A2Mfz3&KzOAF@sw@vjk7AnYhU z1ghLWqTEp=pt2k;&~TIv0$<^OMFJ3#>i>{l>O?@)F**n;oqt#z9ekk8Ulv0DAGT^t z2pk1?Ii?J54-^>Z2hRq2j~hbJSOPoE@POYfDS$QOs6groe26JqV4yuA@cV=a_zz(I z1V7<_`@+G%sQ%9Oe-DrYaL<9}?%9po;mAA}^W% z7&VPb_}@~J|7(RN{y*THxWCgmgG%_X-2K0XBuM%XxMqfm@PCq^rQl#-C4c9P`(HrX zpZ@{o#6bgVlF|Oh^8cntg=YVrW9q*E3F*+l&{=G#|2H|6_ZI+{@h`ycEEU1O(ggn+ z?f^(OCqei>apS$eSH=GCRr#N92mJTo&HazDjvV5D{^Wmy$^X||JPZC83=J$C!Uj^$ z<3o@)1EV{?0?p=`A%1uLB^m$mrKP_b@IK(}yg9^DKTv1E45ADK>|0NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto init - -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto init - -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:init -@rem Get command-line arguments, handling Windows variants - -if not "%OS%" == "Windows_NT" goto win9xME_args - -:win9xME_args -@rem Slurp the command line arguments. -set CMD_LINE_ARGS= -set _SKIP=2 - -:win9xME_args_slurp -if "x%~1" == "x" goto execute - -set CMD_LINE_ARGS=%* - -:execute -@rem Setup the command line - -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% - -:end -@rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega From 52ae34782d85b030567113950f8ba89749f52e8e Mon Sep 17 00:00:00 2001 From: Joel Dudley Date: Wed, 24 Jan 2018 16:41:49 +0000 Subject: [PATCH 28/36] Removes a reference to the bundled node webserver. --- docs/source/node-administration.rst | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/docs/source/node-administration.rst b/docs/source/node-administration.rst index fe9b9231fd..2fadfccef0 100644 --- a/docs/source/node-administration.rst +++ b/docs/source/node-administration.rst @@ -1,9 +1,8 @@ Node administration =================== -When a node is running, it exposes an RPC interface that lets you monitor it, -you can upload and download attachments, access a REST API and so on. A bundled -Jetty web server exposes the same interface over HTTP. +When a node is running, it exposes an RPC interface that lets you monitor it, upload and download attachments, and so +on. Logging ------- From 0fa6969d5d29675ab2d343dd42f4938cb2f04587 Mon Sep 17 00:00:00 2001 From: Shams Asari Date: Wed, 24 Jan 2018 18:07:29 +0000 Subject: [PATCH 29/36] Added various X509 utilities to remove some of the existing boilerplate. (#2416) --- .../core/identity/PartyAndCertificate.kt | 8 +- .../net/corda/core/internal/CertRole.kt | 9 -- .../core/crypto/X509NameConstraintsTest.kt | 84 +++++++++---------- .../core/identity/PartyAndCertificateTest.kt | 4 +- .../nodeapi/internal/KeyStoreConfigHelpers.kt | 2 +- .../nodeapi/internal/crypto/X509KeyStore.kt | 10 ++- .../nodeapi/internal/crypto/X509Utilities.kt | 56 +++++++++---- .../internal/crypto/X509UtilitiesTest.kt | 8 +- .../registration/NodeRegistrationTest.kt | 18 ++-- .../net/corda/node/internal/AbstractNode.kt | 3 +- .../protonwrapper/netty/AMQPChannelHandler.kt | 5 +- .../identity/InMemoryIdentityService.kt | 19 +++-- .../identity/PersistentIdentityService.kt | 18 ++-- .../net/corda/node/services/keys/KMSUtils.kt | 4 +- .../services/messaging/CoreBridgeManager.kt | 6 +- .../registration/NetworkRegistrationHelper.kt | 5 +- .../identity/InMemoryIdentityServiceTests.kt | 4 +- .../PersistentIdentityServiceTests.kt | 11 ++- .../net/corda/testing/core/TestUtils.kt | 2 +- .../testing/internal/TestNodeInfoBuilder.kt | 11 ++- 20 files changed, 156 insertions(+), 131 deletions(-) diff --git a/core/src/main/kotlin/net/corda/core/identity/PartyAndCertificate.kt b/core/src/main/kotlin/net/corda/core/identity/PartyAndCertificate.kt index 17794be789..7f01393eaf 100644 --- a/core/src/main/kotlin/net/corda/core/identity/PartyAndCertificate.kt +++ b/core/src/main/kotlin/net/corda/core/identity/PartyAndCertificate.kt @@ -1,6 +1,7 @@ package net.corda.core.identity import net.corda.core.internal.CertRole +import net.corda.core.internal.uncheckedCast import net.corda.core.serialization.CordaSerializable import java.security.PublicKey import java.security.cert.* @@ -45,15 +46,16 @@ class PartyAndCertificate(val certPath: CertPath) { // Apply Corda-specific validity rules to the chain. This only applies to chains with any roles present, so // an all-null chain is in theory valid. var parentRole: CertRole? = CertRole.extract(result.trustAnchor.trustedCert) - for (certIdx in (0 until certPath.certificates.size).reversed()) { - val certificate = certPath.certificates[certIdx] + val certChain: List = uncheckedCast(certPath.certificates) + for (certIdx in (0 until certChain.size).reversed()) { + val certificate = certChain[certIdx] val role = CertRole.extract(certificate) if (parentRole != null) { if (role == null) { throw CertPathValidatorException("Child certificate whose issuer includes a Corda role, must also specify Corda role") } if (!role.isValidParent(parentRole)) { - val certificateString = (certificate as? X509Certificate)?.subjectDN?.toString() ?: certificate.toString() + val certificateString = certificate.subjectDN.toString() throw CertPathValidatorException("The issuing certificate for $certificateString has role $parentRole, expected one of ${role.validParents}") } } diff --git a/core/src/main/kotlin/net/corda/core/internal/CertRole.kt b/core/src/main/kotlin/net/corda/core/internal/CertRole.kt index f14633c883..82f4a42deb 100644 --- a/core/src/main/kotlin/net/corda/core/internal/CertRole.kt +++ b/core/src/main/kotlin/net/corda/core/internal/CertRole.kt @@ -7,7 +7,6 @@ import org.bouncycastle.asn1.ASN1Integer import org.bouncycastle.asn1.ASN1Primitive import org.bouncycastle.asn1.DEROctetString import java.math.BigInteger -import java.security.cert.Certificate import java.security.cert.X509Certificate /** @@ -70,14 +69,6 @@ enum class CertRole(val validParents: NonEmptySet, val isIdentity: Bo */ fun getInstance(data: ByteArray): CertRole = getInstance(ASN1Integer.getInstance(data)) - /** - * Get a role from a certificate. - * - * @return the role if the extension is present, or null otherwise. - * @throws IllegalArgumentException if the extension is present but is invalid. - */ - fun extract(cert: Certificate): CertRole? = (cert as? X509Certificate)?.let { extract(it) } - /** * Get a role from a certificate. * diff --git a/core/src/test/kotlin/net/corda/core/crypto/X509NameConstraintsTest.kt b/core/src/test/kotlin/net/corda/core/crypto/X509NameConstraintsTest.kt index 3e51ad7b9f..42a221862e 100644 --- a/core/src/test/kotlin/net/corda/core/crypto/X509NameConstraintsTest.kt +++ b/core/src/test/kotlin/net/corda/core/crypto/X509NameConstraintsTest.kt @@ -1,7 +1,9 @@ package net.corda.core.crypto import net.corda.core.identity.CordaX500Name -import net.corda.nodeapi.internal.crypto.* +import net.corda.nodeapi.internal.crypto.CertificateType +import net.corda.nodeapi.internal.crypto.X509KeyStore +import net.corda.nodeapi.internal.crypto.X509Utilities import net.corda.testing.internal.createDevIntermediateCaCertPath import org.bouncycastle.asn1.x500.X500Name import org.bouncycastle.asn1.x509.GeneralName @@ -9,7 +11,6 @@ import org.bouncycastle.asn1.x509.GeneralSubtree import org.bouncycastle.asn1.x509.NameConstraints import org.bouncycastle.jce.provider.BouncyCastleProvider import org.junit.Test -import java.security.KeyStore import java.security.cert.CertPathValidator import java.security.cert.CertPathValidatorException import java.security.cert.PKIXParameters @@ -19,37 +20,32 @@ import kotlin.test.assertTrue class X509NameConstraintsTest { - private fun makeKeyStores(subjectName: X500Name, nameConstraints: NameConstraints): Pair { + private fun makeKeyStores(subjectName: X500Name, nameConstraints: NameConstraints): Pair { val (rootCa, intermediateCa) = createDevIntermediateCaCertPath() - val nodeCaKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) - val nodeCaCert = X509Utilities.createCertificate( - CertificateType.NODE_CA, - intermediateCa.certificate, - intermediateCa.keyPair, - CordaX500Name("Corda Client CA", "R3 Ltd", "London", "GB").x500Principal, - nodeCaKeyPair.public, - nameConstraints = nameConstraints) - val keyPass = "password" - val trustStore = KeyStore.getInstance(KEYSTORE_TYPE) - trustStore.load(null, keyPass.toCharArray()) - trustStore.addOrReplaceCertificate(X509Utilities.CORDA_ROOT_CA, rootCa.certificate) + val trustStore = X509KeyStore("password").apply { + setCertificate(X509Utilities.CORDA_ROOT_CA, rootCa.certificate) + } - val tlsKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) - val tlsCert = X509Utilities.createCertificate( - CertificateType.TLS, - nodeCaCert, - nodeCaKeyPair, - X500Principal(subjectName.encoded), - tlsKeyPair.public) + val keyStore = X509KeyStore("password").apply { + val nodeCaKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) + val nodeCaCert = X509Utilities.createCertificate( + CertificateType.NODE_CA, + intermediateCa.certificate, + intermediateCa.keyPair, + CordaX500Name("Corda Client CA", "R3 Ltd", "London", "GB").x500Principal, + nodeCaKeyPair.public, + nameConstraints = nameConstraints) + val tlsKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) + val tlsCert = X509Utilities.createCertificate( + CertificateType.TLS, + nodeCaCert, + nodeCaKeyPair, + X500Principal(subjectName.encoded), + tlsKeyPair.public) + setPrivateKey(X509Utilities.CORDA_CLIENT_TLS, tlsKeyPair.private, listOf(tlsCert, nodeCaCert, intermediateCa.certificate, rootCa.certificate)) + } - val keyStore = KeyStore.getInstance(KEYSTORE_TYPE) - keyStore.load(null, keyPass.toCharArray()) - keyStore.addOrReplaceKey( - X509Utilities.CORDA_CLIENT_TLS, - tlsKeyPair.private, - keyPass.toCharArray(), - arrayOf(tlsCert, nodeCaCert, intermediateCa.certificate, rootCa.certificate)) return Pair(keyStore, trustStore) } @@ -60,30 +56,29 @@ class X509NameConstraintsTest { val nameConstraints = NameConstraints(acceptableNames, arrayOf()) val pathValidator = CertPathValidator.getInstance("PKIX") - val certFactory = X509CertificateFactory() assertFailsWith(CertPathValidatorException::class) { val (keystore, trustStore) = makeKeyStores(X500Name("CN=Bank B"), nameConstraints) - val params = PKIXParameters(trustStore) + val params = PKIXParameters(trustStore.internal) params.isRevocationEnabled = false - val certPath = certFactory.generateCertPath(keystore.getCertificateChain(X509Utilities.CORDA_CLIENT_TLS).asList()) + val certPath = X509Utilities.buildCertPath(keystore.getCertificateChain(X509Utilities.CORDA_CLIENT_TLS)) pathValidator.validate(certPath, params) } assertTrue { val (keystore, trustStore) = makeKeyStores(X500Name("CN=Bank A TLS, O=Bank A"), nameConstraints) - val params = PKIXParameters(trustStore) + val params = PKIXParameters(trustStore.internal) params.isRevocationEnabled = false - val certPath = certFactory.generateCertPath(keystore.getCertificateChain(X509Utilities.CORDA_CLIENT_TLS).asList()) + val certPath = X509Utilities.buildCertPath(keystore.getCertificateChain(X509Utilities.CORDA_CLIENT_TLS)) pathValidator.validate(certPath, params) true } assertTrue { val (keystore, trustStore) = makeKeyStores(X500Name("CN=Bank A"), nameConstraints) - val params = PKIXParameters(trustStore) + val params = PKIXParameters(trustStore.internal) params.isRevocationEnabled = false - val certPath = certFactory.generateCertPath(keystore.getCertificateChain(X509Utilities.CORDA_CLIENT_TLS).asList()) + val certPath = X509Utilities.buildCertPath(keystore.getCertificateChain(X509Utilities.CORDA_CLIENT_TLS)) pathValidator.validate(certPath, params) true } @@ -95,40 +90,39 @@ class X509NameConstraintsTest { .map { GeneralSubtree(GeneralName(X500Name(it))) }.toTypedArray() val nameConstraints = NameConstraints(acceptableNames, arrayOf()) - val certFactory = X509CertificateFactory().delegate Crypto.ECDSA_SECP256R1_SHA256 val pathValidator = CertPathValidator.getInstance("PKIX", BouncyCastleProvider.PROVIDER_NAME) assertFailsWith(CertPathValidatorException::class) { val (keystore, trustStore) = makeKeyStores(X500Name("CN=Bank A"), nameConstraints) - val params = PKIXParameters(trustStore) + val params = PKIXParameters(trustStore.internal) params.isRevocationEnabled = false - val certPath = certFactory.generateCertPath(keystore.getCertificateChain(X509Utilities.CORDA_CLIENT_TLS).asList()) + val certPath = X509Utilities.buildCertPath(keystore.getCertificateChain(X509Utilities.CORDA_CLIENT_TLS)) pathValidator.validate(certPath, params) } assertFailsWith(CertPathValidatorException::class) { val (keystore, trustStore) = makeKeyStores(X500Name("CN=Bank A, UID=12345"), nameConstraints) - val params = PKIXParameters(trustStore) + val params = PKIXParameters(trustStore.internal) params.isRevocationEnabled = false - val certPath = certFactory.generateCertPath(keystore.getCertificateChain(X509Utilities.CORDA_CLIENT_TLS).asList()) + val certPath = X509Utilities.buildCertPath(keystore.getCertificateChain(X509Utilities.CORDA_CLIENT_TLS)) pathValidator.validate(certPath, params) } assertTrue { val (keystore, trustStore) = makeKeyStores(X500Name("CN=Bank A TLS, UID=, E=me@email.com, C=GB"), nameConstraints) - val params = PKIXParameters(trustStore) + val params = PKIXParameters(trustStore.internal) params.isRevocationEnabled = false - val certPath = certFactory.generateCertPath(keystore.getCertificateChain(X509Utilities.CORDA_CLIENT_TLS).asList()) + val certPath = X509Utilities.buildCertPath(keystore.getCertificateChain(X509Utilities.CORDA_CLIENT_TLS)) pathValidator.validate(certPath, params) true } assertTrue { val (keystore, trustStore) = makeKeyStores(X500Name("O=Bank A, UID=, E=me@email.com, C=GB"), nameConstraints) - val params = PKIXParameters(trustStore) + val params = PKIXParameters(trustStore.internal) params.isRevocationEnabled = false - val certPath = certFactory.generateCertPath(keystore.getCertificateChain(X509Utilities.CORDA_CLIENT_TLS).asList()) + val certPath = X509Utilities.buildCertPath(keystore.getCertificateChain(X509Utilities.CORDA_CLIENT_TLS)) pathValidator.validate(certPath, params) true } diff --git a/core/src/test/kotlin/net/corda/core/identity/PartyAndCertificateTest.kt b/core/src/test/kotlin/net/corda/core/identity/PartyAndCertificateTest.kt index 39cdbbe8ba..f89f6a7829 100644 --- a/core/src/test/kotlin/net/corda/core/identity/PartyAndCertificateTest.kt +++ b/core/src/test/kotlin/net/corda/core/identity/PartyAndCertificateTest.kt @@ -5,8 +5,8 @@ import com.google.common.jimfs.Jimfs import net.corda.core.crypto.entropyToKeyPair import net.corda.core.serialization.deserialize import net.corda.core.serialization.serialize -import net.corda.nodeapi.internal.crypto.X509CertificateFactory import net.corda.nodeapi.internal.crypto.X509KeyStore +import net.corda.nodeapi.internal.crypto.X509Utilities import net.corda.testing.core.DEV_ROOT_CA import net.corda.testing.core.SerializationEnvironmentRule import net.corda.testing.core.getTestPartyAndCertificate @@ -23,7 +23,7 @@ class PartyAndCertificateTest { @Test fun `reject a path with no roles`() { - val path = X509CertificateFactory().generateCertPath(DEV_ROOT_CA.certificate) + val path = X509Utilities.buildCertPath(DEV_ROOT_CA.certificate) assertFailsWith { PartyAndCertificate(path) } } diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/KeyStoreConfigHelpers.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/KeyStoreConfigHelpers.kt index 2ecc461771..70c32ed6cd 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/KeyStoreConfigHelpers.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/KeyStoreConfigHelpers.kt @@ -62,7 +62,7 @@ fun X509KeyStore.storeLegalIdentity(alias: String, keyPair: KeyPair = Crypto.gen val identityCertPath = listOf(identityCert) + nodeCaCertPath setPrivateKey(alias, keyPair.private, identityCertPath) save() - return PartyAndCertificate(X509CertificateFactory().generateCertPath(identityCertPath)) + return PartyAndCertificate(X509Utilities.buildCertPath(identityCertPath)) } fun createDevNetworkMapCa(rootCa: CertificateAndKeyPair = DEV_ROOT_CA): CertificateAndKeyPair { diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/crypto/X509KeyStore.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/crypto/X509KeyStore.kt index 53eb020224..1fa56bebea 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/crypto/X509KeyStore.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/crypto/X509KeyStore.kt @@ -13,7 +13,13 @@ import java.security.cert.X509Certificate */ class X509KeyStore private constructor(val internal: KeyStore, private val storePassword: String, private val keyStoreFile: Path? = null) { /** Wrap an existing [KeyStore]. [save] is not supported. */ - constructor(internal: KeyStore, storePassword: String) : this(internal, storePassword, null) + constructor(keyStore: KeyStore, storePassword: String) : this(keyStore, storePassword, null) + + /** Create an empty [KeyStore] using the given password. [save] is not supported. */ + constructor(storePassword: String) : this( + KeyStore.getInstance(KEYSTORE_TYPE).apply { load(null, storePassword.toCharArray()) }, + storePassword + ) companion object { /** @@ -49,12 +55,10 @@ class X509KeyStore private constructor(val internal: KeyStore, private val store } fun setPrivateKey(alias: String, key: PrivateKey, certificates: List, keyPassword: String = storePassword) { - checkWritableToFile() internal.setKeyEntry(alias, key, keyPassword.toCharArray(), certificates.toTypedArray()) } fun setCertificate(alias: String, certificate: X509Certificate) { - checkWritableToFile() internal.setCertificateEntry(alias, certificate) } diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/crypto/X509Utilities.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/crypto/X509Utilities.kt index 959fc83a21..f950ccdc7b 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/crypto/X509Utilities.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/crypto/X509Utilities.kt @@ -6,6 +6,7 @@ import net.corda.core.crypto.SignatureScheme import net.corda.core.crypto.random63BitValue import net.corda.core.internal.CertRole import net.corda.core.internal.reader +import net.corda.core.internal.uncheckedCast import net.corda.core.internal.writer import net.corda.core.utilities.days import net.corda.core.utilities.millis @@ -93,15 +94,19 @@ object X509Utilities { return createCertificate(CertificateType.ROOT_CA, subject, keyPair, subject, keyPair.public, window) } - // TODO Provide an overload which takes in a List or a CertPath - @Throws(CertPathValidatorException::class) - fun validateCertificateChain(trustedRoot: X509Certificate, vararg certificates: Certificate) { + fun validateCertificateChain(trustedRoot: X509Certificate, vararg certificates: X509Certificate) { + validateCertificateChain(trustedRoot, certificates.asList()) + } + + fun validateCertificateChain(trustedRoot: X509Certificate, certificates: List) { require(certificates.isNotEmpty()) { "Certificate path must contain at least one certificate" } + validateCertPath(trustedRoot, buildCertPath(certificates)) + } + + fun validateCertPath(trustedRoot: X509Certificate, certPath: CertPath) { val params = PKIXParameters(setOf(TrustAnchor(trustedRoot, null))) params.isRevocationEnabled = false - val certPath = X509CertificateFactory().generateCertPath(*certificates) - val pathValidator = CertPathValidator.getInstance("PKIX") - pathValidator.validate(certPath, params) + CertPathValidator.getInstance("PKIX").validate(certPath, params) } /** @@ -265,6 +270,21 @@ object X509Utilities { fun createCertificateSigningRequest(subject: X500Principal, email: String, keyPair: KeyPair): PKCS10CertificationRequest { return createCertificateSigningRequest(subject, email, keyPair, DEFAULT_TLS_SIGNATURE_SCHEME) } + + fun buildCertPath(first: X509Certificate, remaining: List): CertPath { + val certificates = ArrayList(1 + remaining.size) + certificates += first + certificates += remaining + return buildCertPath(certificates) + } + + fun buildCertPath(vararg certificates: X509Certificate): CertPath { + return X509CertificateFactory().generateCertPath(*certificates) + } + + fun buildCertPath(certificates: List): CertPath { + return X509CertificateFactory().generateCertPath(certificates) + } } /** @@ -275,6 +295,16 @@ object X509Utilities { fun X509Certificate.toBc() = X509CertificateHolder(encoded) fun X509CertificateHolder.toJca(): X509Certificate = X509CertificateFactory().generateCertificate(encoded.inputStream()) +val CertPath.x509Certificates: List get() { + require(type == "X.509") { "Not an X.509 cert path: $this" } + // We're not mapping the list to avoid creating a new one. + return uncheckedCast(certificates) +} + +val Certificate.x509: X509Certificate get() = requireNotNull(this as? X509Certificate) { "Not an X.509 certificate: $this" } + +val Array.x509: List get() = map { it.x509 } + /** * Wraps a [CertificateFactory] to remove boilerplate. It's unclear whether [CertificateFactory] is threadsafe so best * so assume this class is not. @@ -282,19 +312,11 @@ fun X509CertificateHolder.toJca(): X509Certificate = X509CertificateFactory().ge class X509CertificateFactory { val delegate: CertificateFactory = CertificateFactory.getInstance("X.509") - fun generateCertificate(input: InputStream): X509Certificate { - return delegate.generateCertificate(input) as X509Certificate - } + fun generateCertificate(input: InputStream): X509Certificate = delegate.generateCertificate(input).x509 - // TODO X509Certificate - fun generateCertPath(certificates: List): CertPath { - return delegate.generateCertPath(certificates) - } + fun generateCertPath(vararg certificates: X509Certificate): CertPath = generateCertPath(certificates.asList()) - // TODO X509Certificate - fun generateCertPath(vararg certificates: Certificate): CertPath { - return delegate.generateCertPath(certificates.asList()) - } + fun generateCertPath(certificates: List): CertPath = delegate.generateCertPath(certificates) } enum class CertificateType(val keyUsage: KeyUsage, vararg val purposes: KeyPurposeId, val isCA: Boolean, val role: CertRole?) { diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/crypto/X509UtilitiesTest.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/crypto/X509UtilitiesTest.kt index 2f12970874..c37d6b6eb1 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/crypto/X509UtilitiesTest.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/crypto/X509UtilitiesTest.kt @@ -266,10 +266,10 @@ class X509UtilitiesTest { assertTrue(clientSocket.isConnected) // Double check hostname manually - val peerChain = clientSocket.session.peerCertificates - val peerX500Principal = (peerChain[0] as X509Certificate).subjectX500Principal + val peerChain = clientSocket.session.peerCertificates.x509 + val peerX500Principal = peerChain[0].subjectX500Principal assertEquals(MEGA_CORP.name.x500Principal, peerX500Principal) - X509Utilities.validateCertificateChain(rootCa.certificate, *peerChain) + X509Utilities.validateCertificateChain(rootCa.certificate, peerChain) val output = DataOutputStream(clientSocket.outputStream) output.writeUTF("Hello World") var timeout = 0 @@ -338,7 +338,7 @@ class X509UtilitiesTest { val rootCAKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) val rootCACert = X509Utilities.createSelfSignedCACertificate(ALICE_NAME.x500Principal, rootCAKey) val certificate = X509Utilities.createCertificate(CertificateType.TLS, rootCACert, rootCAKey, BOB_NAME.x500Principal, BOB.publicKey) - val expected = X509CertificateFactory().generateCertPath(certificate, rootCACert) + val expected = X509Utilities.buildCertPath(certificate, rootCACert) val serialized = expected.serialize(factory, context).bytes val actual: CertPath = serialized.deserialize(factory, context) assertEquals(expected, actual) diff --git a/node/src/integration-test/kotlin/net/corda/node/utilities/registration/NodeRegistrationTest.kt b/node/src/integration-test/kotlin/net/corda/node/utilities/registration/NodeRegistrationTest.kt index b9af17dcb9..ea71007414 100644 --- a/node/src/integration-test/kotlin/net/corda/node/utilities/registration/NodeRegistrationTest.kt +++ b/node/src/integration-test/kotlin/net/corda/node/utilities/registration/NodeRegistrationTest.kt @@ -12,25 +12,27 @@ import net.corda.finance.DOLLARS import net.corda.finance.flows.CashIssueAndPaymentFlow import net.corda.nodeapi.internal.crypto.CertificateAndKeyPair import net.corda.nodeapi.internal.crypto.CertificateType -import net.corda.nodeapi.internal.crypto.X509CertificateFactory import net.corda.nodeapi.internal.crypto.X509Utilities import net.corda.nodeapi.internal.crypto.X509Utilities.CORDA_CLIENT_CA import net.corda.nodeapi.internal.crypto.X509Utilities.CORDA_INTERMEDIATE_CA import net.corda.nodeapi.internal.crypto.X509Utilities.CORDA_ROOT_CA +import net.corda.testing.common.internal.testNetworkParameters import net.corda.testing.core.DEV_ROOT_CA import net.corda.testing.core.SerializationEnvironmentRule -import net.corda.testing.common.internal.testNetworkParameters +import net.corda.testing.core.singleIdentity import net.corda.testing.driver.PortAllocation import net.corda.testing.node.NotarySpec import net.corda.testing.node.internal.CompatibilityZoneParams import net.corda.testing.node.internal.internalDriver import net.corda.testing.node.internal.network.NetworkMapServer -import net.corda.testing.core.singleIdentity import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThatThrownBy import org.bouncycastle.pkcs.PKCS10CertificationRequest import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequest -import org.junit.* +import org.junit.After +import org.junit.Before +import org.junit.Rule +import org.junit.Test import java.io.ByteArrayOutputStream import java.io.InputStream import java.net.URL @@ -151,7 +153,7 @@ class RegistrationHandler(private val rootCertAndKeyPair: CertificateAndKeyPair) val (certPath, name) = createSignedClientCertificate( certificationRequest, rootCertAndKeyPair.keyPair, - arrayOf(rootCertAndKeyPair.certificate)) + listOf(rootCertAndKeyPair.certificate)) require(!name.organisation.contains("\\s".toRegex())) { "Whitespace in the organisation name not supported" } certPaths[name.organisation] = certPath return Response.ok(name.organisation).build() @@ -180,17 +182,17 @@ class RegistrationHandler(private val rootCertAndKeyPair: CertificateAndKeyPair) private fun createSignedClientCertificate(certificationRequest: PKCS10CertificationRequest, caKeyPair: KeyPair, - caCertPath: Array): Pair { + caCertPath: List): Pair { val request = JcaPKCS10CertificationRequest(certificationRequest) val name = CordaX500Name.parse(request.subject.toString()) val nodeCaCert = X509Utilities.createCertificate( CertificateType.NODE_CA, - caCertPath[0] as X509Certificate , + caCertPath[0], caKeyPair, name.x500Principal, request.publicKey, nameConstraints = null) - val certPath = X509CertificateFactory().generateCertPath(nodeCaCert, *caCertPath) + val certPath = X509Utilities.buildCertPath(nodeCaCert, caCertPath) return Pair(certPath, name) } } diff --git a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt index 899d6618e7..8b3e992ef1 100644 --- a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt +++ b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt @@ -58,7 +58,6 @@ import net.corda.node.shell.InteractiveShell import net.corda.node.utilities.AffinityExecutor import net.corda.nodeapi.internal.DevIdentityGenerator import net.corda.nodeapi.internal.SignedNodeInfo -import net.corda.nodeapi.internal.crypto.X509CertificateFactory import net.corda.nodeapi.internal.crypto.X509Utilities import net.corda.nodeapi.internal.network.NETWORK_PARAMS_FILE_NAME import net.corda.nodeapi.internal.network.NetworkParameters @@ -755,7 +754,7 @@ abstract class AbstractNode(val configuration: NodeConfiguration, throw ConfigurationException("The name '$singleName' for $id doesn't match what's in the key store: $subject") } - val certPath = X509CertificateFactory().generateCertPath(certificates) + val certPath = X509Utilities.buildCertPath(certificates) return Pair(PartyAndCertificate(certPath), keyPair) } diff --git a/node/src/main/kotlin/net/corda/node/internal/protonwrapper/netty/AMQPChannelHandler.kt b/node/src/main/kotlin/net/corda/node/internal/protonwrapper/netty/AMQPChannelHandler.kt index f31f9fbe88..115dcb1da8 100644 --- a/node/src/main/kotlin/net/corda/node/internal/protonwrapper/netty/AMQPChannelHandler.kt +++ b/node/src/main/kotlin/net/corda/node/internal/protonwrapper/netty/AMQPChannelHandler.kt @@ -14,6 +14,7 @@ import net.corda.node.internal.protonwrapper.engine.EventProcessor import net.corda.node.internal.protonwrapper.messages.ReceivedMessage import net.corda.node.internal.protonwrapper.messages.impl.ReceivedMessageImpl import net.corda.node.internal.protonwrapper.messages.impl.SendableMessageImpl +import net.corda.nodeapi.internal.crypto.x509 import org.apache.qpid.proton.engine.ProtonJTransport import org.apache.qpid.proton.engine.Transport import org.apache.qpid.proton.engine.impl.ProtocolTracer @@ -80,8 +81,8 @@ internal class AMQPChannelHandler(private val serverMode: Boolean, if (evt is SslHandshakeCompletionEvent) { if (evt.isSuccess) { val sslHandler = ctx.pipeline().get(SslHandler::class.java) - localCert = sslHandler.engine().session.localCertificates[0] as X509Certificate - remoteCert = sslHandler.engine().session.peerCertificates[0] as X509Certificate + localCert = sslHandler.engine().session.localCertificates[0].x509 + remoteCert = sslHandler.engine().session.peerCertificates[0].x509 try { val remoteX500Name = CordaX500Name.build(remoteCert.subjectX500Principal) require(allowedRemoteLegalNames == null || remoteX500Name in allowedRemoteLegalNames) diff --git a/node/src/main/kotlin/net/corda/node/services/identity/InMemoryIdentityService.kt b/node/src/main/kotlin/net/corda/node/services/identity/InMemoryIdentityService.kt index 4876533003..d016a56371 100644 --- a/node/src/main/kotlin/net/corda/node/services/identity/InMemoryIdentityService.kt +++ b/node/src/main/kotlin/net/corda/node/services/identity/InMemoryIdentityService.kt @@ -9,7 +9,8 @@ import net.corda.core.serialization.SingletonSerializeAsToken import net.corda.core.utilities.contextLogger import net.corda.core.utilities.trace import net.corda.node.services.api.IdentityServiceInternal -import net.corda.nodeapi.internal.crypto.X509CertificateFactory +import net.corda.nodeapi.internal.crypto.X509Utilities +import net.corda.nodeapi.internal.crypto.x509Certificates import java.security.InvalidAlgorithmParameterException import java.security.PublicKey import java.security.cert.* @@ -45,24 +46,24 @@ class InMemoryIdentityService(identities: Array, @Throws(CertificateExpiredException::class, CertificateNotYetValidException::class, InvalidAlgorithmParameterException::class) override fun verifyAndRegisterIdentity(identity: PartyAndCertificate): PartyAndCertificate? { // Validate the chain first, before we do anything clever with it + val identityCertChain = identity.certPath.x509Certificates try { identity.verify(trustAnchor) } catch (e: CertPathValidatorException) { log.warn("Certificate validation failed for ${identity.name} against trusted root ${trustAnchor.trustedCert.subjectX500Principal}.") log.warn("Certificate path :") - identity.certPath.certificates.reversed().forEachIndexed { index, certificate -> + identityCertChain.reversed().forEachIndexed { index, certificate -> val space = (0 until index).joinToString("") { " " } - log.warn("$space${(certificate as X509Certificate).subjectX500Principal}") + log.warn("$space${certificate.subjectX500Principal}") } throw e } // Ensure we record the first identity of the same name, first - val wellKnownCert: Certificate = identity.certPath.certificates.single { CertRole.extract(it)?.isWellKnown ?: false } + val wellKnownCert = identityCertChain.single { CertRole.extract(it)?.isWellKnown ?: false } if (wellKnownCert != identity.certificate) { - val certificates = identity.certPath.certificates - val idx = certificates.lastIndexOf(wellKnownCert) - val firstPath = X509CertificateFactory().generateCertPath(certificates.slice(idx until certificates.size)) + val idx = identityCertChain.lastIndexOf(wellKnownCert) + val firstPath = X509Utilities.buildCertPath(identityCertChain.slice(idx until identityCertChain.size)) verifyAndRegisterIdentity(PartyAndCertificate(firstPath)) } @@ -70,7 +71,7 @@ class InMemoryIdentityService(identities: Array, keyToParties[identity.owningKey] = identity // Always keep the first party we registered, as that's the well known identity principalToParties.computeIfAbsent(identity.name) { identity } - return keyToParties[identity.certPath.certificates[1].publicKey] + return keyToParties[identityCertChain[1].publicKey] } override fun certificateFromKey(owningKey: PublicKey): PartyAndCertificate? = keyToParties[owningKey] @@ -103,7 +104,7 @@ class InMemoryIdentityService(identities: Array, val results = LinkedHashSet() for ((x500name, partyAndCertificate) in principalToParties) { val party = partyAndCertificate.party - val components = listOf(x500name.commonName, x500name.organisationUnit, x500name.organisation, x500name.locality, x500name.state, x500name.country).filterNotNull() + val components = listOfNotNull(x500name.commonName, x500name.organisationUnit, x500name.organisation, x500name.locality, x500name.state, x500name.country) components.forEach { component -> if (exactMatch && component == query) { results += party diff --git a/node/src/main/kotlin/net/corda/node/services/identity/PersistentIdentityService.kt b/node/src/main/kotlin/net/corda/node/services/identity/PersistentIdentityService.kt index c21f7e9c91..9c77d45ded 100644 --- a/node/src/main/kotlin/net/corda/node/services/identity/PersistentIdentityService.kt +++ b/node/src/main/kotlin/net/corda/node/services/identity/PersistentIdentityService.kt @@ -13,6 +13,8 @@ import net.corda.core.utilities.debug import net.corda.node.services.api.IdentityServiceInternal import net.corda.node.utilities.AppendOnlyPersistentMap import net.corda.nodeapi.internal.crypto.X509CertificateFactory +import net.corda.nodeapi.internal.crypto.X509Utilities +import net.corda.nodeapi.internal.crypto.x509Certificates import net.corda.nodeapi.internal.persistence.NODE_DATABASE_PREFIX import java.security.InvalidAlgorithmParameterException import java.security.PublicKey @@ -111,23 +113,23 @@ class PersistentIdentityService(override val trustRoot: X509Certificate, @Throws(CertificateExpiredException::class, CertificateNotYetValidException::class, InvalidAlgorithmParameterException::class) override fun verifyAndRegisterIdentity(identity: PartyAndCertificate): PartyAndCertificate? { // Validate the chain first, before we do anything clever with it + val identityCertChain = identity.certPath.x509Certificates try { identity.verify(trustAnchor) } catch (e: CertPathValidatorException) { log.warn(e.localizedMessage) log.warn("Path = ") - identity.certPath.certificates.reversed().forEach { - log.warn((it as X509Certificate).subjectX500Principal.toString()) + identityCertChain.reversed().forEach { + log.warn(it.subjectX500Principal.toString()) } throw e } // Ensure we record the first identity of the same name, first - val wellKnownCert: Certificate = identity.certPath.certificates.single { CertRole.extract(it)?.isWellKnown ?: false } + val wellKnownCert = identityCertChain.single { CertRole.extract(it)?.isWellKnown ?: false } if (wellKnownCert != identity.certificate) { - val certificates = identity.certPath.certificates - val idx = certificates.lastIndexOf(wellKnownCert) - val firstPath = X509CertificateFactory().generateCertPath(certificates.slice(idx until certificates.size)) + val idx = identityCertChain.lastIndexOf(wellKnownCert) + val firstPath = X509Utilities.buildCertPath(identityCertChain.slice(idx until identityCertChain.size)) verifyAndRegisterIdentity(PartyAndCertificate(firstPath)) } @@ -136,7 +138,7 @@ class PersistentIdentityService(override val trustRoot: X509Certificate, keyToParties.addWithDuplicatesAllowed(key, identity) // Always keep the first party we registered, as that's the well known identity principalToParties.addWithDuplicatesAllowed(identity.name, key, false) - val parentId = mapToKey(identity.certPath.certificates[1].publicKey) + val parentId = mapToKey(identityCertChain[1].publicKey) return keyToParties[parentId] } @@ -175,7 +177,7 @@ class PersistentIdentityService(override val trustRoot: X509Certificate, val results = LinkedHashSet() for ((x500name, partyId) in principalToParties.allPersisted()) { val party = keyToParties[partyId]!!.party - val components = listOf(x500name.commonName, x500name.organisationUnit, x500name.organisation, x500name.locality, x500name.state, x500name.country).filterNotNull() + val components = listOfNotNull(x500name.commonName, x500name.organisationUnit, x500name.organisation, x500name.locality, x500name.state, x500name.country) components.forEach { component -> if (exactMatch && component == query) { results += party diff --git a/node/src/main/kotlin/net/corda/node/services/keys/KMSUtils.kt b/node/src/main/kotlin/net/corda/node/services/keys/KMSUtils.kt index dad6a7b2a7..460845a652 100644 --- a/node/src/main/kotlin/net/corda/node/services/keys/KMSUtils.kt +++ b/node/src/main/kotlin/net/corda/node/services/keys/KMSUtils.kt @@ -7,8 +7,8 @@ import net.corda.core.utilities.days import net.corda.node.services.api.IdentityServiceInternal import net.corda.nodeapi.internal.crypto.CertificateType import net.corda.nodeapi.internal.crypto.ContentSignerBuilder -import net.corda.nodeapi.internal.crypto.X509CertificateFactory import net.corda.nodeapi.internal.crypto.X509Utilities +import net.corda.nodeapi.internal.crypto.x509Certificates import org.bouncycastle.operator.ContentSigner import java.security.KeyPair import java.security.PublicKey @@ -43,7 +43,7 @@ fun freshCertificate(identityService: IdentityServiceInternal, issuer.name.x500Principal, subjectPublicKey, window) - val ourCertPath = X509CertificateFactory().generateCertPath(listOf(ourCertificate) + issuer.certPath.certificates) + val ourCertPath = X509Utilities.buildCertPath(ourCertificate, issuer.certPath.x509Certificates) val anonymisedIdentity = PartyAndCertificate(ourCertPath) identityService.justVerifyAndRegisterIdentity(anonymisedIdentity) return anonymisedIdentity diff --git a/node/src/main/kotlin/net/corda/node/services/messaging/CoreBridgeManager.kt b/node/src/main/kotlin/net/corda/node/services/messaging/CoreBridgeManager.kt index 208bc3c946..c3c9a1041d 100644 --- a/node/src/main/kotlin/net/corda/node/services/messaging/CoreBridgeManager.kt +++ b/node/src/main/kotlin/net/corda/node/services/messaging/CoreBridgeManager.kt @@ -13,6 +13,7 @@ import net.corda.nodeapi.internal.ArtemisMessagingComponent import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.PEER_USER import net.corda.nodeapi.internal.ArtemisMessagingComponent.RemoteInboxAddress.Companion.translateLocalQueueToInboxAddress import net.corda.nodeapi.internal.crypto.X509Utilities +import net.corda.nodeapi.internal.crypto.x509 import org.apache.activemq.artemis.api.core.Message import org.apache.activemq.artemis.core.config.BridgeConfiguration import org.apache.activemq.artemis.core.remoting.impl.netty.NettyConnection @@ -23,7 +24,6 @@ import org.apache.activemq.artemis.core.server.ActiveMQServer import org.apache.activemq.artemis.core.server.cluster.Transformer import org.apache.activemq.artemis.spi.core.remoting.* import org.apache.activemq.artemis.utils.ConfigurationHelper -import java.security.cert.X509Certificate import java.time.Duration import java.util.concurrent.Executor import java.util.concurrent.ScheduledExecutorService @@ -162,7 +162,9 @@ class VerifyingNettyConnectorFactory : NettyConnectorFactory() { "Peer has wrong subject name in the certificate - expected $expectedLegalNames but got $peerCertificateName. This is either a fatal " + "misconfiguration by the remote peer or an SSL man-in-the-middle attack!" } - X509Utilities.validateCertificateChain(session.localCertificates.last() as X509Certificate, *session.peerCertificates) + X509Utilities.validateCertificateChain( + session.localCertificates.last().x509, + session.peerCertificates.x509) } catch (e: IllegalArgumentException) { connection.close() log.error(e.message) diff --git a/node/src/main/kotlin/net/corda/node/utilities/registration/NetworkRegistrationHelper.kt b/node/src/main/kotlin/net/corda/node/utilities/registration/NetworkRegistrationHelper.kt index ac49461b05..509ade8a32 100644 --- a/node/src/main/kotlin/net/corda/node/utilities/registration/NetworkRegistrationHelper.kt +++ b/node/src/main/kotlin/net/corda/node/utilities/registration/NetworkRegistrationHelper.kt @@ -10,6 +10,7 @@ import net.corda.nodeapi.internal.crypto.X509Utilities import net.corda.nodeapi.internal.crypto.X509Utilities.CORDA_CLIENT_CA import net.corda.nodeapi.internal.crypto.X509Utilities.CORDA_CLIENT_TLS import net.corda.nodeapi.internal.crypto.X509Utilities.CORDA_ROOT_CA +import net.corda.nodeapi.internal.crypto.x509 import org.bouncycastle.openssl.jcajce.JcaPEMWriter import org.bouncycastle.util.io.pem.PemObject import java.io.StringWriter @@ -43,7 +44,7 @@ class NetworkRegistrationHelper(private val config: NodeConfiguration, private v "This file must contain the root CA cert of your compatibility zone. " + "Please contact your CZ operator." } - this.rootCert = rootCert as X509Certificate + this.rootCert = rootCert.x509 } /** @@ -109,7 +110,7 @@ class NetworkRegistrationHelper(private val config: NodeConfiguration, private v } println("Checking root of the certificate path is what we expect.") - X509Utilities.validateCertificateChain(rootCert, *certificates.toTypedArray()) + X509Utilities.validateCertificateChain(rootCert, certificates) println("Certificate signing request approved, storing private key with the certificate chain.") // Save private key and certificate chain to the key store. diff --git a/node/src/test/kotlin/net/corda/node/services/identity/InMemoryIdentityServiceTests.kt b/node/src/test/kotlin/net/corda/node/services/identity/InMemoryIdentityServiceTests.kt index 91c216f12d..f1256db27d 100644 --- a/node/src/test/kotlin/net/corda/node/services/identity/InMemoryIdentityServiceTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/identity/InMemoryIdentityServiceTests.kt @@ -8,8 +8,8 @@ import net.corda.core.identity.Party import net.corda.core.identity.PartyAndCertificate import net.corda.core.node.services.UnknownAnonymousPartyException import net.corda.nodeapi.internal.crypto.CertificateType -import net.corda.nodeapi.internal.crypto.X509CertificateFactory import net.corda.nodeapi.internal.crypto.X509Utilities +import net.corda.nodeapi.internal.crypto.x509Certificates import net.corda.testing.core.* import org.junit.Rule import org.junit.Test @@ -173,7 +173,7 @@ class InMemoryIdentityServiceTests { issuerKeyPair, x500Name.x500Principal, txKeyPair.public) - val txCertPath = X509CertificateFactory().generateCertPath(listOf(txCert) + issuer.certPath.certificates) + val txCertPath = X509Utilities.buildCertPath(txCert, issuer.certPath.x509Certificates) return Pair(issuer, PartyAndCertificate(txCertPath)) } diff --git a/node/src/test/kotlin/net/corda/node/services/identity/PersistentIdentityServiceTests.kt b/node/src/test/kotlin/net/corda/node/services/identity/PersistentIdentityServiceTests.kt index 7e3a062885..dda6516173 100644 --- a/node/src/test/kotlin/net/corda/node/services/identity/PersistentIdentityServiceTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/identity/PersistentIdentityServiceTests.kt @@ -10,8 +10,8 @@ import net.corda.core.node.services.IdentityService import net.corda.core.node.services.UnknownAnonymousPartyException import net.corda.node.internal.configureDatabase import net.corda.nodeapi.internal.crypto.CertificateType -import net.corda.nodeapi.internal.crypto.X509CertificateFactory import net.corda.nodeapi.internal.crypto.X509Utilities +import net.corda.nodeapi.internal.crypto.x509Certificates import net.corda.nodeapi.internal.persistence.CordaPersistence import net.corda.nodeapi.internal.persistence.DatabaseConfig import net.corda.testing.core.* @@ -264,8 +264,13 @@ class PersistentIdentityServiceTests { val issuerKeyPair = generateKeyPair() val issuer = getTestPartyAndCertificate(x500Name, issuerKeyPair.public) val txKey = Crypto.generateKeyPair() - val txCert = X509Utilities.createCertificate(CertificateType.CONFIDENTIAL_LEGAL_IDENTITY, issuer.certificate, issuerKeyPair, x500Name.x500Principal, txKey.public) - val txCertPath = X509CertificateFactory().generateCertPath(listOf(txCert) + issuer.certPath.certificates) + val txCert = X509Utilities.createCertificate( + CertificateType.CONFIDENTIAL_LEGAL_IDENTITY, + issuer.certificate, + issuerKeyPair, + x500Name.x500Principal, + txKey.public) + val txCertPath = X509Utilities.buildCertPath(txCert, issuer.certPath.x509Certificates) return Pair(issuer, PartyAndCertificate(txCertPath)) } diff --git a/testing/test-utils/src/main/kotlin/net/corda/testing/core/TestUtils.kt b/testing/test-utils/src/main/kotlin/net/corda/testing/core/TestUtils.kt index f7614e2424..762ea036be 100644 --- a/testing/test-utils/src/main/kotlin/net/corda/testing/core/TestUtils.kt +++ b/testing/test-utils/src/main/kotlin/net/corda/testing/core/TestUtils.kt @@ -87,7 +87,7 @@ fun getTestPartyAndCertificate(party: Party): PartyAndCertificate { party.name.x500Principal, party.owningKey) - val certPath = X509CertificateFactory().generateCertPath(identityCert, nodeCaCert, intermediate.certificate, trustRoot) + val certPath = X509Utilities.buildCertPath(identityCert, nodeCaCert, intermediate.certificate, trustRoot) return PartyAndCertificate(certPath) } diff --git a/testing/test-utils/src/main/kotlin/net/corda/testing/internal/TestNodeInfoBuilder.kt b/testing/test-utils/src/main/kotlin/net/corda/testing/internal/TestNodeInfoBuilder.kt index 83cd569679..c99ca190cf 100644 --- a/testing/test-utils/src/main/kotlin/net/corda/testing/internal/TestNodeInfoBuilder.kt +++ b/testing/test-utils/src/main/kotlin/net/corda/testing/internal/TestNodeInfoBuilder.kt @@ -11,7 +11,6 @@ import net.corda.nodeapi.internal.SignedNodeInfo import net.corda.nodeapi.internal.createDevNodeCa import net.corda.nodeapi.internal.crypto.CertificateAndKeyPair import net.corda.nodeapi.internal.crypto.CertificateType -import net.corda.nodeapi.internal.crypto.X509CertificateFactory import net.corda.nodeapi.internal.crypto.X509Utilities import net.corda.testing.core.DEV_INTERMEDIATE_CA import net.corda.testing.core.DEV_ROOT_CA @@ -31,11 +30,11 @@ class TestNodeInfoBuilder(private val intermediateAndRoot: Pair Date: Thu, 25 Jan 2018 14:32:58 +0000 Subject: [PATCH 30/36] Exposed RPC SSL settings through Cordformation (#2419) --- .../rpc/StandaloneCordaRPCJavaClientTest.java | 1 + .../kotlin/rpc/StandaloneCordaRPClientTest.kt | 1 + .../net/corda/core/NodeVersioningTest.kt | 1 + .../corda/core/cordapp/CordappSmokeTest.kt | 1 + .../java/net/corda/cordform/CordformNode.java | 14 +++++ .../java/net/corda/cordform/RpcSettings.java | 56 ++++++++++++++++++ .../java/net/corda/cordform/SslOptions.java | 56 ++++++++++++++++++ .../src/main/kotlin/net/corda/plugins/Node.kt | 11 +++- .../kotlin/net/corda/plugins/RpcSettings.kt | 59 +++++++++++++++++++ .../kotlin/net/corda/plugins/SslOptions.kt | 50 ++++++++++++++++ .../kotlin/net/corda/node/SSHServerTest.kt | 3 +- .../kotlin/net/corda/node/internal/Node.kt | 30 ++++++---- samples/attachment-demo/build.gradle | 15 ++++- .../net/corda/bank/BankOfCordaCordform.kt | 20 +++++-- samples/irs-demo/cordapp/build.gradle | 15 ++++- .../net/corda/notarydemo/BFTNotaryCordform.kt | 30 ++++++++-- .../corda/notarydemo/CustomNotaryCordform.kt | 15 ++++- .../corda/notarydemo/RaftNotaryCordform.kt | 25 ++++++-- .../corda/notarydemo/SingleNotaryCordform.kt | 15 ++++- samples/simm-valuation-demo/build.gradle | 15 ++++- samples/trader-demo/build.gradle | 15 ++++- .../node/internal/demorun/CordformUtils.kt | 10 ++++ .../net/corda/smoketesting/NodeConfig.kt | 6 +- .../net/corda/smoketesting/NodeProcess.kt | 8 ++- .../net/corda/webserver/WebServerConfig.kt | 2 +- 25 files changed, 422 insertions(+), 52 deletions(-) create mode 100644 gradle-plugins/cordform-common/src/main/java/net/corda/cordform/RpcSettings.java create mode 100644 gradle-plugins/cordform-common/src/main/java/net/corda/cordform/SslOptions.java create mode 100644 gradle-plugins/cordformation/src/main/kotlin/net/corda/plugins/RpcSettings.kt create mode 100644 gradle-plugins/cordformation/src/main/kotlin/net/corda/plugins/SslOptions.kt diff --git a/client/rpc/src/smoke-test/java/net/corda/java/rpc/StandaloneCordaRPCJavaClientTest.java b/client/rpc/src/smoke-test/java/net/corda/java/rpc/StandaloneCordaRPCJavaClientTest.java index 30edeeb897..0cd1f4fad8 100644 --- a/client/rpc/src/smoke-test/java/net/corda/java/rpc/StandaloneCordaRPCJavaClientTest.java +++ b/client/rpc/src/smoke-test/java/net/corda/java/rpc/StandaloneCordaRPCJavaClientTest.java @@ -47,6 +47,7 @@ public class StandaloneCordaRPCJavaClientTest { port.getAndIncrement(), port.getAndIncrement(), port.getAndIncrement(), + port.getAndIncrement(), true, Collections.singletonList(rpcUser) ); diff --git a/client/rpc/src/smoke-test/kotlin/net/corda/kotlin/rpc/StandaloneCordaRPClientTest.kt b/client/rpc/src/smoke-test/kotlin/net/corda/kotlin/rpc/StandaloneCordaRPClientTest.kt index 09e739819a..3c2b01d471 100644 --- a/client/rpc/src/smoke-test/kotlin/net/corda/kotlin/rpc/StandaloneCordaRPClientTest.kt +++ b/client/rpc/src/smoke-test/kotlin/net/corda/kotlin/rpc/StandaloneCordaRPClientTest.kt @@ -60,6 +60,7 @@ class StandaloneCordaRPClientTest { legalName = CordaX500Name(organisation = "Notary Service", locality = "Zurich", country = "CH"), p2pPort = port.andIncrement, rpcPort = port.andIncrement, + rpcAdminPort = port.andIncrement, webPort = port.andIncrement, isNotary = true, users = listOf(user) diff --git a/core/src/smoke-test/kotlin/net/corda/core/NodeVersioningTest.kt b/core/src/smoke-test/kotlin/net/corda/core/NodeVersioningTest.kt index f682bfcb75..52f7a73064 100644 --- a/core/src/smoke-test/kotlin/net/corda/core/NodeVersioningTest.kt +++ b/core/src/smoke-test/kotlin/net/corda/core/NodeVersioningTest.kt @@ -37,6 +37,7 @@ class NodeVersioningTest { legalName = CordaX500Name(organisation = "Alice Corp", locality = "Madrid", country = "ES"), p2pPort = port.andIncrement, rpcPort = port.andIncrement, + rpcAdminPort = port.andIncrement, webPort = port.andIncrement, isNotary = false, users = listOf(user) diff --git a/core/src/smoke-test/kotlin/net/corda/core/cordapp/CordappSmokeTest.kt b/core/src/smoke-test/kotlin/net/corda/core/cordapp/CordappSmokeTest.kt index 1f76e500fd..2017e6401a 100644 --- a/core/src/smoke-test/kotlin/net/corda/core/cordapp/CordappSmokeTest.kt +++ b/core/src/smoke-test/kotlin/net/corda/core/cordapp/CordappSmokeTest.kt @@ -33,6 +33,7 @@ class CordappSmokeTest { legalName = CordaX500Name(organisation = "Alice Corp", locality = "Madrid", country = "ES"), p2pPort = port.andIncrement, rpcPort = port.andIncrement, + rpcAdminPort = port.andIncrement, webPort = port.andIncrement, isNotary = false, users = listOf(user) diff --git a/gradle-plugins/cordform-common/src/main/java/net/corda/cordform/CordformNode.java b/gradle-plugins/cordform-common/src/main/java/net/corda/cordform/CordformNode.java index 285260f6db..52300f0f63 100644 --- a/gradle-plugins/cordform-common/src/main/java/net/corda/cordform/CordformNode.java +++ b/gradle-plugins/cordform-common/src/main/java/net/corda/cordform/CordformNode.java @@ -95,6 +95,9 @@ public class CordformNode implements NodeDefinition { */ @Nullable public String getRpcAddress() { + if (config.hasPath("rpcSettings.address")) { + return config.getConfig("rpcSettings").getString("address"); + } return getOptionalString("rpcAddress"); } @@ -102,7 +105,9 @@ public class CordformNode implements NodeDefinition { * Set the Artemis RPC port for this node on localhost. * * @param rpcPort The Artemis RPC queue port. + * @deprecated Use {@link CordformNode#rpcSettings(RpcSettings)} instead. */ + @Deprecated public void rpcPort(int rpcPort) { rpcAddress(DEFAULT_HOST + ':' + rpcPort); } @@ -111,7 +116,9 @@ public class CordformNode implements NodeDefinition { * Set the Artemis RPC address for this node. * * @param rpcAddress The Artemis RPC queue host and port. + * @deprecated Use {@link CordformNode#rpcSettings(RpcSettings)} instead. */ + @Deprecated public void rpcAddress(String rpcAddress) { setValue("rpcAddress", rpcAddress); } @@ -140,6 +147,13 @@ public class CordformNode implements NodeDefinition { setValue("webAddress", webAddress); } + /** + * Specifies RPC settings for the node. + */ + public void rpcSettings(RpcSettings settings) { + config = settings.addTo("rpcSettings", config); + } + /** * Set the path to a file with optional properties, which are appended to the generated node.conf file. * diff --git a/gradle-plugins/cordform-common/src/main/java/net/corda/cordform/RpcSettings.java b/gradle-plugins/cordform-common/src/main/java/net/corda/cordform/RpcSettings.java new file mode 100644 index 0000000000..e429bb0ca6 --- /dev/null +++ b/gradle-plugins/cordform-common/src/main/java/net/corda/cordform/RpcSettings.java @@ -0,0 +1,56 @@ +package net.corda.cordform; + +import com.typesafe.config.Config; +import com.typesafe.config.ConfigFactory; +import com.typesafe.config.ConfigValueFactory; + +public final class RpcSettings { + + private Config config = ConfigFactory.empty(); + + /** + * RPC address for the node. + */ + public final void address(final String value) { + setValue("address", value); + } + + /** + * RPC admin address for the node (necessary if [useSsl] is false or unset). + */ + public final void adminAddress(final String value) { + setValue("adminAddress", value); + } + + /** + * Specifies whether the node RPC layer will require SSL from clients. + */ + public final void useSsl(final Boolean value) { + setValue("useSsl", value); + } + + /** + * Specifies whether the RPC broker is separate from the node. + */ + public final void standAloneBroker(final Boolean value) { + setValue("standAloneBroker", value); + } + + /** + * Specifies SSL certificates options for the RPC layer. + */ + public final void ssl(final SslOptions options) { + config = options.addTo("ssl", config); + } + + final Config addTo(final String key, final Config config) { + if (this.config.isEmpty()) { + return config; + } + return config.withValue(key, this.config.root()); + } + + private void setValue(String path, Object value) { + config = config.withValue(path, ConfigValueFactory.fromAnyRef(value)); + } +} diff --git a/gradle-plugins/cordform-common/src/main/java/net/corda/cordform/SslOptions.java b/gradle-plugins/cordform-common/src/main/java/net/corda/cordform/SslOptions.java new file mode 100644 index 0000000000..da3cc22288 --- /dev/null +++ b/gradle-plugins/cordform-common/src/main/java/net/corda/cordform/SslOptions.java @@ -0,0 +1,56 @@ +package net.corda.cordform; + +import com.typesafe.config.Config; +import com.typesafe.config.ConfigFactory; +import com.typesafe.config.ConfigValueFactory; + +public final class SslOptions { + + private Config config = ConfigFactory.empty(); + + /** + * Password for the keystore. + */ + public final void keyStorePassword(final String value) { + setValue("keyStorePassword", value); + } + + /** + * Password for the truststore. + */ + public final void trustStorePassword(final String value) { + setValue("trustStorePassword", value); + } + + /** + * Directory under which key stores are to be placed. + */ + public final void certificatesDirectory(final String value) { + setValue("certificatesDirectory", value); + } + + /** + * Absolute path to SSL keystore. Default: "[certificatesDirectory]/sslkeystore.jks" + */ + public final void sslKeystore(final String value) { + setValue("sslKeystore", value); + } + + /** + * Absolute path to SSL truststore. Default: "[certificatesDirectory]/truststore.jks" + */ + public final void trustStoreFile(final String value) { + setValue("trustStoreFile", value); + } + + final Config addTo(final String key, final Config config) { + if (this.config.isEmpty()) { + return config; + } + return config.withValue(key, this.config.root()); + } + + private void setValue(String path, Object value) { + config = config.withValue(path, ConfigValueFactory.fromAnyRef(value)); + } +} diff --git a/gradle-plugins/cordformation/src/main/kotlin/net/corda/plugins/Node.kt b/gradle-plugins/cordformation/src/main/kotlin/net/corda/plugins/Node.kt index 1f0cdc9701..5fb670cceb 100644 --- a/gradle-plugins/cordformation/src/main/kotlin/net/corda/plugins/Node.kt +++ b/gradle-plugins/cordformation/src/main/kotlin/net/corda/plugins/Node.kt @@ -3,6 +3,7 @@ package net.corda.plugins import com.typesafe.config.ConfigFactory import com.typesafe.config.ConfigRenderOptions import com.typesafe.config.ConfigValueFactory +import groovy.lang.Closure import net.corda.cordform.CordformNode import org.gradle.api.Project import java.io.File @@ -54,6 +55,14 @@ class Node(private val project: Project) : CordformNode() { config = config.withValue("useTestClock", ConfigValueFactory.fromAnyRef(useTestClock)) } + /** + * Specifies RPC settings for the node. + */ + fun rpcSettings(configureClosure: Closure) { + val rpcSettings = project.configure(RpcSettings(project), configureClosure) as RpcSettings + config = rpcSettings.addTo("rpcSettings", config) + } + /** * Enables SSH access on given port * @@ -158,7 +167,7 @@ class Node(private val project: Project) : CordformNode() { // Need to write a temporary file first to use the project.copy, which resolves directories correctly. val tmpDir = File(project.buildDir, "tmp") Files.createDirectories(tmpDir.toPath()) - var fileName = "${nodeDir.getName()}.conf" + var fileName = "${nodeDir.name}.conf" val tmpConfFile = File(tmpDir, fileName) Files.write(tmpConfFile.toPath(), configFileText, StandardCharsets.UTF_8) return tmpConfFile diff --git a/gradle-plugins/cordformation/src/main/kotlin/net/corda/plugins/RpcSettings.kt b/gradle-plugins/cordformation/src/main/kotlin/net/corda/plugins/RpcSettings.kt new file mode 100644 index 0000000000..2f46f0b2c5 --- /dev/null +++ b/gradle-plugins/cordformation/src/main/kotlin/net/corda/plugins/RpcSettings.kt @@ -0,0 +1,59 @@ +package net.corda.plugins + +import com.typesafe.config.Config +import com.typesafe.config.ConfigFactory +import com.typesafe.config.ConfigValueFactory +import groovy.lang.Closure +import org.gradle.api.Project + +class RpcSettings(private val project: Project) { + private var config: Config = ConfigFactory.empty() + + /** + * RPC address for the node. + */ + fun address(value: String) { + config += "address" to value + } + + /** + * RPC admin address for the node (necessary if [useSsl] is false or unset). + */ + fun adminAddress(value: String) { + config += "adminAddress" to value + } + + /** + * Specifies whether the node RPC layer will require SSL from clients. + */ + fun useSsl(value: Boolean) { + config += "useSsl" to value + } + + /** + * Specifies whether the RPC broker is separate from the node. + */ + fun standAloneBroker(value: Boolean) { + config += "standAloneBroker" to value + } + + /** + * Specifies SSL certificates options for the RPC layer. + */ + fun ssl(configureClosure: Closure) { + val sslOptions = project.configure(SslOptions(), configureClosure) as SslOptions + config = sslOptions.addTo("ssl", config) + } + + internal fun addTo(key: String, config: Config): Config { + if (this.config.isEmpty) { + return config + } + return config + (key to this.config.root()) + } +} + +internal operator fun Config.plus(entry: Pair): Config { + + return withValue(entry.first, ConfigValueFactory.fromAnyRef(entry.second)) +} \ No newline at end of file diff --git a/gradle-plugins/cordformation/src/main/kotlin/net/corda/plugins/SslOptions.kt b/gradle-plugins/cordformation/src/main/kotlin/net/corda/plugins/SslOptions.kt new file mode 100644 index 0000000000..bb8fed288e --- /dev/null +++ b/gradle-plugins/cordformation/src/main/kotlin/net/corda/plugins/SslOptions.kt @@ -0,0 +1,50 @@ +package net.corda.plugins + +import com.typesafe.config.Config +import com.typesafe.config.ConfigFactory + +class SslOptions { + private var config: Config = ConfigFactory.empty() + + /** + * Password for the keystore. + */ + fun keyStorePassword(value: String) { + config += "keyStorePassword" to value + } + + /** + * Password for the truststore. + */ + fun trustStorePassword(value: String) { + config += "trustStorePassword" to value + } + + /** + * Directory under which key stores are to be placed. + */ + fun certificatesDirectory(value: String) { + config += "certificatesDirectory" to value + } + + /** + * Absolute path to SSL keystore. Default: "[certificatesDirectory]/sslkeystore.jks" + */ + fun sslKeystore(value: String) { + config += "sslKeystore" to value + } + + /** + * Absolute path to SSL truststore. Default: "[certificatesDirectory]/truststore.jks" + */ + fun trustStoreFile(value: String) { + config += "trustStoreFile" to value + } + + internal fun addTo(key: String, config: Config): Config { + if (this.config.isEmpty) { + return config + } + return config + (key to this.config.root()) + } +} \ No newline at end of file diff --git a/node/src/integration-test/kotlin/net/corda/node/SSHServerTest.kt b/node/src/integration-test/kotlin/net/corda/node/SSHServerTest.kt index 8a0e2e3727..e905b7f4d9 100644 --- a/node/src/integration-test/kotlin/net/corda/node/SSHServerTest.kt +++ b/node/src/integration-test/kotlin/net/corda/node/SSHServerTest.kt @@ -140,8 +140,9 @@ class SSHServerTest { val response = String(Streams.readAll(channel.inputStream)) + val linesWithDoneCount = response.lines().filter { line -> line.contains("Done") } // There are ANSI control characters involved, so we want to avoid direct byte to byte matching. - assertThat(response.lines()).filteredOn( { it.contains("Done")}).hasSize(1) + assertThat(linesWithDoneCount).hasSize(1) } } diff --git a/node/src/main/kotlin/net/corda/node/internal/Node.kt b/node/src/main/kotlin/net/corda/node/internal/Node.kt index d854e5e215..b2714cb4fa 100644 --- a/node/src/main/kotlin/net/corda/node/internal/Node.kt +++ b/node/src/main/kotlin/net/corda/node/internal/Node.kt @@ -154,7 +154,9 @@ open class Node(configuration: NodeConfiguration, val advertisedAddress = info.addresses.single() printBasicNodeInfo("Incoming connection address", advertisedAddress.toString()) - rpcMessagingClient = RPCMessagingClient(configuration.rpcOptions.sslConfig, rpcServerAddresses.admin, networkParameters.maxMessageSize) + rpcServerAddresses?.let { + rpcMessagingClient = RPCMessagingClient(configuration.rpcOptions.sslConfig, it.admin, networkParameters.maxMessageSize) + } verifierMessagingClient = when (configuration.verifierType) { VerifierType.OutOfProcess -> VerifierMessagingClient(configuration, serverAddress, services.monitoringService.metrics, networkParameters.maxMessageSize) VerifierType.InMemory -> null @@ -173,18 +175,20 @@ open class Node(configuration: NodeConfiguration, networkParameters.maxMessageSize) } - private fun startLocalRpcBroker(): BrokerAddresses { + private fun startLocalRpcBroker(): BrokerAddresses? { with(configuration) { - require(rpcOptions.address != null) { "RPC address needs to be specified for local RPC broker." } - val rpcBrokerDirectory: Path = baseDirectory / "brokers" / "rpc" - with(rpcOptions) { - rpcBroker = if (useSsl) { - ArtemisRpcBroker.withSsl(this.address!!, sslConfig, securityManager, certificateChainCheckPolicies, networkParameters.maxMessageSize, exportJMXto.isNotEmpty(), rpcBrokerDirectory) - } else { - ArtemisRpcBroker.withoutSsl(this.address!!, adminAddress!!, sslConfig, securityManager, certificateChainCheckPolicies, networkParameters.maxMessageSize, exportJMXto.isNotEmpty(), rpcBrokerDirectory) + return rpcOptions.address?.let { + require(rpcOptions.address != null) { "RPC address needs to be specified for local RPC broker." } + val rpcBrokerDirectory: Path = baseDirectory / "brokers" / "rpc" + with(rpcOptions) { + rpcBroker = if (useSsl) { + ArtemisRpcBroker.withSsl(this.address!!, sslConfig, securityManager, certificateChainCheckPolicies, networkParameters.maxMessageSize, exportJMXto.isNotEmpty(), rpcBrokerDirectory) + } else { + ArtemisRpcBroker.withoutSsl(this.address!!, adminAddress!!, sslConfig, securityManager, certificateChainCheckPolicies, networkParameters.maxMessageSize, exportJMXto.isNotEmpty(), rpcBrokerDirectory) + } } + return rpcBroker!!.addresses } - return rpcBroker!!.addresses } } @@ -250,7 +254,7 @@ open class Node(configuration: NodeConfiguration, start() } // Start up the MQ clients. - rpcMessagingClient.run { + rpcMessagingClient?.run { runOnStop += this::close start(rpcOps, securityManager) } @@ -353,11 +357,11 @@ open class Node(configuration: NodeConfiguration, checkpointContext = KRYO_CHECKPOINT_CONTEXT.withClassLoader(classloader)) } - private lateinit var rpcMessagingClient: RPCMessagingClient + private var rpcMessagingClient: RPCMessagingClient? = null private var verifierMessagingClient: VerifierMessagingClient? = null /** Starts a blocking event loop for message dispatch. */ fun run() { - rpcMessagingClient.start2(rpcBroker!!.serverControl) + rpcMessagingClient?.start2(rpcBroker!!.serverControl) verifierMessagingClient?.start2() (network as P2PMessagingClient).run() } diff --git a/samples/attachment-demo/build.gradle b/samples/attachment-demo/build.gradle index a898df32c5..16e1533ede 100644 --- a/samples/attachment-demo/build.gradle +++ b/samples/attachment-demo/build.gradle @@ -47,21 +47,30 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) { name "O=Notary Service,L=Zurich,C=CH" notary = [validating : true] p2pPort 10002 - rpcPort 10003 cordapps = [] rpcUsers = ext.rpcUsers + rpcSettings { + address "localhost:10003" + adminAddress "localhost:10004" + } } node { name "O=Bank A,L=London,C=GB" p2pPort 10005 - rpcPort 10006 cordapps = [] rpcUsers = ext.rpcUsers + rpcSettings { + address "localhost:10006" + adminAddress "localhost:10007" + } } node { name "O=Bank B,L=New York,C=US" p2pPort 10008 - rpcPort 10009 + rpcSettings { + address "localhost:10009" + adminAddress "localhost:10011" + } webPort 10010 cordapps = [] rpcUsers = ext.rpcUsers diff --git a/samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/BankOfCordaCordform.kt b/samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/BankOfCordaCordform.kt index 61609835d0..260d37365f 100644 --- a/samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/BankOfCordaCordform.kt +++ b/samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/BankOfCordaCordform.kt @@ -20,8 +20,9 @@ import kotlin.system.exitProcess val BIGCORP_NAME = CordaX500Name(organisation = "BigCorporation", locality = "New York", country = "US") private val NOTARY_NAME = CordaX500Name(organisation = "Notary Service", locality = "Zurich", country = "CH") -private val BOC_RPC_PORT = 10006 -private val BOC_WEB_PORT = 10007 +private const val BOC_RPC_PORT = 10006 +private const val BOC_RPC_ADMIN_PORT = 10015 +private const val BOC_WEB_PORT = 10007 class BankOfCordaCordform : CordformDefinition() { init { @@ -30,20 +31,29 @@ class BankOfCordaCordform : CordformDefinition() { name(NOTARY_NAME) notary(NotaryConfig(validating = true)) p2pPort(10002) - rpcPort(10003) + rpcSettings { + address("localhost:10003") + adminAddress("localhost:10004") + } } node { name(BOC_NAME) extraConfig = mapOf("issuableCurrencies" to listOf("USD")) p2pPort(10005) - rpcPort(BOC_RPC_PORT) + rpcSettings { + address("localhost:$BOC_RPC_PORT") + adminAddress("localhost:$BOC_RPC_ADMIN_PORT") + } webPort(BOC_WEB_PORT) rpcUsers(User("bankUser", "test", setOf(all()))) } node { name(BIGCORP_NAME) p2pPort(10008) - rpcPort(10009) + rpcSettings { + address("localhost:10009") + adminAddress("localhost:10011") + } webPort(10010) rpcUsers(User("bigCorpUser", "test", setOf(all()))) } diff --git a/samples/irs-demo/cordapp/build.gradle b/samples/irs-demo/cordapp/build.gradle index 3b5dbd4c08..015f87342a 100644 --- a/samples/irs-demo/cordapp/build.gradle +++ b/samples/irs-demo/cordapp/build.gradle @@ -64,7 +64,10 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) { name "O=Notary Service,L=Zurich,C=CH" notary = [validating : true] p2pPort 10002 - rpcPort 10003 + rpcSettings { + address "localhost:10003" + adminAddress "localhost:10023" + } cordapps = ["${project.group}:finance:$corda_release_version"] rpcUsers = ext.rpcUsers useTestClock true @@ -72,7 +75,10 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) { node { name "O=Bank A,L=London,C=GB" p2pPort 10005 - rpcPort 10006 + rpcSettings { + address "localhost:10006" + adminAddress "localhost:10026" + } cordapps = ["${project.group}:finance:$corda_release_version"] rpcUsers = ext.rpcUsers useTestClock true @@ -80,7 +86,10 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) { node { name "O=Bank B,L=New York,C=US" p2pPort 10008 - rpcPort 10009 + rpcSettings { + address "localhost:10009" + adminAddress "localhost:10029" + } cordapps = ["${project.group}:finance:$corda_release_version"] rpcUsers = ext.rpcUsers useTestClock true diff --git a/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/BFTNotaryCordform.kt b/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/BFTNotaryCordform.kt index 8475571bfc..132636f05a 100644 --- a/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/BFTNotaryCordform.kt +++ b/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/BFTNotaryCordform.kt @@ -29,13 +29,19 @@ class BFTNotaryCordform : CordformDefinition() { node { name(ALICE_NAME) p2pPort(10002) - rpcPort(10003) + rpcSettings { + address("localhost:10003") + adminAddress("localhost:10103") + } rpcUsers(notaryDemoUser) } node { name(BOB_NAME) p2pPort(10005) - rpcPort(10006) + rpcSettings { + address("localhost:10006") + adminAddress("localhost:10106") + } } val clusterAddresses = (0 until clusterSize).map { NetworkHostAndPort("localhost", 11000 + it * 10) } fun notaryNode(replicaId: Int, configure: CordformNode.() -> Unit) = node { @@ -45,19 +51,31 @@ class BFTNotaryCordform : CordformDefinition() { } notaryNode(0) { p2pPort(10009) - rpcPort(10010) + rpcSettings { + address("localhost:10010") + adminAddress("localhost:10110") + } } notaryNode(1) { p2pPort(10013) - rpcPort(10014) + rpcSettings { + address("localhost:10014") + adminAddress("localhost:10114") + } } notaryNode(2) { p2pPort(10017) - rpcPort(10018) + rpcSettings { + address("localhost:10018") + adminAddress("localhost:10118") + } } notaryNode(3) { p2pPort(10021) - rpcPort(10022) + rpcSettings { + address("localhost:10022") + adminAddress("localhost:10122") + } } } diff --git a/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/CustomNotaryCordform.kt b/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/CustomNotaryCordform.kt index 8f04c33c3b..e9a857b1cb 100644 --- a/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/CustomNotaryCordform.kt +++ b/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/CustomNotaryCordform.kt @@ -17,18 +17,27 @@ class CustomNotaryCordform : CordformDefinition() { node { name(ALICE_NAME) p2pPort(10002) - rpcPort(10003) + rpcSettings { + address("localhost:10003") + adminAddress("localhost:10103") + } rpcUsers(notaryDemoUser) } node { name(BOB_NAME) p2pPort(10005) - rpcPort(10006) + rpcSettings { + address("localhost:10006") + adminAddress("localhost:10106") + } } node { name(DUMMY_NOTARY_NAME) p2pPort(10009) - rpcPort(10010) + rpcSettings { + address("localhost:10010") + adminAddress("localhost:10110") + } notary(NotaryConfig(validating = true, custom = true)) } } diff --git a/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/RaftNotaryCordform.kt b/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/RaftNotaryCordform.kt index d6898321aa..c4cbcd0d8e 100644 --- a/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/RaftNotaryCordform.kt +++ b/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/RaftNotaryCordform.kt @@ -29,13 +29,19 @@ class RaftNotaryCordform : CordformDefinition() { node { name(ALICE_NAME) p2pPort(10002) - rpcPort(10003) + rpcSettings { + address("localhost:10003") + adminAddress("localhost:10103") + } rpcUsers(notaryDemoUser) } node { name(BOB_NAME) p2pPort(10005) - rpcPort(10006) + rpcSettings { + address("localhost:10006") + adminAddress("localhost:10106") + } } fun notaryNode(index: Int, nodePort: Int, clusterPort: Int? = null, configure: CordformNode.() -> Unit) = node { name(notaryNames[index]) @@ -45,15 +51,24 @@ class RaftNotaryCordform : CordformDefinition() { } notaryNode(0, 10008) { p2pPort(10009) - rpcPort(10010) + rpcSettings { + address("localhost:10010") + adminAddress("localhost:10110") + } } notaryNode(1, 10012, 10008) { p2pPort(10013) - rpcPort(10014) + rpcSettings { + address("localhost:10014") + adminAddress("localhost:10114") + } } notaryNode(2, 10016, 10008) { p2pPort(10017) - rpcPort(10018) + rpcSettings { + address("localhost:10018") + adminAddress("localhost:10118") + } } } diff --git a/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/SingleNotaryCordform.kt b/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/SingleNotaryCordform.kt index a7a64d2816..af449ce5cb 100644 --- a/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/SingleNotaryCordform.kt +++ b/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/SingleNotaryCordform.kt @@ -23,18 +23,27 @@ class SingleNotaryCordform : CordformDefinition() { node { name(ALICE_NAME) p2pPort(10002) - rpcPort(10003) + rpcSettings { + address("localhost:10003") + adminAddress("localhost:10103") + } rpcUsers(notaryDemoUser) } node { name(BOB_NAME) p2pPort(10005) - rpcPort(10006) + rpcSettings { + address("localhost:10006") + adminAddress("localhost:10106") + } } node { name(DUMMY_NOTARY_NAME) p2pPort(10009) - rpcPort(10010) + rpcSettings { + address("localhost:10010") + adminAddress("localhost:10110") + } notary(NotaryConfig(validating = true)) } } diff --git a/samples/simm-valuation-demo/build.gradle b/samples/simm-valuation-demo/build.gradle index 36b214c4cb..b560d1aa4f 100644 --- a/samples/simm-valuation-demo/build.gradle +++ b/samples/simm-valuation-demo/build.gradle @@ -77,7 +77,10 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) { name "O=Bank A,L=London,C=GB" p2pPort 10004 webPort 10005 - rpcPort 10006 + rpcSettings { + address("localhost:10016") + adminAddress("localhost:10017") + } cordapps = ["$project.group:finance:$corda_release_version"] rpcUsers = ext.rpcUsers extraConfig = [ @@ -88,7 +91,10 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) { name "O=Bank B,L=New York,C=US" p2pPort 10007 webPort 10008 - rpcPort 10009 + rpcSettings { + address("localhost:10026") + adminAddress("localhost:10027") + } cordapps = ["$project.group:finance:$corda_release_version"] rpcUsers = ext.rpcUsers extraConfig = [ @@ -99,7 +105,10 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) { name "O=Bank C,L=Tokyo,C=JP" p2pPort 10010 webPort 10011 - rpcPort 10012 + rpcSettings { + address("localhost:10036") + adminAddress("localhost:10037") + } cordapps = ["$project.group:finance:$corda_release_version"] rpcUsers = ext.rpcUsers extraConfig = [ diff --git a/samples/trader-demo/build.gradle b/samples/trader-demo/build.gradle index 1a00d51bec..bbb6763354 100644 --- a/samples/trader-demo/build.gradle +++ b/samples/trader-demo/build.gradle @@ -55,23 +55,32 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) { node { name "O=Bank A,L=London,C=GB" p2pPort 10005 - rpcPort 10006 cordapps = ["$project.group:finance:$corda_release_version"] rpcUsers = ext.rpcUsers + rpcSettings { + address "localhost:10006" + adminAddress "localhost:10007" + } } node { name "O=Bank B,L=New York,C=US" p2pPort 10008 - rpcPort 10009 cordapps = ["$project.group:finance:$corda_release_version"] rpcUsers = ext.rpcUsers + rpcSettings { + address "localhost:10009" + adminAddress "localhost:10010" + } } node { name "O=BankOfCorda,L=New York,C=US" p2pPort 10011 - rpcPort 10012 cordapps = ["$project.group:finance:$corda_release_version"] rpcUsers = ext.rpcUsers + rpcSettings { + address "localhost:10012" + adminAddress "localhost:10013" + } } } diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/demorun/CordformUtils.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/demorun/CordformUtils.kt index 5807bd893b..b40b1695e2 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/demorun/CordformUtils.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/demorun/CordformUtils.kt @@ -4,6 +4,8 @@ package net.corda.testing.node.internal.demorun import net.corda.cordform.CordformDefinition import net.corda.cordform.CordformNode +import net.corda.cordform.RpcSettings +import net.corda.cordform.SslOptions import net.corda.core.identity.CordaX500Name import net.corda.node.services.config.NotaryConfig import net.corda.nodeapi.internal.config.toConfig @@ -22,3 +24,11 @@ fun CordformNode.rpcUsers(vararg users: User) { fun CordformNode.notary(notaryConfig: NotaryConfig) { notary = notaryConfig.toConfig().root().unwrapped() } + +fun CordformNode.rpcSettings(configure: RpcSettings.() -> Unit) { + RpcSettings().also(configure).also(this::rpcSettings) +} + +fun RpcSettings.ssl(configure: SslOptions.() -> Unit) { + SslOptions().also(configure).also(this::ssl) +} \ No newline at end of file diff --git a/testing/smoke-test-utils/src/main/kotlin/net/corda/smoketesting/NodeConfig.kt b/testing/smoke-test-utils/src/main/kotlin/net/corda/smoketesting/NodeConfig.kt index 4f002c6f9d..03c253f5c4 100644 --- a/testing/smoke-test-utils/src/main/kotlin/net/corda/smoketesting/NodeConfig.kt +++ b/testing/smoke-test-utils/src/main/kotlin/net/corda/smoketesting/NodeConfig.kt @@ -12,6 +12,7 @@ class NodeConfig( val legalName: CordaX500Name, val p2pPort: Int, val rpcPort: Int, + val rpcAdminPort: Int, val webPort: Int, val isNotary: Boolean, val users: List @@ -32,7 +33,10 @@ class NodeConfig( .withValue("myLegalName", valueFor(legalName.toString())) .withValue("p2pAddress", addressValueFor(p2pPort)) .withValue("webAddress", addressValueFor(webPort)) - .withValue("rpcAddress", addressValueFor(rpcPort)) + .withValue("rpcSettings", empty() + .withValue("address", addressValueFor(rpcPort)) + .withValue("adminAddress", addressValueFor(rpcAdminPort)) + .root()) .withValue("rpcUsers", valueFor(users.map(User::toMap).toList())) .withValue("useTestClock", valueFor(true)) return if (isNotary) { diff --git a/testing/smoke-test-utils/src/main/kotlin/net/corda/smoketesting/NodeProcess.kt b/testing/smoke-test-utils/src/main/kotlin/net/corda/smoketesting/NodeProcess.kt index ffe478998d..38ebfc9d2b 100644 --- a/testing/smoke-test-utils/src/main/kotlin/net/corda/smoketesting/NodeProcess.kt +++ b/testing/smoke-test-utils/src/main/kotlin/net/corda/smoketesting/NodeProcess.kt @@ -11,6 +11,7 @@ import net.corda.core.utilities.contextLogger import net.corda.nodeapi.internal.network.NetworkParametersCopier import net.corda.testing.common.internal.testNetworkParameters import net.corda.testing.common.internal.asContextEnv +import java.net.URL import java.nio.file.Path import java.nio.file.Paths import java.time.Instant @@ -51,8 +52,13 @@ class NodeProcess( // as a CorDapp for the nodes. class Factory( val buildDirectory: Path = Paths.get("build"), - val cordaJar: Path = Paths.get(this::class.java.getResource("/corda.jar").toURI()) + val cordaJarUrl: URL? = this::class.java.getResource("/corda.jar") ) { + val cordaJar: Path by lazy { + require(cordaJarUrl != null, { "corda.jar could not be found in classpath" }) + Paths.get(cordaJarUrl!!.toURI()) + } + private companion object { val javaPath: Path = Paths.get(System.getProperty("java.home"), "bin", "java") val formatter: DateTimeFormatter = DateTimeFormatter.ofPattern("yyyyMMddHHmmss").withZone(systemDefault()) diff --git a/webserver/src/main/kotlin/net/corda/webserver/WebServerConfig.kt b/webserver/src/main/kotlin/net/corda/webserver/WebServerConfig.kt index fab23b80bc..1f74da48ac 100644 --- a/webserver/src/main/kotlin/net/corda/webserver/WebServerConfig.kt +++ b/webserver/src/main/kotlin/net/corda/webserver/WebServerConfig.kt @@ -18,7 +18,7 @@ class WebServerConfig(override val baseDirectory: Path, val config: Config) : No val myLegalName: String by config val rpcAddress: NetworkHostAndPort by lazy { if (config.hasPath("rpcSettings.address")) { - return@lazy NetworkHostAndPort.parse(config.getString("rpcSettings.address")) + return@lazy NetworkHostAndPort.parse(config.getConfig("rpcSettings").getString("address")) } if (config.hasPath("rpcAddress")) { return@lazy NetworkHostAndPort.parse(config.getString("rpcAddress")) From 371cb0a0812dca0a1315ed7e5e9dc17d8097d527 Mon Sep 17 00:00:00 2001 From: josecoll Date: Thu, 25 Jan 2018 14:40:11 +0000 Subject: [PATCH 31/36] Gradle plugins: additional of publish sources configuration field (#2420) * Gradle plugins change: publication of source code is config driven (defaults to true). --- constants.properties | 2 +- .../groovy/net/corda/plugins/ProjectPublishExtension.groovy | 5 +++++ .../src/main/groovy/net/corda/plugins/PublishTasks.groovy | 4 +++- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/constants.properties b/constants.properties index 019e9d8e1a..7744042d3c 100644 --- a/constants.properties +++ b/constants.properties @@ -1,4 +1,4 @@ -gradlePluginsVersion=3.0.3 +gradlePluginsVersion=3.0.4 kotlinVersion=1.1.60 platformVersion=2 guavaVersion=21.0 diff --git a/gradle-plugins/publish-utils/src/main/groovy/net/corda/plugins/ProjectPublishExtension.groovy b/gradle-plugins/publish-utils/src/main/groovy/net/corda/plugins/ProjectPublishExtension.groovy index 97029028e3..edb543fa51 100644 --- a/gradle-plugins/publish-utils/src/main/groovy/net/corda/plugins/ProjectPublishExtension.groovy +++ b/gradle-plugins/publish-utils/src/main/groovy/net/corda/plugins/ProjectPublishExtension.groovy @@ -31,4 +31,9 @@ class ProjectPublishExtension { * True if publishing a WAR instead of a JAR. Forces disableDefaultJAR to "true" when true */ Boolean publishWar = false + + /** + * True if publishing sources to remote repositories + */ + Boolean publishSources = true } \ No newline at end of file diff --git a/gradle-plugins/publish-utils/src/main/groovy/net/corda/plugins/PublishTasks.groovy b/gradle-plugins/publish-utils/src/main/groovy/net/corda/plugins/PublishTasks.groovy index f25e07da6a..7112ee8117 100644 --- a/gradle-plugins/publish-utils/src/main/groovy/net/corda/plugins/PublishTasks.groovy +++ b/gradle-plugins/publish-utils/src/main/groovy/net/corda/plugins/PublishTasks.groovy @@ -56,7 +56,9 @@ class PublishTasks implements Plugin { groupId project.group artifactId publishName - artifact project.tasks.sourceJar + if (publishConfig.publishSources) { + artifact project.tasks.sourceJar + } artifact project.tasks.javadocJar project.configurations.publish.artifacts.each { From 471907366b94eba6fe6da19cb6b43c06b5bece6c Mon Sep 17 00:00:00 2001 From: Joel Dudley Date: Thu, 25 Jan 2018 15:14:32 +0000 Subject: [PATCH 32/36] Renames Controller to NetworkMapAndNotary, in line with templates and example CorDapp. --- docs/source/generating-a-node.rst | 8 ++++---- docs/source/hello-world-running.rst | 8 ++++---- docs/source/tutorial-cordapp.rst | 18 +++++++++--------- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/docs/source/generating-a-node.rst b/docs/source/generating-a-node.rst index eee15b3d5d..6a11dc2721 100644 --- a/docs/source/generating-a-node.rst +++ b/docs/source/generating-a-node.rst @@ -78,9 +78,9 @@ nodes. Here is an example ``Cordform`` task called ``deployNodes`` that creates task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) { directory "./build/nodes" - networkMap "O=Controller,L=London,C=GB" + networkMap "O=NetworkMapAndNotary,L=London,C=GB" node { - name "O=Controller,L=London,C=GB" + name "O=NetworkMapAndNotary,L=London,C=GB" // The notary will offer a validating notary service. notary = [validating : true] p2pPort 10002 @@ -114,7 +114,7 @@ nodes. Here is an example ``Cordform`` task called ``deployNodes`` that creates Running this task will create three nodes in the ``build/nodes`` folder: -* A ``Controller`` node that: +* A ``NetworkMapAndNotary`` node that: * Serves as the network map * Offers a validating notary service @@ -123,7 +123,7 @@ Running this task will create three nodes in the ``build/nodes`` folder: * ``PartyA`` and ``PartyB`` nodes that: - * Are pointing at the ``Controller`` as the network map service + * Are pointing at the ``NetworkMapAndNotary`` as the network map service * Are not offering any services * Will have a webserver (since ``webPort`` is defined) * Are running the ``corda-finance`` CorDapp diff --git a/docs/source/hello-world-running.rst b/docs/source/hello-world-running.rst index 58e3271c71..ac42ce69cd 100644 --- a/docs/source/hello-world-running.rst +++ b/docs/source/hello-world-running.rst @@ -13,7 +13,7 @@ Deploying our CorDapp --------------------- Let's take a look at the nodes we're going to deploy. Open the project's ``build.gradle`` file and scroll down to the ``task deployNodes`` section. This section defines three nodes. There are two standard nodes (``PartyA`` and -``PartyB``), plus a special Controller node that is running the network map service and advertises a validating notary +``PartyB``), plus a special network map/notary node that is running the network map service and advertises a validating notary service. .. code:: bash @@ -21,7 +21,7 @@ service. task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) { directory "./build/nodes" node { - name "O=Controller,L=London,C=GB" + name "O=NetworkMapAndNotary,L=London,C=GB" notary = [validating : true] p2pPort 10002 rpcPort 10003 @@ -142,7 +142,7 @@ The vaults of PartyA and PartyB should both display the following output: - "C=GB,L=London,O=PartyA" - "C=US,L=New York,O=PartyB" contract: "com.template.contract.IOUContract" - notary: "C=GB,L=London,O=Controller,CN=corda.notary.validating" + notary: "C=GB,L=London,O=NetworkMapAndNotary,CN=corda.notary.validating" encumbrance: null constraint: attachmentId: "F578320232CAB87BB1E919F3E5DB9D81B7346F9D7EA6D9155DC0F7BA8E472552" @@ -157,7 +157,7 @@ The vaults of PartyA and PartyB should both display the following output: recordedTime: 1506415268.875000000 consumedTime: null status: "UNCONSUMED" - notary: "C=GB,L=London,O=Controller,CN=corda.notary.validating" + notary: "C=GB,L=London,O=NetworkMapAndNotary,CN=corda.notary.validating" lockId: null lockUpdateTime: 1506415269.548000000 totalStatesAvailable: -1 diff --git a/docs/source/tutorial-cordapp.rst b/docs/source/tutorial-cordapp.rst index 7873819f82..240a9c2eb9 100644 --- a/docs/source/tutorial-cordapp.rst +++ b/docs/source/tutorial-cordapp.rst @@ -17,7 +17,7 @@ if: We will deploy the CorDapp on 4 test nodes: -* **Controller**, which hosts a validating notary service +* **NetworkMapAndNotary**, which hosts a validating notary service * **PartyA** * **PartyB** * **PartyC** @@ -252,7 +252,7 @@ For each node, the ``runnodes`` script creates a node tab/window: Fri Jul 07 10:33:47 BST 2017>>> -For every node except the controller, the script also creates a webserver terminal tab/window: +For every node except the network map/notary, the script also creates a webserver terminal tab/window: .. sourcecode:: none @@ -276,7 +276,7 @@ IntelliJ The node driver defined in ``/src/test/kotlin/com/example/Main.kt`` allows you to specify how many nodes you would like to run and the configuration settings for each node. For the example CorDapp, the driver starts up four nodes - and adds an RPC user for all but the "Controller" node (which serves as the notary): + and adds an RPC user for all but the network map/notary node: .. sourcecode:: kotlin @@ -284,7 +284,7 @@ IntelliJ // No permissions required as we are not invoking flows. val user = User("user1", "test", permissions = setOf()) driver(isDebug = true, waitForNodesToFinish = true) { - startNode(getX500Name(O="Controller",L="London",C='GB"), setOf(ServiceInfo(ValidatingNotaryService.type))) + startNode(getX500Name(O="NetworkMapAndNotary",L="London",C='GB"), setOf(ServiceInfo(ValidatingNotaryService.type))) val (nodeA, nodeB, nodeC) = Futures.allAsList( startNode(getX500Name(O="PartyA",L="London",C="GB"), rpcUsers = listOf(user)), startNode(getX500Name(O="PartyB",L="New York",C="US"), rpcUsers = listOf(user)), @@ -475,15 +475,15 @@ The nodes can be split across machines and configured to communicate across the After deploying the nodes, navigate to the build folder (``kotlin-source/build/nodes``) and move some of the individual node folders to a different machine (e.g. using a USB key). It is important that none of the nodes - including the -controller node - end up on more than one machine. Each computer should also have a copy of ``runnodes`` and +network map/notary node - end up on more than one machine. Each computer should also have a copy of ``runnodes`` and ``runnodes.bat``. For example, you may end up with the following layout: -* Machine 1: ``controller``, ``nodea``, ``runnodes``, ``runnodes.bat`` -* Machine 2: ``nodeb``, ``nodec``, ``runnodes``, ``runnodes.bat`` +* Machine 1: ``NetworkMapAndNotary``, ``PartyA``, ``runnodes``, ``runnodes.bat`` +* Machine 2: ``PartyB``, ``PartyC``, ``runnodes``, ``runnodes.bat`` -You must now edit the configuration file for each node, including the controller. Open each node's config file, +You must now edit the configuration file for each node, including the network map/notary. Open each node's config file, and make the following changes: * Change the Artemis messaging address to the machine's IP address (e.g. ``p2pAddress="10.18.0.166:10006"``) @@ -504,7 +504,7 @@ Debugging is done via IntelliJ as follows: // No permissions required as we are not invoking flows. val user = User("user1", "test", permissions = setOf()) driver(isDebug = true, waitForNodesToFinish = true) { - startNode(getX500Name(O="Controller",L="London",C="GB"), setOf(ServiceInfo(ValidatingNotaryService.type))) + startNode(getX500Name(O="NetworkMapAndNotary",L="London",C="GB"), setOf(ServiceInfo(ValidatingNotaryService.type))) val (nodeA, nodeB, nodeC) = Futures.allAsList( startNode(getX500Name(O="PartyA",L=London,C=GB"), rpcUsers = listOf(user)), startNode(getX500Name(O="PartyB",L=New York,C=US"), rpcUsers = listOf(user)), From 242d9cf7ade854d75ec4781edfaf16034b3b6217 Mon Sep 17 00:00:00 2001 From: Andrzej Cichocki Date: Thu, 25 Jan 2018 15:26:13 +0000 Subject: [PATCH 33/36] CORDA-692 Don't include internal packages in API documentation (#2415) * Use G1 for gradle daemon --- build.gradle | 5 +- .../net/corda/core/internal/InternalUtils.kt | 13 +--- docs/build.gradle | 65 +++++++++++-------- docs/packages.md | 2 +- gradle.properties | 1 + .../net/corda/node/internal/AbstractNode.kt | 1 - 6 files changed, 46 insertions(+), 41 deletions(-) diff --git a/build.gradle b/build.gradle index a64dc578eb..66e94fd571 100644 --- a/build.gradle +++ b/build.gradle @@ -56,7 +56,7 @@ buildscript { ext.h2_version = '1.4.194' // Update docs if renamed or removed. ext.postgresql_version = '42.1.4' ext.rxjava_version = '1.2.4' - ext.dokka_version = '0.9.14' + ext.dokka_version = '0.9.16-eap-2' ext.eddsa_version = '0.2.0' ext.dependency_checker_version = '3.0.1' ext.commons_collections_version = '4.1' @@ -73,6 +73,9 @@ buildscript { mavenLocal() mavenCentral() jcenter() + maven { + url 'https://dl.bintray.com/kotlin/kotlin-eap/' + } } dependencies { classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" diff --git a/core/src/main/kotlin/net/corda/core/internal/InternalUtils.kt b/core/src/main/kotlin/net/corda/core/internal/InternalUtils.kt index 70724cb884..6d22445cb6 100644 --- a/core/src/main/kotlin/net/corda/core/internal/InternalUtils.kt +++ b/core/src/main/kotlin/net/corda/core/internal/InternalUtils.kt @@ -254,13 +254,12 @@ fun Class<*>.staticField(name: String): DeclaredField = DeclaredField(thi /** Returns a [DeclaredField] wrapper around the declared (possibly non-public) static field of the receiver [KClass]. */ fun KClass<*>.staticField(name: String): DeclaredField = DeclaredField(java, name, null) -/** @suppress Returns a [DeclaredField] wrapper around the declared (possibly non-public) instance field of the receiver object. */ +/** Returns a [DeclaredField] wrapper around the declared (possibly non-public) instance field of the receiver object. */ fun Any.declaredField(name: String): DeclaredField = DeclaredField(javaClass, name, this) /** * Returns a [DeclaredField] wrapper around the (possibly non-public) instance field of the receiver object, but declared * in its superclass [clazz]. - * @suppress */ fun Any.declaredField(clazz: KClass<*>, name: String): DeclaredField = DeclaredField(clazz.java, name, this) @@ -295,18 +294,12 @@ fun uncheckedCast(obj: T) = obj as U fun Iterable>.toMultiMap(): Map> = this.groupBy({ it.first }) { it.second } -/** - * Provide access to internal method for AttachmentClassLoaderTests - * @suppress - */ +/** Provide access to internal method for AttachmentClassLoaderTests */ fun TransactionBuilder.toWireTransaction(cordappProvider: CordappProvider, serializationContext: SerializationContext): WireTransaction { return toWireTransactionWithContext(cordappProvider, serializationContext) } -/** - * Provide access to internal method for AttachmentClassLoaderTests - * @suppress - */ +/** Provide access to internal method for AttachmentClassLoaderTests */ fun TransactionBuilder.toLedgerTransaction(services: ServicesForResolution, serializationContext: SerializationContext) = toLedgerTransactionWithContext(services, serializationContext) /** Convenience method to get the package name of a class literal. */ diff --git a/docs/build.gradle b/docs/build.gradle index a0d60a371e..0da6f2ecca 100644 --- a/docs/build.gradle +++ b/docs/build.gradle @@ -5,48 +5,57 @@ dependencies { compile rootProject } +def internalPackagePrefixes(sourceDirs) { + def prefixes = [] + // Kotlin allows packages to deviate from the directory structure, but let's assume they don't: + sourceDirs.collect { sourceDir -> + sourceDir.traverse(type: groovy.io.FileType.DIRECTORIES) { + if (it.name == 'internal') { + prefixes.add sourceDir.toPath().relativize(it.toPath()).toString().replace(File.separator, '.') + } + } + } + prefixes +} + ext { // TODO: Add '../client/jfx/src/main/kotlin' and '../client/mock/src/main/kotlin' if we decide to make them into public API dokkaSourceDirs = files('../core/src/main/kotlin', '../client/rpc/src/main/kotlin', '../finance/src/main/kotlin', '../client/jackson/src/main/kotlin', '../testing/test-utils/src/main/kotlin', '../testing/node-driver/src/main/kotlin') + internalPackagePrefixes = internalPackagePrefixes(dokkaSourceDirs) } dokka { - moduleName = 'corda' outputDirectory = file("${rootProject.rootDir}/docs/build/html/api/kotlin") - processConfigurations = ['compile'] - sourceDirs = dokkaSourceDirs - includes = ['packages.md'] - jdkVersion = 8 - - externalDocumentationLink { - url = new URL("http://fasterxml.github.io/jackson-core/javadoc/2.8/") - } - externalDocumentationLink { - url = new URL("https://docs.oracle.com/javafx/2/api/") - } - externalDocumentationLink { - url = new URL("http://www.bouncycastle.org/docs/docs1.5on/") - } } task dokkaJavadoc(type: org.jetbrains.dokka.gradle.DokkaTask) { - moduleName = 'corda' outputFormat = "javadoc" outputDirectory = file("${rootProject.rootDir}/docs/build/html/api/javadoc") - processConfigurations = ['compile'] - sourceDirs = dokkaSourceDirs - includes = ['packages.md'] - jdkVersion = 8 +} - externalDocumentationLink { - url = new URL("http://fasterxml.github.io/jackson-core/javadoc/2.8/") - } - externalDocumentationLink { - url = new URL("https://docs.oracle.com/javafx/2/api/") - } - externalDocumentationLink { - url = new URL("http://www.bouncycastle.org/docs/docs1.5on/") +[dokka, dokkaJavadoc].collect { + it.configure { + moduleName = 'corda' + processConfigurations = ['compile'] + sourceDirs = dokkaSourceDirs + includes = ['packages.md'] + jdkVersion = 8 + externalDocumentationLink { + url = new URL("http://fasterxml.github.io/jackson-core/javadoc/2.8/") + } + externalDocumentationLink { + url = new URL("https://docs.oracle.com/javafx/2/api/") + } + externalDocumentationLink { + url = new URL("http://www.bouncycastle.org/docs/docs1.5on/") + } + internalPackagePrefixes.collect { packagePrefix -> + packageOptions { + prefix = packagePrefix + suppress = true + } + } } } diff --git a/docs/packages.md b/docs/packages.md index 7331383ccc..2be38afd1e 100644 --- a/docs/packages.md +++ b/docs/packages.md @@ -23,7 +23,7 @@ is complete. # Package net.corda.core.contracts This package contains the base data types for smarts contracts implemented in Corda. To implement a new contract start -with [Contract], or see the examples in [net.corda.finance.contracts]. +with [Contract], or see the examples in `net.corda.finance.contracts`. Corda smart contracts are a combination of state held on the distributed ledger, and verification logic which defines which transformations of state are valid. diff --git a/gradle.properties b/gradle.properties index 152a881d36..59e65ce554 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1 +1,2 @@ kotlin.incremental=true +org.gradle.jvmargs=-XX:+UseG1GC -Xmx1g diff --git a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt index 8b3e992ef1..42355c78f4 100644 --- a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt +++ b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt @@ -508,7 +508,6 @@ abstract class AbstractNode(val configuration: NodeConfiguration, * Installs a flow that's core to the Corda platform. Unlike CorDapp flows which are versioned individually using * [InitiatingFlow.version], core flows have the same version as the node's platform version. To cater for backwards * compatibility [flowFactory] provides a second parameter which is the platform version of the initiating party. - * @suppress */ @VisibleForTesting fun installCoreFlow(clientFlowClass: KClass>, flowFactory: (FlowSession) -> FlowLogic<*>) { From 17a6f61eba907c63b599f780a58ea312ae4eb2bc Mon Sep 17 00:00:00 2001 From: Mike Hearn Date: Thu, 25 Jan 2018 16:29:26 +0100 Subject: [PATCH 34/36] Simplify CashTests and add some comments/convenience APIs to MockServices (#2241) --- .../kotlin/net/corda/core/crypto/Crypto.kt | 1 + .../tutorial/testdsl/CommercialPaperTest.java | 2 +- .../finance/contracts/CommercialPaperTests.kt | 208 +++++------- .../finance/contracts/asset/CashTests.kt | 313 ++++++++---------- .../net/corda/testing/node/MockServices.kt | 65 +++- 5 files changed, 291 insertions(+), 298 deletions(-) diff --git a/core/src/main/kotlin/net/corda/core/crypto/Crypto.kt b/core/src/main/kotlin/net/corda/core/crypto/Crypto.kt index ef365c7a4f..98c150b5b8 100644 --- a/core/src/main/kotlin/net/corda/core/crypto/Crypto.kt +++ b/core/src/main/kotlin/net/corda/core/crypto/Crypto.kt @@ -497,6 +497,7 @@ object Crypto { * It returns true if it succeeds, but it always throws an exception if verification fails. * Strategy on identifying the actual signing scheme is based on the [PublicKey] type, but if the schemeCodeName is known, * then better use doVerify(schemeCodeName: String, publicKey: PublicKey, signatureData: ByteArray, clearData: ByteArray). + * * @param publicKey the signer's [PublicKey]. * @param signatureData the signatureData on a message. * @param clearData the clear data/message that was signed (usually the Merkle root). diff --git a/docs/source/example-code/src/test/java/net/corda/docs/java/tutorial/testdsl/CommercialPaperTest.java b/docs/source/example-code/src/test/java/net/corda/docs/java/tutorial/testdsl/CommercialPaperTest.java index 91516ee751..0e206bf77c 100644 --- a/docs/source/example-code/src/test/java/net/corda/docs/java/tutorial/testdsl/CommercialPaperTest.java +++ b/docs/source/example-code/src/test/java/net/corda/docs/java/tutorial/testdsl/CommercialPaperTest.java @@ -32,7 +32,7 @@ public class CommercialPaperTest { private static final TestIdentity BOB = new TestIdentity(BOB_NAME, 80L); private static final TestIdentity MEGA_CORP = new TestIdentity(new CordaX500Name("MegaCorp", "London", "GB")); private final byte[] defaultRef = {123}; - private final MockServices ledgerServices = new MockServices(emptyList(), makeTestIdentityService(MEGA_CORP.getIdentity())); + private final MockServices ledgerServices = new MockServices(MEGA_CORP); // DOCSTART 1 private ICommercialPaperState getPaper() { diff --git a/finance/src/test/kotlin/net/corda/finance/contracts/CommercialPaperTests.kt b/finance/src/test/kotlin/net/corda/finance/contracts/CommercialPaperTests.kt index 12aa01f5f1..43ebe167fd 100644 --- a/finance/src/test/kotlin/net/corda/finance/contracts/CommercialPaperTests.kt +++ b/finance/src/test/kotlin/net/corda/finance/contracts/CommercialPaperTests.kt @@ -1,27 +1,23 @@ package net.corda.finance.contracts -import com.nhaarman.mockito_kotlin.doReturn -import com.nhaarman.mockito_kotlin.whenever import net.corda.core.contracts.* -import net.corda.core.crypto.generateKeyPair import net.corda.core.identity.AnonymousParty import net.corda.core.identity.CordaX500Name import net.corda.core.identity.Party import net.corda.core.node.services.Vault -import net.corda.core.node.services.VaultService import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.TransactionBuilder import net.corda.core.utilities.days import net.corda.core.utilities.seconds import net.corda.finance.DOLLARS import net.corda.finance.`issued by` -import net.corda.finance.contracts.asset.* -import net.corda.node.services.api.IdentityServiceInternal +import net.corda.finance.contracts.asset.CASH +import net.corda.finance.contracts.asset.Cash +import net.corda.finance.contracts.asset.STATE import net.corda.testing.core.* import net.corda.testing.dsl.EnforceVerifyOrFail import net.corda.testing.dsl.TransactionDSL import net.corda.testing.dsl.TransactionDSLInterpreter -import net.corda.testing.internal.rigorousMock import net.corda.testing.internal.vault.VaultFiller import net.corda.testing.node.MockServices import net.corda.testing.node.MockServices.Companion.makeTestDatabaseAndMockServices @@ -48,15 +44,12 @@ interface ICommercialPaperTestTemplate { } private val megaCorp = TestIdentity(CordaX500Name("MegaCorp", "London", "GB")) -private val MEGA_CORP get() = megaCorp.party -private val MEGA_CORP_IDENTITY get() = megaCorp.identity -private val MEGA_CORP_PUBKEY get() = megaCorp.publicKey class JavaCommercialPaperTest : ICommercialPaperTestTemplate { override fun getPaper(): ICommercialPaperState = JavaCommercialPaper.State( - MEGA_CORP.ref(123), - MEGA_CORP, - 1000.DOLLARS `issued by` MEGA_CORP.ref(123), + megaCorp.ref(123), + megaCorp.party, + 1000.DOLLARS `issued by` megaCorp.ref(123), TEST_TX_TIME + 7.days ) @@ -68,9 +61,9 @@ class JavaCommercialPaperTest : ICommercialPaperTestTemplate { class KotlinCommercialPaperTest : ICommercialPaperTestTemplate { override fun getPaper(): ICommercialPaperState = CommercialPaper.State( - issuance = MEGA_CORP.ref(123), - owner = MEGA_CORP, - faceValue = 1000.DOLLARS `issued by` MEGA_CORP.ref(123), + issuance = megaCorp.ref(123), + owner = megaCorp.party, + faceValue = 1000.DOLLARS `issued by` megaCorp.ref(123), maturityDate = TEST_TX_TIME + 7.days ) @@ -82,9 +75,9 @@ class KotlinCommercialPaperTest : ICommercialPaperTestTemplate { class KotlinCommercialPaperLegacyTest : ICommercialPaperTestTemplate { override fun getPaper(): ICommercialPaperState = CommercialPaper.State( - issuance = MEGA_CORP.ref(123), - owner = MEGA_CORP, - faceValue = 1000.DOLLARS `issued by` MEGA_CORP.ref(123), + issuance = megaCorp.ref(123), + owner = megaCorp.party, + faceValue = 1000.DOLLARS `issued by` megaCorp.ref(123), maturityDate = TEST_TX_TIME + 7.days ) @@ -102,49 +95,36 @@ class CommercialPaperTestsGeneric { fun data() = listOf(JavaCommercialPaperTest(), KotlinCommercialPaperTest(), KotlinCommercialPaperLegacyTest()) private val dummyCashIssuer = TestIdentity(CordaX500Name("Snake Oil Issuer", "London", "GB"), 10) - private val DUMMY_CASH_ISSUER_IDENTITY get() = dummyCashIssuer.identity - private val DUMMY_CASH_ISSUER = dummyCashIssuer.ref(1) - private val alice = TestIdentity(ALICE_NAME, 70) - private val BIG_CORP_KEY = generateKeyPair() private val dummyNotary = TestIdentity(DUMMY_NOTARY_NAME, 20) + private val alice = TestIdentity(ALICE_NAME, 70) private val miniCorp = TestIdentity(CordaX500Name("MiniCorp", "London", "GB")) - private val ALICE get() = alice.party - private val ALICE_KEY get() = alice.keyPair - private val ALICE_PUBKEY get() = alice.publicKey - private val DUMMY_NOTARY get() = dummyNotary.party - private val DUMMY_NOTARY_IDENTITY get() = dummyNotary.identity - private val MINI_CORP get() = miniCorp.party - private val MINI_CORP_IDENTITY get() = miniCorp.identity - private val MINI_CORP_PUBKEY get() = miniCorp.publicKey } @Parameterized.Parameter lateinit var thisTest: ICommercialPaperTestTemplate + @Rule @JvmField val testSerialization = SerializationEnvironmentRule() - val issuer = MEGA_CORP.ref(123) - private val ledgerServices = MockServices(emptyList(), rigorousMock().also { - doReturn(MEGA_CORP).whenever(it).partyFromKey(MEGA_CORP_PUBKEY) - doReturn(MINI_CORP).whenever(it).partyFromKey(MINI_CORP_PUBKEY) - doReturn(null).whenever(it).partyFromKey(ALICE_PUBKEY) - }, MEGA_CORP.name) + + private val megaCorpRef = megaCorp.ref(123) + private val ledgerServices = MockServices(megaCorp, miniCorp) @Test fun `trade lifecycle test`() { - val someProfits = 1200.DOLLARS `issued by` issuer - ledgerServices.ledger(DUMMY_NOTARY) { + val someProfits = 1200.DOLLARS `issued by` megaCorpRef + ledgerServices.ledger(dummyNotary.party) { unverifiedTransaction { attachment(Cash.PROGRAM_ID) - output(Cash.PROGRAM_ID, "alice's $900", 900.DOLLARS.CASH issuedBy issuer ownedBy ALICE) - output(Cash.PROGRAM_ID, "some profits", someProfits.STATE ownedBy MEGA_CORP) + output(Cash.PROGRAM_ID, "alice's $900", 900.DOLLARS.CASH issuedBy megaCorpRef ownedBy alice.party) + output(Cash.PROGRAM_ID, "some profits", someProfits.STATE ownedBy megaCorp.party) } // Some CP is issued onto the ledger by MegaCorp. transaction("Issuance") { attachments(CP_PROGRAM_ID, JavaCommercialPaper.JCP_PROGRAM_ID) output(thisTest.getContract(), "paper", thisTest.getPaper()) - command(MEGA_CORP_PUBKEY, thisTest.getIssueCommand(DUMMY_NOTARY)) + command(megaCorp.publicKey, thisTest.getIssueCommand(dummyNotary.party)) timeWindow(TEST_TX_TIME) this.verifies() } @@ -155,10 +135,10 @@ class CommercialPaperTestsGeneric { attachments(Cash.PROGRAM_ID, JavaCommercialPaper.JCP_PROGRAM_ID) input("paper") input("alice's $900") - output(Cash.PROGRAM_ID, "borrowed $900", 900.DOLLARS.CASH issuedBy issuer ownedBy MEGA_CORP) - output(thisTest.getContract(), "alice's paper", "paper".output().withOwner(ALICE)) - command(ALICE_PUBKEY, Cash.Commands.Move()) - command(MEGA_CORP_PUBKEY, thisTest.getMoveCommand()) + output(Cash.PROGRAM_ID, "borrowed $900", 900.DOLLARS.CASH issuedBy megaCorpRef ownedBy megaCorp.party) + output(thisTest.getContract(), "alice's paper", "paper".output().withOwner(alice.party)) + command(alice.publicKey, Cash.Commands.Move()) + command(megaCorp.publicKey, thisTest.getMoveCommand()) this.verifies() } @@ -170,17 +150,17 @@ class CommercialPaperTestsGeneric { input("some profits") fun TransactionDSL.outputs(aliceGetsBack: Amount>) { - output(Cash.PROGRAM_ID, "Alice's profit", aliceGetsBack.STATE ownedBy ALICE) - output(Cash.PROGRAM_ID, "Change", (someProfits - aliceGetsBack).STATE ownedBy MEGA_CORP) + output(Cash.PROGRAM_ID, "Alice's profit", aliceGetsBack.STATE ownedBy alice.party) + output(Cash.PROGRAM_ID, "Change", (someProfits - aliceGetsBack).STATE ownedBy megaCorp.party) } - command(MEGA_CORP_PUBKEY, Cash.Commands.Move()) - command(ALICE_PUBKEY, thisTest.getRedeemCommand(DUMMY_NOTARY)) + command(megaCorp.publicKey, Cash.Commands.Move()) + command(alice.publicKey, thisTest.getRedeemCommand(dummyNotary.party)) tweak { - outputs(700.DOLLARS `issued by` issuer) + outputs(700.DOLLARS `issued by` megaCorpRef) timeWindow(TEST_TX_TIME + 8.days) this `fails with` "received amount equals the face value" } - outputs(1000.DOLLARS `issued by` issuer) + outputs(1000.DOLLARS `issued by` megaCorpRef) tweak { @@ -200,7 +180,7 @@ class CommercialPaperTestsGeneric { } private fun transaction(script: TransactionDSL.() -> EnforceVerifyOrFail) = run { - ledgerServices.transaction(DUMMY_NOTARY, script) + ledgerServices.transaction(dummyNotary.party, script) } @Test @@ -209,7 +189,7 @@ class CommercialPaperTestsGeneric { attachment(CP_PROGRAM_ID) attachment(JavaCommercialPaper.JCP_PROGRAM_ID) output(thisTest.getContract(), thisTest.getPaper()) - command(MINI_CORP_PUBKEY, thisTest.getIssueCommand(DUMMY_NOTARY)) + command(miniCorp.publicKey, thisTest.getIssueCommand(dummyNotary.party)) timeWindow(TEST_TX_TIME) this `fails with` "output states are issued by a command signer" } @@ -220,8 +200,8 @@ class CommercialPaperTestsGeneric { transaction { attachment(CP_PROGRAM_ID) attachment(JavaCommercialPaper.JCP_PROGRAM_ID) - output(thisTest.getContract(), thisTest.getPaper().withFaceValue(0.DOLLARS `issued by` issuer)) - command(MEGA_CORP_PUBKEY, thisTest.getIssueCommand(DUMMY_NOTARY)) + output(thisTest.getContract(), thisTest.getPaper().withFaceValue(0.DOLLARS `issued by` megaCorpRef)) + command(megaCorp.publicKey, thisTest.getIssueCommand(dummyNotary.party)) timeWindow(TEST_TX_TIME) this `fails with` "output values sum to more than the inputs" } @@ -233,7 +213,7 @@ class CommercialPaperTestsGeneric { attachment(CP_PROGRAM_ID) attachment(JavaCommercialPaper.JCP_PROGRAM_ID) output(thisTest.getContract(), thisTest.getPaper().withMaturityDate(TEST_TX_TIME - 10.days)) - command(MEGA_CORP_PUBKEY, thisTest.getIssueCommand(DUMMY_NOTARY)) + command(megaCorp.publicKey, thisTest.getIssueCommand(dummyNotary.party)) timeWindow(TEST_TX_TIME) this `fails with` "maturity date is not in the past" } @@ -246,95 +226,83 @@ class CommercialPaperTestsGeneric { attachment(JavaCommercialPaper.JCP_PROGRAM_ID) input(thisTest.getContract(), thisTest.getPaper()) output(thisTest.getContract(), thisTest.getPaper()) - command(MEGA_CORP_PUBKEY, thisTest.getIssueCommand(DUMMY_NOTARY)) + command(megaCorp.publicKey, thisTest.getIssueCommand(dummyNotary.party)) timeWindow(TEST_TX_TIME) this `fails with` "output values sum to more than the inputs" } } - /** - * Unit test requires two separate Database instances to represent each of the two - * transaction participants (enforces uniqueness of vault content in lieu of partipant identity) - */ - - private lateinit var bigCorpServices: MockServices - private lateinit var bigCorpVault: Vault - private lateinit var bigCorpVaultService: VaultService - - private lateinit var aliceServices: MockServices - private lateinit var aliceVaultService: VaultService - private lateinit var alicesVault: Vault - private val notaryServices = MockServices(emptyList(), rigorousMock(), MEGA_CORP.name, dummyNotary.keyPair) - private val issuerServices = MockServices(listOf("net.corda.finance.contracts"), rigorousMock(), MEGA_CORP.name, dummyCashIssuer.keyPair) - private lateinit var moveTX: SignedTransaction @Test fun `issue move and then redeem`() { - val aliceDatabaseAndServices = makeTestDatabaseAndMockServices( - listOf("net.corda.finance.contracts"), - makeTestIdentityService(MEGA_CORP_IDENTITY, MINI_CORP_IDENTITY, DUMMY_CASH_ISSUER_IDENTITY, DUMMY_NOTARY_IDENTITY), - TestIdentity(MEGA_CORP.name, ALICE_KEY)) - val databaseAlice = aliceDatabaseAndServices.first - aliceServices = aliceDatabaseAndServices.second - aliceVaultService = aliceServices.vaultService + // Set up a test environment with 4 parties: + // 1. The notary + // 2. A dummy cash issuer e.g. central bank + // 3. Alice + // 4. MegaCorp + // + // MegaCorp will issue some commercial paper and Alice will buy it, using cash issued to her in the name + // of the dummy cash issuer. - databaseAlice.transaction { - alicesVault = VaultFiller(aliceServices, dummyNotary, rngFactory = ::Random).fillWithSomeTestCash(9000.DOLLARS, issuerServices, 1, DUMMY_CASH_ISSUER) - aliceVaultService = aliceServices.vaultService + val allIdentities = arrayOf(megaCorp.identity, miniCorp.identity, dummyCashIssuer.identity, dummyNotary.identity) + val notaryServices = MockServices(dummyNotary) + val issuerServices = MockServices(dummyCashIssuer, dummyNotary) + val (aliceDatabase, aliceServices) = makeTestDatabaseAndMockServices( + listOf("net.corda.finance.contracts"), + makeTestIdentityService(*allIdentities), + alice + ) + val aliceCash: Vault = aliceDatabase.transaction { + VaultFiller(aliceServices, dummyNotary).fillWithSomeTestCash(9000.DOLLARS, issuerServices, 1, dummyCashIssuer.ref(1)) } - val bigCorpDatabaseAndServices = makeTestDatabaseAndMockServices( - listOf("net.corda.finance.contracts"), - makeTestIdentityService(MEGA_CORP_IDENTITY, MINI_CORP_IDENTITY, DUMMY_CASH_ISSUER_IDENTITY, DUMMY_NOTARY_IDENTITY), - TestIdentity(MEGA_CORP.name, BIG_CORP_KEY)) - val databaseBigCorp = bigCorpDatabaseAndServices.first - bigCorpServices = bigCorpDatabaseAndServices.second - bigCorpVaultService = bigCorpServices.vaultService - databaseBigCorp.transaction { - bigCorpVault = VaultFiller(bigCorpServices, dummyNotary, rngFactory = ::Random).fillWithSomeTestCash(13000.DOLLARS, issuerServices, 1, DUMMY_CASH_ISSUER) - bigCorpVaultService = bigCorpServices.vaultService + val (megaCorpDatabase, megaCorpServices) = makeTestDatabaseAndMockServices( + listOf("net.corda.finance.contracts"), + makeTestIdentityService(*allIdentities), + megaCorp + ) + val bigCorpCash: Vault = megaCorpDatabase.transaction { + VaultFiller(megaCorpServices, dummyNotary).fillWithSomeTestCash(13000.DOLLARS, issuerServices, 1, dummyCashIssuer.ref(1)) } // Propagate the cash transactions to each side. - aliceServices.recordTransactions(bigCorpVault.states.map { bigCorpServices.validatedTransactions.getTransaction(it.ref.txhash)!! }) - bigCorpServices.recordTransactions(alicesVault.states.map { aliceServices.validatedTransactions.getTransaction(it.ref.txhash)!! }) + aliceServices.recordTransactions(bigCorpCash.states.map { megaCorpServices.validatedTransactions.getTransaction(it.ref.txhash)!! }) + megaCorpServices.recordTransactions(aliceCash.states.map { aliceServices.validatedTransactions.getTransaction(it.ref.txhash)!! }) - // BigCorp™ issues $10,000 of commercial paper, to mature in 30 days, owned initially by itself. - val faceValue = 10000.DOLLARS `issued by` DUMMY_CASH_ISSUER - val issuance = bigCorpServices.myInfo.chooseIdentity().ref(1) - val issueBuilder = CommercialPaper().generateIssue(issuance, faceValue, TEST_TX_TIME + 30.days, DUMMY_NOTARY) + // MegaCorp™ issues $10,000 of commercial paper, to mature in 30 days, owned initially by itself. + val faceValue = 10000.DOLLARS `issued by` dummyCashIssuer.ref(1) + val issuance = megaCorpServices.myInfo.chooseIdentity().ref(1) + val issueBuilder = CommercialPaper().generateIssue(issuance, faceValue, TEST_TX_TIME + 30.days, dummyNotary.party) issueBuilder.setTimeWindow(TEST_TX_TIME, 30.seconds) - val issuePtx = bigCorpServices.signInitialTransaction(issueBuilder) + val issuePtx = megaCorpServices.signInitialTransaction(issueBuilder) val issueTx = notaryServices.addSignature(issuePtx) - databaseAlice.transaction { + val moveTX = aliceDatabase.transaction { // Alice pays $9000 to BigCorp to own some of their debt. - moveTX = run { - val builder = TransactionBuilder(DUMMY_NOTARY) - Cash.generateSpend(aliceServices, builder, 9000.DOLLARS, AnonymousParty(BIG_CORP_KEY.public)) - CommercialPaper().generateMove(builder, issueTx.tx.outRef(0), AnonymousParty(ALICE_KEY.public)) - val ptx = aliceServices.signInitialTransaction(builder) - val ptx2 = bigCorpServices.addSignature(ptx) - val stx = notaryServices.addSignature(ptx2) - stx - } + val builder = TransactionBuilder(dummyNotary.party) + Cash.generateSpend(aliceServices, builder, 9000.DOLLARS, alice.identity, AnonymousParty(megaCorp.publicKey)) + CommercialPaper().generateMove(builder, issueTx.tx.outRef(0), AnonymousParty(alice.keyPair.public)) + val ptx = aliceServices.signInitialTransaction(builder) + val ptx2 = megaCorpServices.addSignature(ptx) + val stx = notaryServices.addSignature(ptx2) + stx } - databaseBigCorp.transaction { + megaCorpDatabase.transaction { // Verify the txns are valid and insert into both sides. - listOf(issueTx, moveTX).forEach { - it.toLedgerTransaction(aliceServices).verify() - aliceServices.recordTransactions(it) - bigCorpServices.recordTransactions(it) + for (tx in listOf(issueTx, moveTX)) { + tx.toLedgerTransaction(aliceServices).verify() + aliceServices.recordTransactions(tx) + megaCorpServices.recordTransactions(tx) } } - databaseBigCorp.transaction { + megaCorpDatabase.transaction { fun makeRedeemTX(time: Instant): Pair { - val builder = TransactionBuilder(DUMMY_NOTARY) + val builder = TransactionBuilder(dummyNotary.party) builder.setTimeWindow(time, 30.seconds) - CommercialPaper().generateRedeem(builder, moveTX.tx.outRef(1), bigCorpServices, bigCorpServices.myInfo.chooseIdentityAndCert()) + CommercialPaper().generateRedeem(builder, moveTX.tx.outRef(1), megaCorpServices, megaCorpServices.myInfo.chooseIdentityAndCert()) val ptx = aliceServices.signInitialTransaction(builder) - val ptx2 = bigCorpServices.addSignature(ptx) + val ptx2 = megaCorpServices.addSignature(ptx) val stx = notaryServices.addSignature(ptx2) return Pair(stx, builder.lockId) } @@ -347,7 +315,7 @@ class CommercialPaperTestsGeneric { } // manually release locks held by this failing transaction aliceServices.vaultService.softLockRelease(tooEarlyRedemptionLockId) - assertTrue(e.cause!!.message!!.contains("paper must have matured")) + assertTrue("paper must have matured" in e.cause!!.message!!) val validRedemption = makeRedeemTX(TEST_TX_TIME + 31.days).first validRedemption.toLedgerTransaction(aliceServices).verify() diff --git a/finance/src/test/kotlin/net/corda/finance/contracts/asset/CashTests.kt b/finance/src/test/kotlin/net/corda/finance/contracts/asset/CashTests.kt index aed8d302f8..a2fa76e92c 100644 --- a/finance/src/test/kotlin/net/corda/finance/contracts/asset/CashTests.kt +++ b/finance/src/test/kotlin/net/corda/finance/contracts/asset/CashTests.kt @@ -1,9 +1,11 @@ package net.corda.finance.contracts.asset -import com.nhaarman.mockito_kotlin.* import net.corda.core.contracts.* import net.corda.core.crypto.SecureHash -import net.corda.core.identity.* +import net.corda.core.identity.AbstractParty +import net.corda.core.identity.AnonymousParty +import net.corda.core.identity.CordaX500Name +import net.corda.core.identity.Party import net.corda.core.node.ServiceHub import net.corda.core.node.services.VaultService import net.corda.core.node.services.queryBy @@ -15,16 +17,14 @@ import net.corda.finance.utils.sumCash import net.corda.finance.utils.sumCashBy import net.corda.finance.utils.sumCashOrNull import net.corda.finance.utils.sumCashOrZero -import net.corda.node.services.api.IdentityServiceInternal import net.corda.node.services.vault.NodeVaultService import net.corda.nodeapi.internal.persistence.CordaPersistence import net.corda.testing.contracts.DummyState import net.corda.testing.core.* -import net.corda.testing.internal.LogHelper import net.corda.testing.dsl.EnforceVerifyOrFail import net.corda.testing.dsl.TransactionDSL import net.corda.testing.dsl.TransactionDSLInterpreter -import net.corda.testing.internal.rigorousMock +import net.corda.testing.internal.LogHelper import net.corda.testing.internal.vault.VaultFiller import net.corda.testing.node.MockServices import net.corda.testing.node.MockServices.Companion.makeTestDatabaseAndMockServices @@ -41,41 +41,25 @@ import kotlin.test.* class CashTests { private companion object { val alice = TestIdentity(ALICE_NAME, 70) - val BOB_PUBKEY = TestIdentity(BOB_NAME, 80).publicKey + val bob = TestIdentity(BOB_NAME, 80) val charlie = TestIdentity(CHARLIE_NAME, 90) - val DUMMY_CASH_ISSUER_IDENTITY = TestIdentity(CordaX500Name("Snake Oil Issuer", "London", "GB"), 10).identity + val dummyCashIssuer = TestIdentity(CordaX500Name("Snake Oil Issuer", "London", "GB"), 10) val dummyNotary = TestIdentity(DUMMY_NOTARY_NAME, 20) val megaCorp = TestIdentity(CordaX500Name("MegaCorp", "London", "GB")) val miniCorp = TestIdentity(CordaX500Name("MiniCorp", "London", "GB")) - val ALICE get() = alice.party - val ALICE_PUBKEY get() = alice.publicKey - val CHARLIE get() = charlie.party - val CHARLIE_IDENTITY get() = charlie.identity - val DUMMY_NOTARY get() = dummyNotary.party - val DUMMY_NOTARY_IDENTITY get() = dummyNotary.identity - val DUMMY_NOTARY_KEY get() = dummyNotary.keyPair - val MEGA_CORP get() = megaCorp.party - val MEGA_CORP_IDENTITY get() = megaCorp.identity - val MEGA_CORP_KEY get() = megaCorp.keyPair - val MEGA_CORP_PUBKEY get() = megaCorp.publicKey - val MINI_CORP get() = miniCorp.party - val MINI_CORP_IDENTITY get() = miniCorp.identity - val MINI_CORP_KEY get() = miniCorp.keyPair - val MINI_CORP_PUBKEY get() = miniCorp.publicKey } @Rule @JvmField val testSerialization = SerializationEnvironmentRule() - private val defaultRef = OpaqueBytes.of(1) - private val defaultIssuer = MEGA_CORP.ref(defaultRef) + private val defaultIssuer = megaCorp.ref(1) private val inState = Cash.State( amount = 1000.DOLLARS `issued by` defaultIssuer, - owner = AnonymousParty(ALICE_PUBKEY) + owner = AnonymousParty(alice.publicKey) ) // Input state held by the issuer private val issuerInState = inState.copy(owner = defaultIssuer.party) - private val outState = issuerInState.copy(owner = AnonymousParty(BOB_PUBKEY)) + private val outState = issuerInState.copy(owner = AnonymousParty(bob.publicKey)) private fun Cash.State.editDepositRef(ref: Byte) = copy( amount = Amount(amount.quantity, token = amount.token.copy(amount.token.issuer.copy(reference = OpaqueBytes.of(ref)))) @@ -90,48 +74,42 @@ class CashTests { private lateinit var ourIdentity: AbstractParty private lateinit var miniCorpAnonymised: AnonymousParty - private val CHARLIE_ANONYMISED = CHARLIE_IDENTITY.party.anonymise() - - private lateinit var WALLET: List> + private lateinit var cashStates: List> + // TODO: Optimise this so that we don't throw away and rebuild state that can be shared across tests. @Before fun setUp() { LogHelper.setLevel(NodeVaultService::class) - megaCorpServices = MockServices(listOf("net.corda.finance.contracts.asset"), rigorousMock(), MEGA_CORP.name, MEGA_CORP_KEY) - miniCorpServices = MockServices(listOf("net.corda.finance.contracts.asset"), rigorousMock().also { - doNothing().whenever(it).justVerifyAndRegisterIdentity(argThat { name == MINI_CORP.name }) - }, MINI_CORP.name, MINI_CORP_KEY) - val notaryServices = MockServices(listOf("net.corda.finance.contracts.asset"), rigorousMock(), DUMMY_NOTARY.name, DUMMY_NOTARY_KEY) + megaCorpServices = MockServices(megaCorp) + miniCorpServices = MockServices(miniCorp) val databaseAndServices = makeTestDatabaseAndMockServices( listOf("net.corda.finance.contracts.asset"), - makeTestIdentityService(MEGA_CORP_IDENTITY, MINI_CORP_IDENTITY, DUMMY_CASH_ISSUER_IDENTITY, DUMMY_NOTARY_IDENTITY), - TestIdentity(CordaX500Name("Me", "London", "GB"))) + makeTestIdentityService(megaCorp.identity, miniCorp.identity, dummyCashIssuer.identity, dummyNotary.identity), + TestIdentity(CordaX500Name("Me", "London", "GB")) + ) database = databaseAndServices.first ourServices = databaseAndServices.second // Set up and register identities ourIdentity = ourServices.myInfo.singleIdentity() miniCorpAnonymised = miniCorpServices.myInfo.singleIdentityAndCert().party.anonymise() - (miniCorpServices.myInfo.legalIdentitiesAndCerts + megaCorpServices.myInfo.legalIdentitiesAndCerts + notaryServices.myInfo.legalIdentitiesAndCerts).forEach { identity -> - ourServices.identityService.verifyAndRegisterIdentity(identity) // TODO: Configure a mock identity service instead. - } // Create some cash. Any attempt to spend >$500 will require multiple issuers to be involved. database.transaction { - val vaultFiller = VaultFiller(ourServices, dummyNotary, rngFactory = ::Random) - vaultFiller.fillWithSomeTestCash(100.DOLLARS, megaCorpServices, 1, MEGA_CORP.ref(1), ourIdentity) - vaultFiller.fillWithSomeTestCash(400.DOLLARS, megaCorpServices, 1, MEGA_CORP.ref(1), ourIdentity) - vaultFiller.fillWithSomeTestCash(80.DOLLARS, miniCorpServices, 1, MINI_CORP.ref(1), ourIdentity) - vaultFiller.fillWithSomeTestCash(80.SWISS_FRANCS, miniCorpServices, 1, MINI_CORP.ref(1), ourIdentity) + val vaultFiller = VaultFiller(ourServices, dummyNotary) + vaultFiller.fillWithSomeTestCash(100.DOLLARS, megaCorpServices, 1, megaCorp.ref(1), ourIdentity) + vaultFiller.fillWithSomeTestCash(400.DOLLARS, megaCorpServices, 1, megaCorp.ref(1), ourIdentity) + vaultFiller.fillWithSomeTestCash(80.DOLLARS, miniCorpServices, 1, miniCorp.ref(1), ourIdentity) + vaultFiller.fillWithSomeTestCash(80.SWISS_FRANCS, miniCorpServices, 1, miniCorp.ref(1), ourIdentity) } database.transaction { vaultStatesUnconsumed = ourServices.vaultService.queryBy().states } - WALLET = listOf( - makeCash(100.DOLLARS, MEGA_CORP), - makeCash(400.DOLLARS, MEGA_CORP), - makeCash(80.DOLLARS, MINI_CORP), - makeCash(80.SWISS_FRANCS, MINI_CORP, 2) + cashStates = listOf( + makeCash(100.DOLLARS, megaCorp.party), + makeCash(400.DOLLARS, megaCorp.party), + makeCash(80.DOLLARS, miniCorp.party), + makeCash(80.SWISS_FRANCS, miniCorp.party, 2) ) } @@ -140,13 +118,8 @@ class CashTests { database.close() } - private fun transaction(script: TransactionDSL.() -> EnforceVerifyOrFail) = run { - MockServices(emptyList(), rigorousMock().also { - doReturn(MEGA_CORP).whenever(it).partyFromKey(MEGA_CORP_PUBKEY) - doReturn(MINI_CORP).whenever(it).partyFromKey(MINI_CORP_PUBKEY) - doReturn(null).whenever(it).partyFromKey(ALICE_PUBKEY) - doReturn(null).whenever(it).partyFromKey(BOB_PUBKEY) - }, MEGA_CORP.name).transaction(DUMMY_NOTARY, script) + private fun transaction(script: TransactionDSL.() -> EnforceVerifyOrFail) { + MockServices(megaCorp).transaction(dummyNotary.party, script) } @Test @@ -156,30 +129,30 @@ class CashTests { input(Cash.PROGRAM_ID, inState) tweak { output(Cash.PROGRAM_ID, outState.copy(amount = 2000.DOLLARS `issued by` defaultIssuer)) - command(ALICE_PUBKEY, Cash.Commands.Move()) + command(alice.publicKey, Cash.Commands.Move()) this `fails with` "the amounts balance" } tweak { output(Cash.PROGRAM_ID, outState) - command(ALICE_PUBKEY, DummyCommandData) + command(alice.publicKey, DummyCommandData) // Invalid command this `fails with` "required net.corda.finance.contracts.asset.Cash.Commands.Move command" } tweak { output(Cash.PROGRAM_ID, outState) - command(BOB_PUBKEY, Cash.Commands.Move()) + command(bob.publicKey, Cash.Commands.Move()) this `fails with` "the owning keys are a subset of the signing keys" } tweak { output(Cash.PROGRAM_ID, outState) - output(Cash.PROGRAM_ID, outState issuedBy MINI_CORP) - command(ALICE_PUBKEY, Cash.Commands.Move()) + output(Cash.PROGRAM_ID, outState issuedBy miniCorp.party) + command(alice.publicKey, Cash.Commands.Move()) this `fails with` "at least one cash input" } // Simple reallocation works. tweak { output(Cash.PROGRAM_ID, outState) - command(ALICE_PUBKEY, Cash.Commands.Move()) + command(alice.publicKey, Cash.Commands.Move()) this.verifies() } } @@ -192,7 +165,7 @@ class CashTests { attachment(Cash.PROGRAM_ID) input(Cash.PROGRAM_ID, DummyState()) output(Cash.PROGRAM_ID, outState) - command(MINI_CORP_PUBKEY, Cash.Commands.Move()) + command(miniCorp.publicKey, Cash.Commands.Move()) this `fails with` "there is at least one cash input for this group" } } @@ -204,16 +177,16 @@ class CashTests { transaction { attachment(Cash.PROGRAM_ID) output(Cash.PROGRAM_ID, outState) - command(ALICE_PUBKEY, Cash.Commands.Issue()) + command(alice.publicKey, Cash.Commands.Issue()) this `fails with` "output states are issued by a command signer" } transaction { attachment(Cash.PROGRAM_ID) output(Cash.PROGRAM_ID, Cash.State( - amount = 1000.DOLLARS `issued by` MINI_CORP.ref(12, 34), - owner = AnonymousParty(ALICE_PUBKEY))) - command(MINI_CORP_PUBKEY, Cash.Commands.Issue()) + amount = 1000.DOLLARS `issued by` miniCorp.ref(12, 34), + owner = AnonymousParty(alice.publicKey))) + command(miniCorp.publicKey, Cash.Commands.Issue()) this.verifies() } } @@ -222,23 +195,23 @@ class CashTests { fun generateIssueRaw() { // Test generation works. val tx: WireTransaction = TransactionBuilder(notary = null).apply { - Cash().generateIssue(this, 100.DOLLARS `issued by` MINI_CORP.ref(12, 34), owner = AnonymousParty(ALICE_PUBKEY), notary = DUMMY_NOTARY) + Cash().generateIssue(this, 100.DOLLARS `issued by` miniCorp.ref(12, 34), owner = AnonymousParty(alice.publicKey), notary = dummyNotary.party) }.toWireTransaction(miniCorpServices) assertTrue(tx.inputs.isEmpty()) val s = tx.outputsOfType().single() - assertEquals(100.DOLLARS `issued by` MINI_CORP.ref(12, 34), s.amount) - assertEquals(MINI_CORP as AbstractParty, s.amount.token.issuer.party) - assertEquals(AnonymousParty(ALICE_PUBKEY), s.owner) + assertEquals(100.DOLLARS `issued by` miniCorp.ref(12, 34), s.amount) + assertEquals(miniCorp.party as AbstractParty, s.amount.token.issuer.party) + assertEquals(AnonymousParty(alice.publicKey), s.owner) assertTrue(tx.commands[0].value is Cash.Commands.Issue) - assertEquals(MINI_CORP_PUBKEY, tx.commands[0].signers[0]) + assertEquals(miniCorp.publicKey, tx.commands[0].signers[0]) } @Test fun generateIssueFromAmount() { // Test issuance from an issued amount - val amount = 100.DOLLARS `issued by` MINI_CORP.ref(12, 34) + val amount = 100.DOLLARS `issued by` miniCorp.ref(12, 34) val tx: WireTransaction = TransactionBuilder(notary = null).apply { - Cash().generateIssue(this, amount, owner = AnonymousParty(ALICE_PUBKEY), notary = DUMMY_NOTARY) + Cash().generateIssue(this, amount, owner = AnonymousParty(alice.publicKey), notary = dummyNotary.party) }.toWireTransaction(miniCorpServices) assertTrue(tx.inputs.isEmpty()) assertEquals(tx.outputs[0], tx.outputs[0]) @@ -253,13 +226,13 @@ class CashTests { output(Cash.PROGRAM_ID, inState.copy(amount = inState.amount * 2)) // Move fails: not allowed to summon money. tweak { - command(ALICE_PUBKEY, Cash.Commands.Move()) + command(alice.publicKey, Cash.Commands.Move()) this `fails with` "the amounts balance" } // Issue works. tweak { - command(MEGA_CORP_PUBKEY, Cash.Commands.Issue()) + command(megaCorp.publicKey, Cash.Commands.Issue()) this.verifies() } } @@ -269,7 +242,7 @@ class CashTests { attachment(Cash.PROGRAM_ID) input(Cash.PROGRAM_ID, inState) output(Cash.PROGRAM_ID, inState.copy(amount = inState.amount.splitEvenly(2).first())) - command(MEGA_CORP_PUBKEY, Cash.Commands.Issue()) + command(megaCorp.publicKey, Cash.Commands.Issue()) this `fails with` "output values sum to more than the inputs" } @@ -278,7 +251,7 @@ class CashTests { attachment(Cash.PROGRAM_ID) input(Cash.PROGRAM_ID, inState) output(Cash.PROGRAM_ID, inState) - command(MEGA_CORP_PUBKEY, Cash.Commands.Issue()) + command(megaCorp.publicKey, Cash.Commands.Issue()) this `fails with` "output values sum to more than the inputs" } @@ -287,9 +260,9 @@ class CashTests { attachment(Cash.PROGRAM_ID) input(Cash.PROGRAM_ID, inState) output(Cash.PROGRAM_ID, inState.copy(amount = inState.amount * 2)) - command(MEGA_CORP_PUBKEY, Cash.Commands.Issue()) + command(megaCorp.publicKey, Cash.Commands.Issue()) tweak { - command(MEGA_CORP_PUBKEY, Cash.Commands.Issue()) + command(megaCorp.publicKey, Cash.Commands.Issue()) this `fails with` "there is only a single issue command" } this.verifies() @@ -303,15 +276,15 @@ class CashTests { @Test(expected = IllegalStateException::class) fun `reject issuance with inputs`() { // Issue some cash - var ptx = TransactionBuilder(DUMMY_NOTARY) + var ptx = TransactionBuilder(dummyNotary.party) - Cash().generateIssue(ptx, 100.DOLLARS `issued by` MINI_CORP.ref(12, 34), owner = MINI_CORP, notary = DUMMY_NOTARY) + Cash().generateIssue(ptx, 100.DOLLARS `issued by` miniCorp.ref(12, 34), owner = miniCorp.party, notary = dummyNotary.party) val tx = miniCorpServices.signInitialTransaction(ptx) // Include the previously issued cash in a new issuance command - ptx = TransactionBuilder(DUMMY_NOTARY) + ptx = TransactionBuilder(dummyNotary.party) ptx.addInputState(tx.tx.outRef(0)) - Cash().generateIssue(ptx, 100.DOLLARS `issued by` MINI_CORP.ref(12, 34), owner = MINI_CORP, notary = DUMMY_NOTARY) + Cash().generateIssue(ptx, 100.DOLLARS `issued by` miniCorp.ref(12, 34), owner = miniCorp.party, notary = dummyNotary.party) } @Test @@ -319,7 +292,7 @@ class CashTests { // Splitting value works. transaction { attachment(Cash.PROGRAM_ID) - command(ALICE_PUBKEY, Cash.Commands.Move()) + command(alice.publicKey, Cash.Commands.Move()) tweak { input(Cash.PROGRAM_ID, inState) val splits4 = inState.amount.splitEvenly(4) @@ -350,7 +323,7 @@ class CashTests { attachment(Cash.PROGRAM_ID) input(Cash.PROGRAM_ID, inState) input(Cash.PROGRAM_ID, inState.copy(amount = 0.DOLLARS `issued by` defaultIssuer)) - command(ALICE_PUBKEY, Cash.Commands.Move()) + command(alice.publicKey, Cash.Commands.Move()) this `fails with` "zero sized inputs" } transaction { @@ -358,7 +331,7 @@ class CashTests { input(Cash.PROGRAM_ID, inState) output(Cash.PROGRAM_ID, inState) output(Cash.PROGRAM_ID, inState.copy(amount = 0.DOLLARS `issued by` defaultIssuer)) - command(ALICE_PUBKEY, Cash.Commands.Move()) + command(alice.publicKey, Cash.Commands.Move()) this `fails with` "zero sized outputs" } } @@ -369,8 +342,8 @@ class CashTests { transaction { attachment(Cash.PROGRAM_ID) input(Cash.PROGRAM_ID, inState) - output(Cash.PROGRAM_ID, outState issuedBy MINI_CORP) - command(ALICE_PUBKEY, Cash.Commands.Move()) + output(Cash.PROGRAM_ID, outState issuedBy miniCorp.party) + command(alice.publicKey, Cash.Commands.Move()) this `fails with` "the amounts balance" } // Can't change deposit reference when splitting. @@ -379,7 +352,7 @@ class CashTests { val splits2 = inState.amount.splitEvenly(2) input(Cash.PROGRAM_ID, inState) for (i in 0..1) output(Cash.PROGRAM_ID, outState.copy(amount = splits2[i]).editDepositRef(i.toByte())) - command(ALICE_PUBKEY, Cash.Commands.Move()) + command(alice.publicKey, Cash.Commands.Move()) this `fails with` "the amounts balance" } // Can't mix currencies. @@ -388,7 +361,7 @@ class CashTests { input(Cash.PROGRAM_ID, inState) output(Cash.PROGRAM_ID, outState.copy(amount = 800.DOLLARS `issued by` defaultIssuer)) output(Cash.PROGRAM_ID, outState.copy(amount = 200.POUNDS `issued by` defaultIssuer)) - command(ALICE_PUBKEY, Cash.Commands.Move()) + command(alice.publicKey, Cash.Commands.Move()) this `fails with` "the amounts balance" } transaction { @@ -397,18 +370,18 @@ class CashTests { input(Cash.PROGRAM_ID, inState.copy( amount = 150.POUNDS `issued by` defaultIssuer, - owner = AnonymousParty(BOB_PUBKEY))) + owner = AnonymousParty(bob.publicKey))) output(Cash.PROGRAM_ID, outState.copy(amount = 1150.DOLLARS `issued by` defaultIssuer)) - command(ALICE_PUBKEY, Cash.Commands.Move()) + command(alice.publicKey, Cash.Commands.Move()) this `fails with` "the amounts balance" } // Can't have superfluous input states from different issuers. transaction { attachment(Cash.PROGRAM_ID) input(Cash.PROGRAM_ID, inState) - input(Cash.PROGRAM_ID, inState issuedBy MINI_CORP) + input(Cash.PROGRAM_ID, inState issuedBy miniCorp.party) output(Cash.PROGRAM_ID, outState) - command(ALICE_PUBKEY, Cash.Commands.Move()) + command(alice.publicKey, Cash.Commands.Move()) this `fails with` "the amounts balance" } // Can't combine two different deposits at the same issuer. @@ -417,7 +390,7 @@ class CashTests { input(Cash.PROGRAM_ID, inState) input(Cash.PROGRAM_ID, inState.editDepositRef(3)) output(Cash.PROGRAM_ID, outState.copy(amount = inState.amount * 2).editDepositRef(3)) - command(ALICE_PUBKEY, Cash.Commands.Move()) + command(alice.publicKey, Cash.Commands.Move()) this `fails with` "for reference [01]" } } @@ -430,17 +403,17 @@ class CashTests { input(Cash.PROGRAM_ID, issuerInState) output(Cash.PROGRAM_ID, issuerInState.copy(amount = issuerInState.amount - (200.DOLLARS `issued by` defaultIssuer))) tweak { - command(MEGA_CORP_PUBKEY, Cash.Commands.Exit(100.DOLLARS `issued by` defaultIssuer)) - command(MEGA_CORP_PUBKEY, Cash.Commands.Move()) + command(megaCorp.publicKey, Cash.Commands.Exit(100.DOLLARS `issued by` defaultIssuer)) + command(megaCorp.publicKey, Cash.Commands.Move()) this `fails with` "the amounts balance" } tweak { - command(MEGA_CORP_PUBKEY, Cash.Commands.Exit(200.DOLLARS `issued by` defaultIssuer)) + command(megaCorp.publicKey, Cash.Commands.Exit(200.DOLLARS `issued by` defaultIssuer)) this `fails with` "required net.corda.finance.contracts.asset.Cash.Commands.Move command" tweak { - command(MEGA_CORP_PUBKEY, Cash.Commands.Move()) + command(megaCorp.publicKey, Cash.Commands.Move()) this.verifies() } } @@ -453,14 +426,14 @@ class CashTests { transaction { attachment(Cash.PROGRAM_ID) input(Cash.PROGRAM_ID, issuerInState) - input(Cash.PROGRAM_ID, issuerInState.copy(owner = MINI_CORP) issuedBy MINI_CORP) - output(Cash.PROGRAM_ID, issuerInState.copy(amount = issuerInState.amount - (200.DOLLARS `issued by` defaultIssuer)) issuedBy MINI_CORP) - output(Cash.PROGRAM_ID, issuerInState.copy(owner = MINI_CORP, amount = issuerInState.amount - (200.DOLLARS `issued by` defaultIssuer))) - command(listOf(MEGA_CORP_PUBKEY, MINI_CORP_PUBKEY), Cash.Commands.Move()) + input(Cash.PROGRAM_ID, issuerInState.copy(owner = miniCorp.party) issuedBy miniCorp.party) + output(Cash.PROGRAM_ID, issuerInState.copy(amount = issuerInState.amount - (200.DOLLARS `issued by` defaultIssuer)) issuedBy miniCorp.party) + output(Cash.PROGRAM_ID, issuerInState.copy(owner = miniCorp.party, amount = issuerInState.amount - (200.DOLLARS `issued by` defaultIssuer))) + command(listOf(megaCorp.publicKey, miniCorp.publicKey), Cash.Commands.Move()) this `fails with` "the amounts balance" - command(MEGA_CORP_PUBKEY, Cash.Commands.Exit(200.DOLLARS `issued by` defaultIssuer)) + command(megaCorp.publicKey, Cash.Commands.Exit(200.DOLLARS `issued by` defaultIssuer)) this `fails with` "the amounts balance" - command(MINI_CORP_PUBKEY, Cash.Commands.Exit(200.DOLLARS `issued by` MINI_CORP.ref(defaultRef))) + command(miniCorp.publicKey, Cash.Commands.Exit(200.DOLLARS `issued by` miniCorp.ref(1))) this.verifies() } } @@ -472,8 +445,8 @@ class CashTests { attachment(Cash.PROGRAM_ID) input(Cash.PROGRAM_ID, inState) output(Cash.PROGRAM_ID, outState.copy(amount = inState.amount - (200.DOLLARS `issued by` defaultIssuer))) - command(MEGA_CORP_PUBKEY, Cash.Commands.Exit(200.DOLLARS `issued by` defaultIssuer)) - command(ALICE_PUBKEY, Cash.Commands.Move()) + command(megaCorp.publicKey, Cash.Commands.Exit(200.DOLLARS `issued by` defaultIssuer)) + command(alice.publicKey, Cash.Commands.Move()) this `fails with` "the amounts balance" } } @@ -484,23 +457,23 @@ class CashTests { attachment(Cash.PROGRAM_ID) // Gather 2000 dollars from two different issuers. input(Cash.PROGRAM_ID, inState) - input(Cash.PROGRAM_ID, inState issuedBy MINI_CORP) - command(ALICE_PUBKEY, Cash.Commands.Move()) + input(Cash.PROGRAM_ID, inState issuedBy miniCorp.party) + command(alice.publicKey, Cash.Commands.Move()) // Can't merge them together. tweak { - output(Cash.PROGRAM_ID, inState.copy(owner = AnonymousParty(BOB_PUBKEY), amount = 2000.DOLLARS `issued by` defaultIssuer)) + output(Cash.PROGRAM_ID, inState.copy(owner = AnonymousParty(bob.publicKey), amount = 2000.DOLLARS `issued by` defaultIssuer)) this `fails with` "the amounts balance" } // Missing MiniCorp deposit tweak { - output(Cash.PROGRAM_ID, inState.copy(owner = AnonymousParty(BOB_PUBKEY))) - output(Cash.PROGRAM_ID, inState.copy(owner = AnonymousParty(BOB_PUBKEY))) + output(Cash.PROGRAM_ID, inState.copy(owner = AnonymousParty(bob.publicKey))) + output(Cash.PROGRAM_ID, inState.copy(owner = AnonymousParty(bob.publicKey))) this `fails with` "the amounts balance" } // This works. - output(Cash.PROGRAM_ID, inState.copy(owner = AnonymousParty(BOB_PUBKEY))) - output(Cash.PROGRAM_ID, inState.copy(owner = AnonymousParty(BOB_PUBKEY)) issuedBy MINI_CORP) + output(Cash.PROGRAM_ID, inState.copy(owner = AnonymousParty(bob.publicKey))) + output(Cash.PROGRAM_ID, inState.copy(owner = AnonymousParty(bob.publicKey)) issuedBy miniCorp.party) this.verifies() } } @@ -510,12 +483,12 @@ class CashTests { // Check we can do an atomic currency trade tx. transaction { attachment(Cash.PROGRAM_ID) - val pounds = Cash.State(658.POUNDS `issued by` MINI_CORP.ref(3, 4, 5), AnonymousParty(BOB_PUBKEY)) - input(Cash.PROGRAM_ID, inState ownedBy AnonymousParty(ALICE_PUBKEY)) + val pounds = Cash.State(658.POUNDS `issued by` miniCorp.ref(3, 4, 5), AnonymousParty(bob.publicKey)) + input(Cash.PROGRAM_ID, inState ownedBy AnonymousParty(alice.publicKey)) input(Cash.PROGRAM_ID, pounds) - output(Cash.PROGRAM_ID, inState ownedBy AnonymousParty(BOB_PUBKEY)) - output(Cash.PROGRAM_ID, pounds ownedBy AnonymousParty(ALICE_PUBKEY)) - command(listOf(ALICE_PUBKEY, BOB_PUBKEY), Cash.Commands.Move()) + output(Cash.PROGRAM_ID, inState ownedBy AnonymousParty(bob.publicKey)) + output(Cash.PROGRAM_ID, pounds ownedBy AnonymousParty(alice.publicKey)) + command(listOf(alice.publicKey, bob.publicKey), Cash.Commands.Move()) this.verifies() } } @@ -526,7 +499,7 @@ class CashTests { private fun makeCash(amount: Amount, issuer: AbstractParty, depositRef: Byte = 1) = StateAndRef( - TransactionState(Cash.State(amount `issued by` issuer.ref(depositRef), ourIdentity), Cash.PROGRAM_ID, DUMMY_NOTARY), + TransactionState(Cash.State(amount `issued by` issuer.ref(depositRef), ourIdentity), Cash.PROGRAM_ID, dummyNotary.party), StateRef(SecureHash.randomSHA256(), Random().nextInt(32)) ) @@ -534,15 +507,15 @@ class CashTests { * Generate an exit transaction, removing some amount of cash from the ledger. */ private fun makeExit(serviceHub: ServiceHub, amount: Amount, issuer: Party, depositRef: Byte = 1): WireTransaction { - val tx = TransactionBuilder(DUMMY_NOTARY) - val payChangeTo = serviceHub.keyManagementService.freshKeyAndCert(MINI_CORP_IDENTITY, false).party - Cash().generateExit(tx, Amount(amount.quantity, Issued(issuer.ref(depositRef), amount.token)), WALLET, payChangeTo) + val tx = TransactionBuilder(dummyNotary.party) + val payChangeTo = serviceHub.keyManagementService.freshKeyAndCert(miniCorp.identity, false).party + Cash().generateExit(tx, Amount(amount.quantity, Issued(issuer.ref(depositRef), amount.token)), cashStates, payChangeTo) return tx.toWireTransaction(serviceHub) } private fun makeSpend(services: ServiceHub, amount: Amount, dest: AbstractParty): WireTransaction { val ourIdentity = services.myInfo.singleIdentityAndCert() - val tx = TransactionBuilder(DUMMY_NOTARY) + val tx = TransactionBuilder(dummyNotary.party) database.transaction { Cash.generateSpend(services, tx, amount, ourIdentity, dest) } @@ -554,12 +527,12 @@ class CashTests { */ @Test fun generateSimpleExit() { - val wtx = makeExit(miniCorpServices, 100.DOLLARS, MEGA_CORP, 1) - assertEquals(WALLET[0].ref, wtx.inputs[0]) + val wtx = makeExit(miniCorpServices, 100.DOLLARS, megaCorp.party, 1) + assertEquals(cashStates[0].ref, wtx.inputs[0]) assertEquals(0, wtx.outputs.size) val expectedMove = Cash.Commands.Move() - val expectedExit = Cash.Commands.Exit(Amount(10000, Issued(MEGA_CORP.ref(1), USD))) + val expectedExit = Cash.Commands.Exit(Amount(10000, Issued(megaCorp.ref(1), USD))) assertEquals(listOf(expectedMove, expectedExit), wtx.commands.map { it.value }) } @@ -569,15 +542,15 @@ class CashTests { */ @Test fun generatePartialExit() { - val wtx = makeExit(miniCorpServices, 50.DOLLARS, MEGA_CORP, 1) + val wtx = makeExit(miniCorpServices, 50.DOLLARS, megaCorp.party, 1) val actualInput = wtx.inputs.single() // Filter the available inputs and confirm exactly one has been used - val expectedInputs = WALLET.filter { it.ref == actualInput } + val expectedInputs = cashStates.filter { it.ref == actualInput } assertEquals(1, expectedInputs.size) val inputState = expectedInputs.single() val actualChange = wtx.outputs.single().data as Cash.State val expectedChangeAmount = inputState.state.data.amount.quantity - 50.DOLLARS.quantity - val expectedChange = WALLET[0].state.data.copy(amount = WALLET[0].state.data.amount.copy(quantity = expectedChangeAmount), owner = actualChange.owner) + val expectedChange = cashStates[0].state.data.copy(amount = cashStates[0].state.data.amount.copy(quantity = expectedChangeAmount), owner = actualChange.owner) assertEquals(expectedChange, wtx.getOutput(0)) } @@ -586,7 +559,7 @@ class CashTests { */ @Test fun generateAbsentExit() { - assertFailsWith { makeExit(miniCorpServices, 100.POUNDS, MEGA_CORP, 1) } + assertFailsWith { makeExit(miniCorpServices, 100.POUNDS, megaCorp.party, 1) } } /** @@ -594,7 +567,7 @@ class CashTests { */ @Test fun generateInvalidReferenceExit() { - assertFailsWith { makeExit(miniCorpServices, 100.POUNDS, MEGA_CORP, 2) } + assertFailsWith { makeExit(miniCorpServices, 100.POUNDS, megaCorp.party, 2) } } /** @@ -602,7 +575,7 @@ class CashTests { */ @Test fun generateInsufficientExit() { - assertFailsWith { makeExit(miniCorpServices, 1000.DOLLARS, MEGA_CORP, 1) } + assertFailsWith { makeExit(miniCorpServices, 1000.DOLLARS, megaCorp.party, 1) } } /** @@ -610,7 +583,7 @@ class CashTests { */ @Test fun generateOwnerWithNoStatesExit() { - assertFailsWith { makeExit(miniCorpServices, 100.POUNDS, CHARLIE, 1) } + assertFailsWith { makeExit(miniCorpServices, 100.POUNDS, charlie.party, 1) } } /** @@ -619,8 +592,8 @@ class CashTests { @Test fun generateExitWithEmptyVault() { assertFailsWith { - val tx = TransactionBuilder(DUMMY_NOTARY) - Cash().generateExit(tx, Amount(100, Issued(CHARLIE.ref(1), GBP)), emptyList(), ourIdentity) + val tx = TransactionBuilder(dummyNotary.party) + Cash().generateExit(tx, Amount(100, Issued(charlie.ref(1), GBP)), emptyList(), ourIdentity) } } @@ -641,9 +614,8 @@ class CashTests { @Test fun generateSimpleSpendWithParties() { database.transaction { - - val tx = TransactionBuilder(DUMMY_NOTARY) - Cash.generateSpend(ourServices, tx, 80.DOLLARS, ourServices.myInfo.singleIdentityAndCert(), ALICE, setOf(MINI_CORP)) + val tx = TransactionBuilder(dummyNotary.party) + Cash.generateSpend(ourServices, tx, 80.DOLLARS, ourServices.myInfo.singleIdentityAndCert(), alice.party, setOf(miniCorp.party)) assertEquals(vaultStatesUnconsumed.elementAt(2).ref, tx.inputStates()[0]) } @@ -731,9 +703,9 @@ class CashTests { */ @Test fun aggregation() { - val fiveThousandDollarsFromMega = Cash.State(5000.DOLLARS `issued by` MEGA_CORP.ref(2), MEGA_CORP) - val twoThousandDollarsFromMega = Cash.State(2000.DOLLARS `issued by` MEGA_CORP.ref(2), MINI_CORP) - val oneThousandDollarsFromMini = Cash.State(1000.DOLLARS `issued by` MINI_CORP.ref(3), MEGA_CORP) + val fiveThousandDollarsFromMega = Cash.State(5000.DOLLARS `issued by` megaCorp.ref(2), megaCorp.party) + val twoThousandDollarsFromMega = Cash.State(2000.DOLLARS `issued by` megaCorp.ref(2), miniCorp.party) + val oneThousandDollarsFromMini = Cash.State(1000.DOLLARS `issued by` miniCorp.ref(3), megaCorp.party) // Obviously it must be possible to aggregate states with themselves assertEquals(fiveThousandDollarsFromMega.amount.token, fiveThousandDollarsFromMega.amount.token) @@ -747,7 +719,7 @@ class CashTests { // States cannot be aggregated if the currency differs assertNotEquals(oneThousandDollarsFromMini.amount.token, - Cash.State(1000.POUNDS `issued by` MINI_CORP.ref(3), MEGA_CORP).amount.token) + Cash.State(1000.POUNDS `issued by` miniCorp.ref(3), megaCorp.party).amount.token) // States cannot be aggregated if the reference differs assertNotEquals(fiveThousandDollarsFromMega.amount.token, (fiveThousandDollarsFromMega withDeposit defaultIssuer).amount.token) @@ -757,20 +729,20 @@ class CashTests { @Test fun `summing by owner`() { val states = listOf( - Cash.State(1000.DOLLARS `issued by` defaultIssuer, MINI_CORP), - Cash.State(2000.DOLLARS `issued by` defaultIssuer, MEGA_CORP), - Cash.State(4000.DOLLARS `issued by` defaultIssuer, MEGA_CORP) + Cash.State(1000.DOLLARS `issued by` defaultIssuer, miniCorp.party), + Cash.State(2000.DOLLARS `issued by` defaultIssuer, megaCorp.party), + Cash.State(4000.DOLLARS `issued by` defaultIssuer, megaCorp.party) ) - assertEquals(6000.DOLLARS `issued by` defaultIssuer, states.sumCashBy(MEGA_CORP)) + assertEquals(6000.DOLLARS `issued by` defaultIssuer, states.sumCashBy(megaCorp.party)) } @Test(expected = UnsupportedOperationException::class) fun `summing by owner throws`() { val states = listOf( - Cash.State(2000.DOLLARS `issued by` defaultIssuer, MEGA_CORP), - Cash.State(4000.DOLLARS `issued by` defaultIssuer, MEGA_CORP) + Cash.State(2000.DOLLARS `issued by` defaultIssuer, megaCorp.party), + Cash.State(4000.DOLLARS `issued by` defaultIssuer, megaCorp.party) ) - states.sumCashBy(MINI_CORP) + states.sumCashBy(miniCorp.party) } @Test @@ -789,9 +761,9 @@ class CashTests { @Test fun `summing a single currency`() { val states = listOf( - Cash.State(1000.DOLLARS `issued by` defaultIssuer, MEGA_CORP), - Cash.State(2000.DOLLARS `issued by` defaultIssuer, MEGA_CORP), - Cash.State(4000.DOLLARS `issued by` defaultIssuer, MEGA_CORP) + Cash.State(1000.DOLLARS `issued by` defaultIssuer, megaCorp.party), + Cash.State(2000.DOLLARS `issued by` defaultIssuer, megaCorp.party), + Cash.State(4000.DOLLARS `issued by` defaultIssuer, megaCorp.party) ) // Test that summing everything produces the total number of dollars val expected = 7000.DOLLARS `issued by` defaultIssuer @@ -802,8 +774,8 @@ class CashTests { @Test(expected = IllegalArgumentException::class) fun `summing multiple currencies`() { val states = listOf( - Cash.State(1000.DOLLARS `issued by` defaultIssuer, MEGA_CORP), - Cash.State(4000.POUNDS `issued by` defaultIssuer, MEGA_CORP) + Cash.State(1000.DOLLARS `issued by` defaultIssuer, megaCorp.party), + Cash.State(4000.POUNDS `issued by` defaultIssuer, megaCorp.party) ) // Test that summing everything fails because we're mixing units states.sumCash() @@ -812,23 +784,20 @@ class CashTests { // Double spend. @Test fun chainCashDoubleSpendFailsWith() { - val mockService = MockServices(listOf("net.corda.finance.contracts.asset"), rigorousMock().also { - doReturn(MEGA_CORP).whenever(it).partyFromKey(MEGA_CORP_PUBKEY) - }, MEGA_CORP.name, MEGA_CORP_KEY) - mockService.ledger(DUMMY_NOTARY) { + MockServices(megaCorp).ledger(dummyNotary.party) { unverifiedTransaction { attachment(Cash.PROGRAM_ID) output(Cash.PROGRAM_ID, "MEGA_CORP cash", Cash.State( - amount = 1000.DOLLARS `issued by` MEGA_CORP.ref(1, 1), - owner = MEGA_CORP)) + amount = 1000.DOLLARS `issued by` megaCorp.ref(1, 1), + owner = megaCorp.party)) } transaction { attachment(Cash.PROGRAM_ID) input("MEGA_CORP cash") - output(Cash.PROGRAM_ID, "MEGA_CORP cash 2", "MEGA_CORP cash".output().copy(owner = AnonymousParty(ALICE_PUBKEY))) - command(MEGA_CORP_PUBKEY, Cash.Commands.Move()) + output(Cash.PROGRAM_ID, "MEGA_CORP cash 2", "MEGA_CORP cash".output().copy(owner = AnonymousParty(alice.publicKey))) + command(megaCorp.publicKey, Cash.Commands.Move()) this.verifies() } @@ -837,8 +806,8 @@ class CashTests { attachment(Cash.PROGRAM_ID) input("MEGA_CORP cash") // We send it to another pubkey so that the transaction is not identical to the previous one - output(Cash.PROGRAM_ID, "MEGA_CORP cash 3", "MEGA_CORP cash".output().copy(owner = ALICE)) - command(MEGA_CORP_PUBKEY, Cash.Commands.Move()) + output(Cash.PROGRAM_ID, "MEGA_CORP cash 3", "MEGA_CORP cash".output().copy(owner = alice.party)) + command(megaCorp.publicKey, Cash.Commands.Move()) this.verifies() } this.fails() @@ -850,11 +819,11 @@ class CashTests { @Test fun multiSpend() { - val tx = TransactionBuilder(DUMMY_NOTARY) + val tx = TransactionBuilder(dummyNotary.party) database.transaction { val payments = listOf( PartyAndAmount(miniCorpAnonymised, 400.DOLLARS), - PartyAndAmount(CHARLIE_ANONYMISED, 150.DOLLARS) + PartyAndAmount(charlie.party.anonymise(), 150.DOLLARS) ) Cash.generateSpend(ourServices, tx, payments, ourServices.myInfo.singleIdentityAndCert()) } @@ -865,9 +834,9 @@ class CashTests { assertEquals(320.DOLLARS, out(1).amount.withoutIssuer()) assertEquals(150.DOLLARS, out(2).amount.withoutIssuer()) assertEquals(30.DOLLARS, out(3).amount.withoutIssuer()) - assertEquals(MINI_CORP, out(0).amount.token.issuer.party) - assertEquals(MEGA_CORP, out(1).amount.token.issuer.party) - assertEquals(MEGA_CORP, out(2).amount.token.issuer.party) - assertEquals(MEGA_CORP, out(3).amount.token.issuer.party) + assertEquals(miniCorp.party, out(0).amount.token.issuer.party) + assertEquals(megaCorp.party, out(1).amount.token.issuer.party) + assertEquals(megaCorp.party, out(2).amount.token.issuer.party) + assertEquals(megaCorp.party, out(3).amount.token.issuer.party) } } diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockServices.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockServices.kt index ca7a2fa1f9..da99b28c8e 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockServices.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockServices.kt @@ -46,9 +46,15 @@ import java.time.Clock import java.util.* fun makeTestIdentityService(vararg identities: PartyAndCertificate) = InMemoryIdentityService(identities, DEV_ROOT_CA.certificate) + /** - * A singleton utility that only provides a mock identity, key and storage service. However, this is sufficient for - * building chains of transactions and verifying them. It isn't sufficient for testing flows however. + * An implementation of [ServiceHub] that is designed for in-memory unit tests of contract validation logic. It has + * enough functionality to do tests of code that queries the vault, inserts to the vault, and constructs/checks + * transactions. However it isn't enough to test flows and other aspects of an app that require a network. For that + * you should investigate [MockNetwork]. + * + * There are a variety of constructors that can be used to supply enough data to simulate a node. Each mock service hub + * must have at least an identity of its own. The other components have defaults that work in most situations. */ open class MockServices private constructor( cordappLoader: CordappLoader, @@ -80,6 +86,7 @@ open class MockServices private constructor( /** * Makes database and mock services appropriate for unit tests. + * * @param moreKeys a list of additional [KeyPair] instances to be used by [MockServices]. * @param identityService an instance of [IdentityServiceInternal], see [makeTestIdentityService]. * @param initialIdentity the first (typically sole) identity the services will represent. @@ -109,20 +116,68 @@ open class MockServices private constructor( } return Pair(database, mockService) } + + @JvmStatic + private fun getCallerPackage(): String { + // TODO: In Java 9 there's a new stack walker API that is better than this. + // The magic number '3' here is to chop off this method, an invisible bridge method generated by the + // compiler and then the c'tor itself. + return Throwable().stackTrace[3].className.split('.').dropLast(1).joinToString(".") + } } - private constructor(cordappLoader: CordappLoader, identityService: IdentityServiceInternal, initialIdentity: TestIdentity, moreKeys: Array) : this(cordappLoader, MockTransactionStorage(), identityService, initialIdentity, moreKeys) + private constructor(cordappLoader: CordappLoader, identityService: IdentityServiceInternal, + initialIdentity: TestIdentity, moreKeys: Array) + : this(cordappLoader, MockTransactionStorage(), identityService, initialIdentity, moreKeys) + + /** + * Create a mock [ServiceHub] that looks for app code in the given package names, uses the provided identity service + * (you can get one from [makeTestIdentityService]) and represents the given identity. + */ @JvmOverloads constructor(cordappPackages: List, identityService: IdentityServiceInternal = makeTestIdentityService(), initialIdentity: TestIdentity, vararg moreKeys: KeyPair) : this(CordappLoader.createWithTestPackages(cordappPackages), identityService, initialIdentity, moreKeys) + /** + * Create a mock [ServiceHub] that looks for app code in the given package names, uses the provided identity service + * (you can get one from [makeTestIdentityService]) and represents the given identity. + */ @JvmOverloads constructor(cordappPackages: List, identityService: IdentityServiceInternal = makeTestIdentityService(), initialIdentityName: CordaX500Name, key: KeyPair, vararg moreKeys: KeyPair) : this(cordappPackages, identityService, TestIdentity(initialIdentityName, key), *moreKeys) + /** + * Create a mock [ServiceHub] that can't load CorDapp code, which uses the provided identity service + * (you can get one from [makeTestIdentityService]) and which represents the given identity. + */ @JvmOverloads constructor(cordappPackages: List, identityService: IdentityServiceInternal = makeTestIdentityService(), initialIdentityName: CordaX500Name) : this(cordappPackages, identityService, TestIdentity(initialIdentityName)) + /** + * Create a mock [ServiceHub] which uses the package of the caller to find CorDapp code. It uses the provided identity service + * (you can get one from [makeTestIdentityService]) and which represents the given identity. + */ @JvmOverloads - constructor(cordappPackages: List, identityService: IdentityServiceInternal = makeTestIdentityService(), vararg moreKeys: KeyPair) : this(cordappPackages, identityService, TestIdentity.fresh("MockServices"), *moreKeys) + constructor(identityService: IdentityServiceInternal = makeTestIdentityService(), initialIdentityName: CordaX500Name, key: KeyPair, vararg moreKeys: KeyPair) + : this(listOf(getCallerPackage()), identityService, TestIdentity(initialIdentityName, key), *moreKeys) + + /** + * Create a mock [ServiceHub] which uses the package of the caller to find CorDapp code. It uses the provided identity service + * (you can get one from [makeTestIdentityService]) and which represents the given identity. It has no keys. + */ + @JvmOverloads + constructor(identityService: IdentityServiceInternal = makeTestIdentityService(), initialIdentityName: CordaX500Name) + : this(listOf(getCallerPackage()), identityService, TestIdentity(initialIdentityName)) + + /** + * A helper constructor that requires at least one test identity to be registered, and which takes the package of + * the caller as the package in which to find app code. This is the most convenient constructor and the one that + * is normally worth using. The first identity is the identity of this service hub, the rest are identities that + * it is aware of. + */ + constructor(firstIdentity: TestIdentity, vararg moreIdentities: TestIdentity) : this( + listOf(getCallerPackage()), + makeTestIdentityService(*listOf(firstIdentity, *moreIdentities).map { it.identity }.toTypedArray()), + firstIdentity, firstIdentity.keyPair + ) override fun recordTransactions(statesToRecord: StatesToRecord, txs: Iterable) { txs.forEach { @@ -185,7 +240,7 @@ class MockKeyManagementService(val identityService: IdentityServiceInternal, private fun getSigner(publicKey: PublicKey): ContentSigner = getSigner(getSigningKeyPair(publicKey)) private fun getSigningKeyPair(publicKey: PublicKey): KeyPair { - val pk = publicKey.keys.first { keyStore.containsKey(it) } + val pk = publicKey.keys.firstOrNull { keyStore.containsKey(it) } ?: throw IllegalArgumentException("Public key not found: ${publicKey.toStringShort()}") return KeyPair(pk, keyStore[pk]!!) } From b8f25fe689e3afb9f7c5792845093ccb387fdd8e Mon Sep 17 00:00:00 2001 From: Joel Dudley Date: Thu, 25 Jan 2018 17:15:36 +0000 Subject: [PATCH 35/36] Docs on contributing to Corda --- docs/source/building-a-cordapp-index.rst | 3 +- docs/source/contributing.rst | 64 ++++++++++++++++++++++++ docs/source/other-index.rst | 4 +- docs/source/release-process-index.rst | 1 + 4 files changed, 68 insertions(+), 4 deletions(-) create mode 100644 docs/source/contributing.rst diff --git a/docs/source/building-a-cordapp-index.rst b/docs/source/building-a-cordapp-index.rst index ef4d17d334..f061c6d432 100644 --- a/docs/source/building-a-cordapp-index.rst +++ b/docs/source/building-a-cordapp-index.rst @@ -11,6 +11,7 @@ CorDapps cordapp-build-systems building-against-master corda-api + secure-coding-guidelines flow-cookbook cheat-sheet - building-a-cordapp-samples + building-a-cordapp-samples \ No newline at end of file diff --git a/docs/source/contributing.rst b/docs/source/contributing.rst new file mode 100644 index 0000000000..17d894dddb --- /dev/null +++ b/docs/source/contributing.rst @@ -0,0 +1,64 @@ +Contributing +============ + +Corda is an open-source project and we welcome contributions. This guide explains how to contribute back to Corda. + +.. contents:: + +Identifying an area to contribute +--------------------------------- +There are several ways to identify an area where you can contribute to Corda: + +* Browse issues labelled as ``HelpWanted`` on the + `Corda JIRA board `_ + + * Any issue with a ``HelpWanted`` label is considered ideal for open-source contributions + * If there is a feature you would like to add and there isn't a corresponding issue labelled as ``HelpWanted``, that + doesn't mean your contribution isn't welcome. Please reach out on the Corda Slack channel (see below) to clarify + +* Check the `Corda GitHub issues `_ + + * It's always worth checking in the Corda Slack channel (see below) whether a given issue is a good target for your + contribution. Someone else may already be working on it, or it may be blocked by an on-going piece of work + +* Ask in the `Corda Slack channel `_ + +Making the required changes +--------------------------- + +1. Create a fork of the master branch of the `Corda repo `_ +2. Clone the fork to your local machine +3. Make the changes, in accordance with the :doc:`code style guide ` + +Testing the changes +------------------- + +Running the tests +^^^^^^^^^^^^^^^^^ +Your changes must pass the tests described :doc:`here `. + +Building against the master branch +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +You may also want to test your changes against a CorDapp defined outside of the Corda repo. To do so, please follow the +instructions :doc:`here `. + +Merging the changes back into Corda +----------------------------------- + +1. Create a pull request from your fork to the master branch of the Corda repo +2. Complete the pull-request checklist in the comments box: + + * State that you have run the tests + * State that you have included JavaDocs for any new public APIs + * State that you have included the change in the :doc:`changelog ` and + :doc:`release notes ` where applicable + * State that you are in agreement with the terms of + `CONTRIBUTING.md `_ + +3. Request a review from a member of the Corda platform team via the `Corda Slack channel `_ +4. Wait for your PR to pass all four types of continuous integration tests (integration, API stability, build and unit) + + * Currently, external contributors cannot see the output of these tests. If your PR fails a test that passed + locally, ask the reviewer for further details + +5. Once a reviewer has approved the PR and the tests have passed, squash-and-merge the PR as a single commit \ No newline at end of file diff --git a/docs/source/other-index.rst b/docs/source/other-index.rst index 7f6fa6ef66..c525b2c4cd 100644 --- a/docs/source/other-index.rst +++ b/docs/source/other-index.rst @@ -4,8 +4,6 @@ Other .. toctree:: :maxdepth: 1 - json - secure-coding-guidelines corda-repo-layout building-the-docs - codestyle \ No newline at end of file + json \ No newline at end of file diff --git a/docs/source/release-process-index.rst b/docs/source/release-process-index.rst index 1b59977668..b269691a86 100644 --- a/docs/source/release-process-index.rst +++ b/docs/source/release-process-index.rst @@ -6,5 +6,6 @@ Release process release-notes changelog + contributing codestyle testing \ No newline at end of file From 336aeb96e5ca128a6d2e2227f8a9bc146c25b54f Mon Sep 17 00:00:00 2001 From: Shams Asari Date: Thu, 25 Jan 2018 18:51:29 +0000 Subject: [PATCH 36/36] Merge fixes --- .idea/compiler.xml | 4 +++- .../client/rpc/BlacklistKotlinClosureTest.kt | 2 +- .../net/corda/client/rpc/RPCStabilityTests.kt | 7 ++++--- constants.properties | 2 +- .../net/corda/core/PluginRegistrationTest.kt | 1 + .../finance/flows/CashConfigDataFlowTest.kt | 4 +++- .../finance/contracts/CommercialPaperTests.kt | 5 ----- .../finance/contracts/asset/CashTests.kt | 5 ----- .../r3/corda/networkmanage/HsmSimulator.kt | 2 +- .../doorman/NodeRegistrationTest.kt | 4 ++-- .../hsm/SigningServiceIntegrationTest.kt | 20 ++++++++++++++----- .../persistence/PersistentNodeInfoStorage.kt | 3 ++- .../NetworkParametersConfigurationTest.kt | 3 +-- .../com/r3/corda/networkmanage/TestBase.kt | 2 +- .../doorman/signer/DefaultCsrHandlerTest.kt | 3 ++- .../webservice/NetworkMapWebServiceTest.kt | 2 +- .../serialization/SetsSerializationTest.kt | 4 +--- .../kotlin/net/corda/node/AuthDBTests.kt | 3 +-- .../kotlin/net/corda/node/BootTests.kt | 10 +++++----- .../corda/node/CordappScanningDriverTest.kt | 3 ++- .../net/corda/node/NodeKeystoreCheckTest.kt | 6 +++--- .../net/corda/node/NodePerformanceTests.kt | 9 +++------ .../corda/node/NodeStartupPerformanceTests.kt | 4 +++- .../node/services/AttachmentLoadingTests.kt | 6 +----- .../node/services/MySQLNotaryServiceTests.kt | 4 ++-- .../node/services/network/NetworkMapTest.kt | 4 ++-- .../vault/VaultQueryIntegrationTests.kt | 2 +- .../registration/NodeRegistrationTest.kt | 6 +----- .../services/messaging/MQSecurityTest.kt | 8 +------- .../services/messaging/P2PMessagingTest.kt | 5 ----- .../test/node/NodeStatePersistenceTests.kt | 5 ++--- .../services/vault/VaultQueryJavaTests.java | 5 +---- .../net/corda/node/CordaRPCOpsImplTest.kt | 8 +++----- .../config/NodeConfigurationImplTest.kt | 2 +- .../services/network/NetworkMapCacheTest.kt | 7 ++----- .../persistence/DBCheckpointStorageTests.kt | 8 +++----- .../contracts/CommercialPaperTests.kt | 2 +- .../contracts/asset/CashTests.kt | 2 +- .../flows/CashExitFlowTests.kt | 2 +- .../flows/CashIssueAndPaymentFlowTests.kt | 2 +- .../CashIssueAndPaymentNoSelectionFlowTest.kt | 2 +- .../flows/CashIssueFlowTests.kt | 2 +- .../flows/CashPaymentFlowTests.kt | 2 +- .../flows/TwoPartyTradeFlowTest.kt | 2 +- .../attachmentdemo/AttachmentDemoTest.kt | 4 ++-- .../corda/bank/BankOfCordaRPCClientTest.kt | 7 ++----- .../corda/notarydemo/RaftNotaryCordform.kt | 5 +---- .../net/corda/vega/SimmValuationTest.kt | 2 +- .../net/corda/traderdemo/TraderDemoTest.kt | 5 +---- .../net/corda/testing/driver/DriverTests.kt | 7 ++----- .../net/corda/testing/node/NodeTestUtils.kt | 3 --- .../testing/node/internal/NodeBasedTest.kt | 3 +-- .../testing/internal/TestNodeInfoBuilder.kt | 8 -------- .../views/cordapps/iou/NewTransaction.kt | 9 ++++++--- .../r3/corda/jmeter/StartLocalPerfCorDapp.kt | 8 +++++--- .../r3/enclaves/verify/NativeSgxApiTest.kt | 6 +++--- .../r3/enclaves/txverify/EnclaveletTest.kt | 2 +- 57 files changed, 109 insertions(+), 154 deletions(-) diff --git a/.idea/compiler.xml b/.idea/compiler.xml index 2f959f2b02..c8bd2535cf 100644 --- a/.idea/compiler.xml +++ b/.idea/compiler.xml @@ -121,6 +121,8 @@ + + @@ -186,4 +188,4 @@ - + \ No newline at end of file diff --git a/client/rpc/src/integration-test/kotlin/net/corda/client/rpc/BlacklistKotlinClosureTest.kt b/client/rpc/src/integration-test/kotlin/net/corda/client/rpc/BlacklistKotlinClosureTest.kt index 86fafeaacb..7acc6e7b8a 100644 --- a/client/rpc/src/integration-test/kotlin/net/corda/client/rpc/BlacklistKotlinClosureTest.kt +++ b/client/rpc/src/integration-test/kotlin/net/corda/client/rpc/BlacklistKotlinClosureTest.kt @@ -7,8 +7,8 @@ import net.corda.core.flows.StartableByRPC import net.corda.core.messaging.startFlow import net.corda.core.serialization.CordaSerializable import net.corda.core.utilities.getOrThrow -import net.corda.testing.* import net.corda.testing.core.ALICE_NAME +import net.corda.testing.core.DUMMY_NOTARY_NAME import net.corda.testing.driver.driver import net.corda.testing.internal.IntegrationTest import net.corda.testing.internal.IntegrationTestSchemas diff --git a/client/rpc/src/integration-test/kotlin/net/corda/client/rpc/RPCStabilityTests.kt b/client/rpc/src/integration-test/kotlin/net/corda/client/rpc/RPCStabilityTests.kt index 63cf5ef76d..1db6b0d09a 100644 --- a/client/rpc/src/integration-test/kotlin/net/corda/client/rpc/RPCStabilityTests.kt +++ b/client/rpc/src/integration-test/kotlin/net/corda/client/rpc/RPCStabilityTests.kt @@ -12,13 +12,14 @@ import net.corda.core.serialization.serialize import net.corda.core.utilities.* import net.corda.node.services.messaging.RPCServerConfiguration import net.corda.nodeapi.RPCApi -import net.corda.testing.* +import net.corda.testing.core.ALICE_NAME +import net.corda.testing.core.BOB_NAME +import net.corda.testing.core.DUMMY_BANK_A_NAME +import net.corda.testing.core.SerializationEnvironmentRule import net.corda.testing.internal.IntegrationTest import net.corda.testing.internal.IntegrationTestSchemas import net.corda.testing.internal.testThreadFactory import net.corda.testing.internal.toDatabaseSchemaNames -import net.corda.testing.core.SerializationEnvironmentRule -import net.corda.testing.internal.* import net.corda.testing.node.internal.* import org.apache.activemq.artemis.api.core.SimpleString import org.junit.After diff --git a/constants.properties b/constants.properties index 3d1da960e2..aa6e223ca6 100644 --- a/constants.properties +++ b/constants.properties @@ -1,4 +1,4 @@ -gradlePluginsVersion=3.0.4 +gradlePluginsVersion=3.0.5 kotlinVersion=1.1.60 platformVersion=3 guavaVersion=21.0 diff --git a/core/src/smoke-test/kotlin/net/corda/core/PluginRegistrationTest.kt b/core/src/smoke-test/kotlin/net/corda/core/PluginRegistrationTest.kt index ca1556f2a4..a760f5f262 100644 --- a/core/src/smoke-test/kotlin/net/corda/core/PluginRegistrationTest.kt +++ b/core/src/smoke-test/kotlin/net/corda/core/PluginRegistrationTest.kt @@ -32,6 +32,7 @@ class PluginRegistrationTest { p2pPort = 30100, rpcPort = 30101, webPort = 30102, + rpcAdminPort = 30103, isNotary = false, users = listOf(User("_", "_", setOf("ALL"))), jarDirs = listOf(jarDir)) diff --git a/finance/src/integration-test/kotlin/net/corda/finance/flows/CashConfigDataFlowTest.kt b/finance/src/integration-test/kotlin/net/corda/finance/flows/CashConfigDataFlowTest.kt index 1fa2459d8d..ce66f20fd3 100644 --- a/finance/src/integration-test/kotlin/net/corda/finance/flows/CashConfigDataFlowTest.kt +++ b/finance/src/integration-test/kotlin/net/corda/finance/flows/CashConfigDataFlowTest.kt @@ -4,7 +4,9 @@ import net.corda.core.messaging.startFlow import net.corda.core.utilities.getOrThrow import net.corda.finance.EUR import net.corda.finance.USD -import net.corda.testing.* +import net.corda.testing.core.ALICE_NAME +import net.corda.testing.core.BOB_NAME +import net.corda.testing.core.DUMMY_BANK_A_NAME import net.corda.testing.driver.driver import net.corda.testing.internal.IntegrationTest import net.corda.testing.internal.IntegrationTestSchemas diff --git a/finance/src/test/kotlin/net/corda/finance/contracts/CommercialPaperTests.kt b/finance/src/test/kotlin/net/corda/finance/contracts/CommercialPaperTests.kt index 72196df1ca..78b42d486c 100644 --- a/finance/src/test/kotlin/net/corda/finance/contracts/CommercialPaperTests.kt +++ b/finance/src/test/kotlin/net/corda/finance/contracts/CommercialPaperTests.kt @@ -14,11 +14,6 @@ import net.corda.finance.`issued by` import net.corda.finance.contracts.asset.CASH import net.corda.finance.contracts.asset.Cash import net.corda.finance.contracts.asset.STATE -import net.corda.node.services.api.IdentityServiceInternal -import net.corda.testing.* -import net.corda.finance.contracts.asset.CASH -import net.corda.finance.contracts.asset.Cash -import net.corda.finance.contracts.asset.STATE import net.corda.testing.core.* import net.corda.testing.dsl.EnforceVerifyOrFail import net.corda.testing.dsl.TransactionDSL diff --git a/finance/src/test/kotlin/net/corda/finance/contracts/asset/CashTests.kt b/finance/src/test/kotlin/net/corda/finance/contracts/asset/CashTests.kt index 34f9f06054..13a00a683b 100644 --- a/finance/src/test/kotlin/net/corda/finance/contracts/asset/CashTests.kt +++ b/finance/src/test/kotlin/net/corda/finance/contracts/asset/CashTests.kt @@ -1,9 +1,5 @@ package net.corda.finance.contracts.asset -import com.nhaarman.mockito_kotlin.argThat -import com.nhaarman.mockito_kotlin.doNothing -import com.nhaarman.mockito_kotlin.doReturn -import com.nhaarman.mockito_kotlin.whenever import net.corda.core.contracts.* import net.corda.core.crypto.SecureHash import net.corda.core.identity.AbstractParty @@ -29,7 +25,6 @@ import net.corda.testing.dsl.EnforceVerifyOrFail import net.corda.testing.dsl.TransactionDSL import net.corda.testing.dsl.TransactionDSLInterpreter import net.corda.testing.internal.LogHelper -import net.corda.testing.internal.LogHelper import net.corda.testing.internal.vault.VaultFiller import net.corda.testing.node.MockServices import net.corda.testing.node.MockServices.Companion.makeTestDatabaseAndMockServices diff --git a/network-management/src/integration-test/kotlin/com/r3/corda/networkmanage/HsmSimulator.kt b/network-management/src/integration-test/kotlin/com/r3/corda/networkmanage/HsmSimulator.kt index a7e1231f18..0bec29dd0a 100644 --- a/network-management/src/integration-test/kotlin/com/r3/corda/networkmanage/HsmSimulator.kt +++ b/network-management/src/integration-test/kotlin/com/r3/corda/networkmanage/HsmSimulator.kt @@ -10,7 +10,7 @@ import com.spotify.docker.client.messages.HostConfig import com.spotify.docker.client.messages.PortBinding import com.spotify.docker.client.messages.RegistryAuth import net.corda.core.utilities.loggerFor -import net.corda.testing.freeLocalHostAndPort +import net.corda.testing.core.freeLocalHostAndPort import org.junit.Assume.assumeFalse import org.junit.rules.ExternalResource diff --git a/network-management/src/integration-test/kotlin/com/r3/corda/networkmanage/doorman/NodeRegistrationTest.kt b/network-management/src/integration-test/kotlin/com/r3/corda/networkmanage/doorman/NodeRegistrationTest.kt index 6fa65c1787..03da226702 100644 --- a/network-management/src/integration-test/kotlin/com/r3/corda/networkmanage/doorman/NodeRegistrationTest.kt +++ b/network-management/src/integration-test/kotlin/com/r3/corda/networkmanage/doorman/NodeRegistrationTest.kt @@ -20,8 +20,9 @@ import net.corda.nodeapi.internal.createDevNetworkMapCa import net.corda.nodeapi.internal.crypto.CertificateAndKeyPair import net.corda.nodeapi.internal.network.NetworkParameters import net.corda.nodeapi.internal.persistence.DatabaseConfig -import net.corda.testing.SerializationEnvironmentRule import net.corda.testing.common.internal.testNetworkParameters +import net.corda.testing.core.SerializationEnvironmentRule +import net.corda.testing.core.singleIdentity import net.corda.testing.driver.NodeHandle import net.corda.testing.driver.PortAllocation import net.corda.testing.internal.IntegrationTest @@ -30,7 +31,6 @@ import net.corda.testing.internal.createDevIntermediateCaCertPath import net.corda.testing.node.NotarySpec import net.corda.testing.node.internal.CompatibilityZoneParams import net.corda.testing.node.internal.internalDriver -import net.corda.testing.singleIdentity import org.assertj.core.api.Assertions.assertThat import org.junit.* import java.net.URL diff --git a/network-management/src/integration-test/kotlin/com/r3/corda/networkmanage/hsm/SigningServiceIntegrationTest.kt b/network-management/src/integration-test/kotlin/com/r3/corda/networkmanage/hsm/SigningServiceIntegrationTest.kt index 8c4a3b8215..accbf36cc9 100644 --- a/network-management/src/integration-test/kotlin/com/r3/corda/networkmanage/hsm/SigningServiceIntegrationTest.kt +++ b/network-management/src/integration-test/kotlin/com/r3/corda/networkmanage/hsm/SigningServiceIntegrationTest.kt @@ -21,8 +21,8 @@ import net.corda.node.utilities.registration.NetworkRegistrationHelper import net.corda.nodeapi.internal.createDevNodeCa import net.corda.nodeapi.internal.crypto.* import net.corda.nodeapi.internal.persistence.DatabaseConfig -import net.corda.testing.ALICE_NAME -import net.corda.testing.SerializationEnvironmentRule +import net.corda.testing.core.ALICE_NAME +import net.corda.testing.core.SerializationEnvironmentRule import net.corda.testing.internal.createDevIntermediateCaCertPath import net.corda.testing.internal.rigorousMock import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequest @@ -125,9 +125,16 @@ class SigningServiceIntegrationTest { } } config.certificatesDirectory.createDirectories() - loadOrCreateKeyStore(config.trustStoreFile, config.trustStorePassword).also { - it.addOrReplaceCertificate(X509Utilities.CORDA_ROOT_CA, rootCaCert) - it.save(config.trustStoreFile, config.trustStorePassword) + val trustStore = X509KeyStore.fromFile(config.trustStoreFile, config.trustStorePassword, createNew = true) + trustStore.update { + setCertificate(X509Utilities.CORDA_ROOT_CA, rootCaCert) + } + val nodeKeyStore = X509KeyStore.fromFile(config.nodeKeystore, config.keyStorePassword, createNew = true) + val sslKeyStore = X509KeyStore.fromFile(config.sslKeystore, config.keyStorePassword, createNew = true) + config.also { + doReturn(trustStore).whenever(it).loadTrustStore(any()) + doReturn(nodeKeyStore).whenever(it).loadNodeKeyStore(any()) + doReturn(sslKeyStore).whenever(it).loadSslKeyStore(any()) } NetworkRegistrationHelper(config, HTTPNetworkRegistrationService(config.compatibilityZoneURL!!)).buildKeystore() verify(hsmSigner).sign(any()) @@ -144,6 +151,9 @@ class SigningServiceIntegrationTest { doReturn("trustpass").whenever(it).trustStorePassword doReturn("cordacadevpass").whenever(it).keyStorePassword doReturn("iTest@R3.com").whenever(it).emailAddress +// doReturn(X509KeyStore.fromFile(it.nodeKeystore, it.keyStorePassword, true)).whenever(it).loadNodeKeyStore(any()) +// doReturn(X509KeyStore.fromFile(it.sslKeystore, it.keyStorePassword, true)).whenever(it).loadSslKeyStore(any()) +// doReturn(trustStore).whenever(it).loadTrustStore(any()) } } diff --git a/network-management/src/main/kotlin/com/r3/corda/networkmanage/common/persistence/PersistentNodeInfoStorage.kt b/network-management/src/main/kotlin/com/r3/corda/networkmanage/common/persistence/PersistentNodeInfoStorage.kt index 4d97e79629..e085b84389 100644 --- a/network-management/src/main/kotlin/com/r3/corda/networkmanage/common/persistence/PersistentNodeInfoStorage.kt +++ b/network-management/src/main/kotlin/com/r3/corda/networkmanage/common/persistence/PersistentNodeInfoStorage.kt @@ -8,6 +8,7 @@ import net.corda.core.crypto.sha256 import net.corda.core.internal.CertRole import net.corda.core.serialization.serialize import net.corda.nodeapi.internal.SignedNodeInfo +import net.corda.nodeapi.internal.crypto.x509Certificates import net.corda.nodeapi.internal.persistence.CordaPersistence import net.corda.nodeapi.internal.persistence.DatabaseTransaction import java.security.cert.CertPath @@ -19,7 +20,7 @@ class PersistentNodeInfoStorage(private val database: CordaPersistence) : NodeIn override fun putNodeInfo(nodeInfoWithSigned: NodeInfoWithSigned): SecureHash { val nodeInfo = nodeInfoWithSigned.nodeInfo val signedNodeInfo = nodeInfoWithSigned.signedNodeInfo - val nodeCaCert = nodeInfo.legalIdentitiesAndCerts[0].certPath.certificates.find { CertRole.extract(it) == CertRole.NODE_CA } + val nodeCaCert = nodeInfo.legalIdentitiesAndCerts[0].certPath.x509Certificates.find { CertRole.extract(it) == CertRole.NODE_CA } return database.transaction { // TODO Move these checks out of data access layer val request = nodeCaCert?.let { diff --git a/network-management/src/test/kotlin/com/r3/corda/networkmanage/NetworkParametersConfigurationTest.kt b/network-management/src/test/kotlin/com/r3/corda/networkmanage/NetworkParametersConfigurationTest.kt index b6caa2768a..d7fc5dfa3b 100644 --- a/network-management/src/test/kotlin/com/r3/corda/networkmanage/NetworkParametersConfigurationTest.kt +++ b/network-management/src/test/kotlin/com/r3/corda/networkmanage/NetworkParametersConfigurationTest.kt @@ -4,12 +4,11 @@ import com.r3.corda.networkmanage.doorman.NetworkParametersConfiguration import com.r3.corda.networkmanage.doorman.NotaryConfiguration import com.r3.corda.networkmanage.doorman.parseNetworkParameters import com.r3.corda.networkmanage.doorman.parseNetworkParametersFrom -import com.typesafe.config.ConfigFactory import net.corda.core.identity.CordaX500Name import net.corda.core.internal.copyTo import net.corda.core.internal.deleteIfExists import net.corda.core.serialization.serialize -import net.corda.testing.SerializationEnvironmentRule +import net.corda.testing.core.SerializationEnvironmentRule import net.corda.testing.internal.createNodeInfoAndSigned import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThatThrownBy diff --git a/network-management/src/test/kotlin/com/r3/corda/networkmanage/TestBase.kt b/network-management/src/test/kotlin/com/r3/corda/networkmanage/TestBase.kt index abc8b28401..f46c899d50 100644 --- a/network-management/src/test/kotlin/com/r3/corda/networkmanage/TestBase.kt +++ b/network-management/src/test/kotlin/com/r3/corda/networkmanage/TestBase.kt @@ -6,7 +6,7 @@ import com.r3.corda.networkmanage.common.persistence.CertificateSigningRequest import com.r3.corda.networkmanage.common.persistence.CertificateStatus import com.r3.corda.networkmanage.common.persistence.RequestStatus import net.corda.core.crypto.SecureHash -import net.corda.testing.SerializationEnvironmentRule +import net.corda.testing.core.SerializationEnvironmentRule import org.bouncycastle.pkcs.PKCS10CertificationRequest import org.junit.Rule import java.security.cert.CertPath diff --git a/network-management/src/test/kotlin/com/r3/corda/networkmanage/doorman/signer/DefaultCsrHandlerTest.kt b/network-management/src/test/kotlin/com/r3/corda/networkmanage/doorman/signer/DefaultCsrHandlerTest.kt index 221d7aec11..6beb13d32c 100644 --- a/network-management/src/test/kotlin/com/r3/corda/networkmanage/doorman/signer/DefaultCsrHandlerTest.kt +++ b/network-management/src/test/kotlin/com/r3/corda/networkmanage/doorman/signer/DefaultCsrHandlerTest.kt @@ -12,6 +12,7 @@ import com.r3.corda.networkmanage.common.utils.buildCertPath import net.corda.core.crypto.Crypto import net.corda.core.internal.CertRole import net.corda.nodeapi.internal.crypto.X509Utilities +import net.corda.nodeapi.internal.crypto.x509Certificates import net.corda.testing.internal.createDevIntermediateCaCertPath import org.assertj.core.api.Assertions.assertThat import org.junit.Test @@ -73,7 +74,7 @@ class DefaultCsrHandlerTest : TestBase() { // Then make sure the generated node cert paths are correct certPathCapture.allValues.forEachIndexed { index, certPath -> - X509Utilities.validateCertificateChain(rootCa.certificate, *certPath.certificates.toTypedArray()) + X509Utilities.validateCertificateChain(rootCa.certificate, certPath.x509Certificates) assertThat(certPath.certificates).hasSize(3).element(1).isEqualTo(csrCa.certificate) (certPath.certificates[0] as X509Certificate).apply { assertThat(CertRole.extract(this)).isEqualTo(CertRole.NODE_CA) diff --git a/network-management/src/test/kotlin/com/r3/corda/networkmanage/doorman/webservice/NetworkMapWebServiceTest.kt b/network-management/src/test/kotlin/com/r3/corda/networkmanage/doorman/webservice/NetworkMapWebServiceTest.kt index 430d298a47..65d7a322d5 100644 --- a/network-management/src/test/kotlin/com/r3/corda/networkmanage/doorman/webservice/NetworkMapWebServiceTest.kt +++ b/network-management/src/test/kotlin/com/r3/corda/networkmanage/doorman/webservice/NetworkMapWebServiceTest.kt @@ -23,8 +23,8 @@ import net.corda.nodeapi.internal.createDevNetworkMapCa import net.corda.nodeapi.internal.crypto.CertificateAndKeyPair import net.corda.nodeapi.internal.network.NetworkMap import net.corda.nodeapi.internal.network.verifiedNetworkMapCert -import net.corda.testing.SerializationEnvironmentRule import net.corda.testing.common.internal.testNetworkParameters +import net.corda.testing.core.SerializationEnvironmentRule import net.corda.testing.internal.createDevIntermediateCaCertPath import net.corda.testing.internal.createNodeInfoAndSigned import org.assertj.core.api.Assertions.* diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/SetsSerializationTest.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/SetsSerializationTest.kt index 2074a7c398..203c07f1eb 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/SetsSerializationTest.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/SetsSerializationTest.kt @@ -6,10 +6,8 @@ import net.corda.core.serialization.deserialize import net.corda.core.serialization.serialize import net.corda.node.services.statemachine.DataSessionMessage import net.corda.nodeapi.internal.serialization.kryo.KryoHeaderV0_1 -import net.corda.testing.SerializationEnvironmentRule -import net.corda.testing.internal.kryoSpecific -import net.corda.testing.internal.kryoSpecific import net.corda.testing.core.SerializationEnvironmentRule +import net.corda.testing.internal.kryoSpecific import org.junit.Assert.assertArrayEquals import org.junit.Assert.assertEquals import org.junit.Rule diff --git a/node/src/integration-test/kotlin/net/corda/node/AuthDBTests.kt b/node/src/integration-test/kotlin/net/corda/node/AuthDBTests.kt index 5579ae6b28..1a42f5100c 100644 --- a/node/src/integration-test/kotlin/net/corda/node/AuthDBTests.kt +++ b/node/src/integration-test/kotlin/net/corda/node/AuthDBTests.kt @@ -14,11 +14,10 @@ import net.corda.node.internal.Node import net.corda.node.internal.StartedNode import net.corda.node.services.Permissions import net.corda.node.services.config.PasswordEncryption -import net.corda.testing.ALICE_NAME +import net.corda.testing.core.ALICE_NAME import net.corda.testing.internal.IntegrationTestSchemas import net.corda.testing.internal.toDatabaseSchemaName import net.corda.testing.node.internal.NodeBasedTest -import net.corda.testing.core.ALICE_NAME import org.apache.activemq.artemis.api.core.ActiveMQSecurityException import org.apache.shiro.authc.credential.DefaultPasswordService import org.junit.After diff --git a/node/src/integration-test/kotlin/net/corda/node/BootTests.kt b/node/src/integration-test/kotlin/net/corda/node/BootTests.kt index e3b2002371..fc94c70957 100644 --- a/node/src/integration-test/kotlin/net/corda/node/BootTests.kt +++ b/node/src/integration-test/kotlin/net/corda/node/BootTests.kt @@ -8,17 +8,17 @@ import net.corda.core.messaging.startFlow import net.corda.core.utilities.getOrThrow import net.corda.node.internal.NodeStartup import net.corda.node.services.Permissions.Companion.startFlow -import net.corda.testing.core.ALICE_NAME -import net.corda.testing.BOB_NAME -import net.corda.testing.DUMMY_BANK_A_NAME -import net.corda.testing.DUMMY_NOTARY_NAME -import net.corda.testing.node.User import net.corda.testing.common.internal.ProjectStructure.projectRootDir +import net.corda.testing.core.ALICE_NAME +import net.corda.testing.core.BOB_NAME +import net.corda.testing.core.DUMMY_BANK_A_NAME +import net.corda.testing.core.DUMMY_NOTARY_NAME import net.corda.testing.driver.driver import net.corda.testing.internal.IntegrationTest import net.corda.testing.internal.IntegrationTestSchemas import net.corda.testing.internal.toDatabaseSchemaName import net.corda.testing.internal.toDatabaseSchemaNames +import net.corda.testing.node.User import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThatThrownBy import org.junit.ClassRule diff --git a/node/src/integration-test/kotlin/net/corda/node/CordappScanningDriverTest.kt b/node/src/integration-test/kotlin/net/corda/node/CordappScanningDriverTest.kt index 4ca2161f4e..be09502bc0 100644 --- a/node/src/integration-test/kotlin/net/corda/node/CordappScanningDriverTest.kt +++ b/node/src/integration-test/kotlin/net/corda/node/CordappScanningDriverTest.kt @@ -10,12 +10,13 @@ import net.corda.core.utilities.unwrap import net.corda.node.services.Permissions.Companion.startFlow import net.corda.testing.core.ALICE_NAME import net.corda.testing.core.BOB_NAME +import net.corda.testing.core.DUMMY_NOTARY_NAME import net.corda.testing.core.chooseIdentity import net.corda.testing.driver.driver -import net.corda.testing.node.User import net.corda.testing.internal.IntegrationTest import net.corda.testing.internal.IntegrationTestSchemas import net.corda.testing.internal.toDatabaseSchemaName +import net.corda.testing.node.User import org.assertj.core.api.Assertions.assertThat import org.junit.ClassRule import org.junit.Test diff --git a/node/src/integration-test/kotlin/net/corda/node/NodeKeystoreCheckTest.kt b/node/src/integration-test/kotlin/net/corda/node/NodeKeystoreCheckTest.kt index 65a7dfe36e..fdf0253969 100644 --- a/node/src/integration-test/kotlin/net/corda/node/NodeKeystoreCheckTest.kt +++ b/node/src/integration-test/kotlin/net/corda/node/NodeKeystoreCheckTest.kt @@ -8,13 +8,13 @@ import net.corda.nodeapi.internal.config.SSLConfiguration import net.corda.nodeapi.internal.crypto.CertificateType import net.corda.nodeapi.internal.crypto.X509Utilities import net.corda.testing.core.ALICE_NAME -import net.corda.testing.DUMMY_NOTARY_NAME +import net.corda.testing.core.DUMMY_NOTARY_NAME +import net.corda.testing.driver.driver import net.corda.testing.internal.IntegrationTest import net.corda.testing.internal.IntegrationTestSchemas -import net.corda.testing.driver.driver import net.corda.testing.internal.toDatabaseSchemaName -import org.junit.ClassRule import org.assertj.core.api.Assertions.assertThatThrownBy +import org.junit.ClassRule import org.junit.Test import java.nio.file.Path import javax.security.auth.x500.X500Principal diff --git a/node/src/integration-test/kotlin/net/corda/node/NodePerformanceTests.kt b/node/src/integration-test/kotlin/net/corda/node/NodePerformanceTests.kt index fb76d063f4..58a7b7e8ff 100644 --- a/node/src/integration-test/kotlin/net/corda/node/NodePerformanceTests.kt +++ b/node/src/integration-test/kotlin/net/corda/node/NodePerformanceTests.kt @@ -13,10 +13,10 @@ import net.corda.finance.DOLLARS import net.corda.finance.flows.CashIssueFlow import net.corda.finance.flows.CashPaymentFlow import net.corda.node.services.Permissions.Companion.startFlow -import net.corda.testing.ALICE_NAME -import net.corda.testing.DUMMY_BANK_A_NAME +import net.corda.testing.core.ALICE_NAME +import net.corda.testing.core.DUMMY_BANK_A_NAME import net.corda.testing.core.DUMMY_NOTARY_NAME -import net.corda.testing.TestIdentity +import net.corda.testing.core.TestIdentity import net.corda.testing.driver.NodeHandle import net.corda.testing.driver.PortAllocation import net.corda.testing.driver.driver @@ -31,14 +31,11 @@ import net.corda.testing.node.internal.InternalDriverDSL import net.corda.testing.node.internal.performance.startPublishingFixedRateInjector import net.corda.testing.node.internal.performance.startReporter import net.corda.testing.node.internal.performance.startTightLoopInjector -import org.junit.Before import org.junit.ClassRule import org.junit.Ignore import org.junit.Test -import java.lang.management.ManagementFactory import java.util.* import java.util.concurrent.TimeUnit -import kotlin.streams.toList @Ignore("Run these locally") class NodePerformanceTests : IntegrationTest() { diff --git a/node/src/integration-test/kotlin/net/corda/node/NodeStartupPerformanceTests.kt b/node/src/integration-test/kotlin/net/corda/node/NodeStartupPerformanceTests.kt index 3020c7f731..1a88d55f35 100644 --- a/node/src/integration-test/kotlin/net/corda/node/NodeStartupPerformanceTests.kt +++ b/node/src/integration-test/kotlin/net/corda/node/NodeStartupPerformanceTests.kt @@ -1,7 +1,9 @@ package net.corda.node import com.google.common.base.Stopwatch -import net.corda.testing.* +import net.corda.testing.core.ALICE_NAME +import net.corda.testing.core.BOB_NAME +import net.corda.testing.core.DUMMY_BANK_A_NAME import net.corda.testing.driver.driver import net.corda.testing.internal.IntegrationTest import net.corda.testing.internal.IntegrationTestSchemas diff --git a/node/src/integration-test/kotlin/net/corda/node/services/AttachmentLoadingTests.kt b/node/src/integration-test/kotlin/net/corda/node/services/AttachmentLoadingTests.kt index 301353cdaa..5a2a963979 100644 --- a/node/src/integration-test/kotlin/net/corda/node/services/AttachmentLoadingTests.kt +++ b/node/src/integration-test/kotlin/net/corda/node/services/AttachmentLoadingTests.kt @@ -20,11 +20,7 @@ import net.corda.core.utilities.contextLogger import net.corda.core.utilities.getOrThrow import net.corda.node.internal.cordapp.CordappLoader import net.corda.node.internal.cordapp.CordappProviderImpl -import net.corda.testing.* -import net.corda.testing.core.DUMMY_BANK_A_NAME -import net.corda.testing.core.DUMMY_NOTARY_NAME -import net.corda.testing.core.SerializationEnvironmentRule -import net.corda.testing.core.TestIdentity +import net.corda.testing.core.* import net.corda.testing.driver.DriverDSL import net.corda.testing.driver.NodeHandle import net.corda.testing.driver.driver diff --git a/node/src/integration-test/kotlin/net/corda/node/services/MySQLNotaryServiceTests.kt b/node/src/integration-test/kotlin/net/corda/node/services/MySQLNotaryServiceTests.kt index af431a2a0d..f36c398d8d 100644 --- a/node/src/integration-test/kotlin/net/corda/node/services/MySQLNotaryServiceTests.kt +++ b/node/src/integration-test/kotlin/net/corda/node/services/MySQLNotaryServiceTests.kt @@ -18,10 +18,10 @@ import net.corda.node.services.config.NotaryConfig import net.corda.nodeapi.internal.DevIdentityGenerator import net.corda.nodeapi.internal.network.NetworkParametersCopier import net.corda.nodeapi.internal.network.NotaryInfo -import net.corda.testing.chooseIdentity import net.corda.testing.common.internal.testNetworkParameters import net.corda.testing.contracts.DummyContract -import net.corda.testing.dummyCommand +import net.corda.testing.core.chooseIdentity +import net.corda.testing.core.dummyCommand import net.corda.testing.internal.IntegrationTest import net.corda.testing.internal.IntegrationTestSchemas import net.corda.testing.node.MockNetwork diff --git a/node/src/integration-test/kotlin/net/corda/node/services/network/NetworkMapTest.kt b/node/src/integration-test/kotlin/net/corda/node/services/network/NetworkMapTest.kt index 7cff09ac7d..d40ed67c52 100644 --- a/node/src/integration-test/kotlin/net/corda/node/services/network/NetworkMapTest.kt +++ b/node/src/integration-test/kotlin/net/corda/node/services/network/NetworkMapTest.kt @@ -10,11 +10,11 @@ import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.seconds import net.corda.nodeapi.internal.network.NETWORK_PARAMS_FILE_NAME import net.corda.nodeapi.internal.network.NetworkParameters -import net.corda.testing.* +import net.corda.testing.common.internal.testNetworkParameters import net.corda.testing.core.ALICE_NAME import net.corda.testing.core.BOB_NAME +import net.corda.testing.core.DUMMY_NOTARY_NAME import net.corda.testing.core.SerializationEnvironmentRule -import net.corda.testing.common.internal.testNetworkParameters import net.corda.testing.driver.NodeHandle import net.corda.testing.driver.PortAllocation import net.corda.testing.internal.IntegrationTest diff --git a/node/src/integration-test/kotlin/net/corda/node/services/vault/VaultQueryIntegrationTests.kt b/node/src/integration-test/kotlin/net/corda/node/services/vault/VaultQueryIntegrationTests.kt index 641c49a5af..fb30eaa538 100644 --- a/node/src/integration-test/kotlin/net/corda/node/services/vault/VaultQueryIntegrationTests.kt +++ b/node/src/integration-test/kotlin/net/corda/node/services/vault/VaultQueryIntegrationTests.kt @@ -2,7 +2,7 @@ package net.corda.node.services.vault import net.corda.core.identity.CordaX500Name -import net.corda.testing.* +import net.corda.testing.core.TestIdentity import net.corda.testing.internal.IntegrationTest import net.corda.testing.internal.IntegrationTestSchemas import net.corda.testing.internal.toDatabaseSchemaName diff --git a/node/src/integration-test/kotlin/net/corda/node/utilities/registration/NodeRegistrationTest.kt b/node/src/integration-test/kotlin/net/corda/node/utilities/registration/NodeRegistrationTest.kt index ae6888478d..5cf2621478 100644 --- a/node/src/integration-test/kotlin/net/corda/node/utilities/registration/NodeRegistrationTest.kt +++ b/node/src/integration-test/kotlin/net/corda/node/utilities/registration/NodeRegistrationTest.kt @@ -16,7 +16,6 @@ import net.corda.nodeapi.internal.crypto.X509Utilities import net.corda.nodeapi.internal.crypto.X509Utilities.CORDA_CLIENT_CA import net.corda.nodeapi.internal.crypto.X509Utilities.CORDA_INTERMEDIATE_CA import net.corda.nodeapi.internal.crypto.X509Utilities.CORDA_ROOT_CA -import net.corda.testing.* import net.corda.testing.common.internal.testNetworkParameters import net.corda.testing.core.DEV_ROOT_CA import net.corda.testing.core.SerializationEnvironmentRule @@ -32,10 +31,7 @@ import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThatThrownBy import org.bouncycastle.pkcs.PKCS10CertificationRequest import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequest -import org.junit.After -import org.junit.Before -import org.junit.Rule -import org.junit.Test +import org.junit.* import java.io.ByteArrayOutputStream import java.io.InputStream import java.net.URL diff --git a/node/src/integration-test/kotlin/net/corda/services/messaging/MQSecurityTest.kt b/node/src/integration-test/kotlin/net/corda/services/messaging/MQSecurityTest.kt index 953bd29642..2dc72f58f0 100644 --- a/node/src/integration-test/kotlin/net/corda/services/messaging/MQSecurityTest.kt +++ b/node/src/integration-test/kotlin/net/corda/services/messaging/MQSecurityTest.kt @@ -3,9 +3,7 @@ package net.corda.services.messaging import co.paralleluniverse.fibers.Suspendable import net.corda.client.rpc.CordaRPCClient import net.corda.client.rpc.CordaRPCConnection -import net.corda.core.crypto.generateKeyPair import net.corda.core.crypto.random63BitValue -import net.corda.core.crypto.toStringShort import net.corda.core.flows.FlowLogic import net.corda.core.flows.FlowSession import net.corda.core.flows.InitiatedBy @@ -22,14 +20,10 @@ import net.corda.nodeapi.RPCApi import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.INTERNAL_PREFIX import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.NOTIFICATIONS_ADDRESS import net.corda.nodeapi.internal.config.SSLConfiguration -import net.corda.testing.ALICE_NAME -import net.corda.testing.BOB_NAME -import net.corda.testing.chooseIdentity -import net.corda.testing.internal.IntegrationTestSchemas import net.corda.testing.core.ALICE_NAME import net.corda.testing.core.BOB_NAME -import net.corda.testing.node.User import net.corda.testing.core.chooseIdentity +import net.corda.testing.internal.IntegrationTestSchemas import net.corda.testing.internal.configureTestSSL import net.corda.testing.internal.toDatabaseSchemaName import net.corda.testing.node.User diff --git a/node/src/integration-test/kotlin/net/corda/services/messaging/P2PMessagingTest.kt b/node/src/integration-test/kotlin/net/corda/services/messaging/P2PMessagingTest.kt index 5f471f4f73..fc02be4e79 100644 --- a/node/src/integration-test/kotlin/net/corda/services/messaging/P2PMessagingTest.kt +++ b/node/src/integration-test/kotlin/net/corda/services/messaging/P2PMessagingTest.kt @@ -17,13 +17,8 @@ import net.corda.node.internal.StartedNode import net.corda.node.services.messaging.MessagingService import net.corda.node.services.messaging.ReceivedMessage import net.corda.node.services.messaging.send -import net.corda.node.services.transactions.RaftValidatingNotaryService -import net.corda.testing.* -import net.corda.node.services.messaging.* import net.corda.testing.core.ALICE_NAME import net.corda.testing.core.chooseIdentity -import net.corda.testing.ALICE_NAME -import net.corda.testing.chooseIdentity import net.corda.testing.driver.DriverDSL import net.corda.testing.driver.NodeHandle import net.corda.testing.driver.driver diff --git a/node/src/integration-test/kotlin/net/corda/test/node/NodeStatePersistenceTests.kt b/node/src/integration-test/kotlin/net/corda/test/node/NodeStatePersistenceTests.kt index 6fbdf1bd7d..422d361203 100644 --- a/node/src/integration-test/kotlin/net/corda/test/node/NodeStatePersistenceTests.kt +++ b/node/src/integration-test/kotlin/net/corda/test/node/NodeStatePersistenceTests.kt @@ -19,15 +19,14 @@ import net.corda.core.utilities.ProgressTracker import net.corda.core.utilities.getOrThrow import net.corda.node.services.Permissions.Companion.invokeRpc import net.corda.node.services.Permissions.Companion.startFlow -import net.corda.testing.node.User -import net.corda.testing.* -import net.corda.testing.core.chooseIdentity +import net.corda.testing.core.* import net.corda.testing.driver.PortAllocation import net.corda.testing.driver.driver import net.corda.testing.internal.IntegrationTest import net.corda.testing.internal.IntegrationTestSchemas import net.corda.testing.internal.toDatabaseSchemaName import net.corda.testing.internal.toDatabaseSchemaNames +import net.corda.testing.node.User import org.junit.Assume.assumeFalse import org.junit.ClassRule import org.junit.Test diff --git a/node/src/test/java/net/corda/node/services/vault/VaultQueryJavaTests.java b/node/src/test/java/net/corda/node/services/vault/VaultQueryJavaTests.java index d6d61045f4..e62df0d8de 100644 --- a/node/src/test/java/net/corda/node/services/vault/VaultQueryJavaTests.java +++ b/node/src/test/java/net/corda/node/services/vault/VaultQueryJavaTests.java @@ -46,11 +46,8 @@ import java.util.stream.StreamSupport; import static net.corda.core.node.services.vault.QueryCriteriaUtils.DEFAULT_PAGE_NUM; import static net.corda.core.node.services.vault.QueryCriteriaUtils.MAX_PAGE_SIZE; import static net.corda.core.utilities.ByteArrays.toHexString; -import static net.corda.testing.TestConstants.*; +import static net.corda.testing.core.TestConstants.*; import static net.corda.testing.internal.InternalTestUtilsKt.rigorousMock; -import static net.corda.testing.core.TestConstants.BOC_NAME; -import static net.corda.testing.core.TestConstants.CHARLIE_NAME; -import static net.corda.testing.core.TestConstants.DUMMY_NOTARY_NAME; import static net.corda.testing.node.MockServices.makeTestDatabaseAndMockServices; import static net.corda.testing.node.MockServicesKt.makeTestIdentityService; import static org.assertj.core.api.Assertions.assertThat; diff --git a/node/src/test/kotlin/net/corda/node/CordaRPCOpsImplTest.kt b/node/src/test/kotlin/net/corda/node/CordaRPCOpsImplTest.kt index 7adeb9c70f..1f89c4d5fe 100644 --- a/node/src/test/kotlin/net/corda/node/CordaRPCOpsImplTest.kt +++ b/node/src/test/kotlin/net/corda/node/CordaRPCOpsImplTest.kt @@ -10,7 +10,9 @@ import net.corda.core.contracts.ContractState import net.corda.core.contracts.Issued import net.corda.core.crypto.isFulfilledBy import net.corda.core.crypto.keys -import net.corda.core.flows.* +import net.corda.core.flows.FlowLogic +import net.corda.core.flows.StartableByRPC +import net.corda.core.flows.StateMachineRunId import net.corda.core.identity.Party import net.corda.core.messaging.* import net.corda.core.node.services.Vault @@ -33,9 +35,6 @@ import net.corda.node.services.Permissions.Companion.startFlow import net.corda.node.services.messaging.CURRENT_RPC_CONTEXT import net.corda.node.services.messaging.RpcAuthContext import net.corda.nodeapi.internal.config.User -import net.corda.testing.ALICE_NAME -import net.corda.testing.expect -import net.corda.testing.expectEvents import net.corda.testing.core.ALICE_NAME import net.corda.testing.core.expect import net.corda.testing.core.expectEvents @@ -44,7 +43,6 @@ import net.corda.testing.node.MockNetwork import net.corda.testing.node.MockNetwork.MockNode import net.corda.testing.node.MockNodeParameters import net.corda.testing.node.testActor -import net.corda.testing.sequence import org.apache.commons.io.IOUtils import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThatExceptionOfType diff --git a/node/src/test/kotlin/net/corda/node/services/config/NodeConfigurationImplTest.kt b/node/src/test/kotlin/net/corda/node/services/config/NodeConfigurationImplTest.kt index 5fdd8d9110..f14bce3268 100644 --- a/node/src/test/kotlin/net/corda/node/services/config/NodeConfigurationImplTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/config/NodeConfigurationImplTest.kt @@ -65,7 +65,7 @@ class NodeConfigurationImplTest { return testConfiguration.copy(devMode = devMode, devModeOptions = devModeOptions) } - private fun testConfiguration(dataSourceProperties: Properties): NodeConfigurationImpl { + private fun testConfiguration(dataSourceProperties: Properties): NodeConfigurationImpl { return testConfiguration.copy(dataSourceProperties = dataSourceProperties) } diff --git a/node/src/test/kotlin/net/corda/node/services/network/NetworkMapCacheTest.kt b/node/src/test/kotlin/net/corda/node/services/network/NetworkMapCacheTest.kt index 3202b186ae..0eb496fa9a 100644 --- a/node/src/test/kotlin/net/corda/node/services/network/NetworkMapCacheTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/network/NetworkMapCacheTest.kt @@ -2,16 +2,13 @@ package net.corda.node.services.network import net.corda.core.crypto.generateKeyPair import net.corda.core.node.services.NetworkMapCache +import net.corda.node.services.api.NetworkMapCacheInternal import net.corda.testing.core.ALICE_NAME import net.corda.testing.core.BOB_NAME -import net.corda.node.services.api.NetworkMapCacheInternal -import net.corda.testing.ALICE_NAME -import net.corda.testing.BOB_NAME -import net.corda.testing.getTestPartyAndCertificate import net.corda.testing.core.getTestPartyAndCertificate +import net.corda.testing.core.singleIdentity import net.corda.testing.node.MockNetwork import net.corda.testing.node.MockNodeParameters -import net.corda.testing.core.singleIdentity import org.assertj.core.api.Assertions.assertThat import org.junit.After import org.junit.Test diff --git a/node/src/test/kotlin/net/corda/node/services/persistence/DBCheckpointStorageTests.kt b/node/src/test/kotlin/net/corda/node/services/persistence/DBCheckpointStorageTests.kt index f431989841..bc1987f5f7 100644 --- a/node/src/test/kotlin/net/corda/node/services/persistence/DBCheckpointStorageTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/persistence/DBCheckpointStorageTests.kt @@ -13,13 +13,11 @@ import net.corda.node.services.statemachine.FlowStart import net.corda.node.services.transactions.PersistentUniquenessProvider import net.corda.nodeapi.internal.persistence.CordaPersistence import net.corda.nodeapi.internal.persistence.DatabaseConfig -import net.corda.testing.ALICE_NAME -import net.corda.testing.SerializationEnvironmentRule -import net.corda.testing.TestIdentity +import net.corda.testing.core.ALICE_NAME +import net.corda.testing.core.SerializationEnvironmentRule +import net.corda.testing.core.TestIdentity import net.corda.testing.internal.LogHelper import net.corda.testing.internal.rigorousMock -import net.corda.testing.internal.LogHelper -import net.corda.testing.core.SerializationEnvironmentRule import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties import org.assertj.core.api.Assertions.assertThat import org.junit.After diff --git a/perftestcordapp/src/test/kotlin/com/r3/corda/enterprise/perftestcordapp/contracts/CommercialPaperTests.kt b/perftestcordapp/src/test/kotlin/com/r3/corda/enterprise/perftestcordapp/contracts/CommercialPaperTests.kt index 29cd2e270c..b1598f056c 100644 --- a/perftestcordapp/src/test/kotlin/com/r3/corda/enterprise/perftestcordapp/contracts/CommercialPaperTests.kt +++ b/perftestcordapp/src/test/kotlin/com/r3/corda/enterprise/perftestcordapp/contracts/CommercialPaperTests.kt @@ -16,7 +16,7 @@ import net.corda.core.node.services.VaultService import net.corda.core.transactions.SignedTransaction import net.corda.core.utilities.days import net.corda.node.services.api.IdentityServiceInternal -import net.corda.testing.* +import net.corda.testing.core.* import net.corda.testing.dsl.EnforceVerifyOrFail import net.corda.testing.dsl.TransactionDSL import net.corda.testing.dsl.TransactionDSLInterpreter diff --git a/perftestcordapp/src/test/kotlin/com/r3/corda/enterprise/perftestcordapp/contracts/asset/CashTests.kt b/perftestcordapp/src/test/kotlin/com/r3/corda/enterprise/perftestcordapp/contracts/asset/CashTests.kt index d0b0c3aed8..2a15abe870 100644 --- a/perftestcordapp/src/test/kotlin/com/r3/corda/enterprise/perftestcordapp/contracts/asset/CashTests.kt +++ b/perftestcordapp/src/test/kotlin/com/r3/corda/enterprise/perftestcordapp/contracts/asset/CashTests.kt @@ -25,8 +25,8 @@ import net.corda.core.utilities.OpaqueBytes import net.corda.node.services.api.IdentityServiceInternal import net.corda.node.services.vault.NodeVaultService import net.corda.nodeapi.internal.persistence.CordaPersistence -import net.corda.testing.* import net.corda.testing.contracts.DummyState +import net.corda.testing.core.* import net.corda.testing.dsl.EnforceVerifyOrFail import net.corda.testing.dsl.TransactionDSL import net.corda.testing.dsl.TransactionDSLInterpreter diff --git a/perftestcordapp/src/test/kotlin/com/r3/corda/enterprise/perftestcordapp/flows/CashExitFlowTests.kt b/perftestcordapp/src/test/kotlin/com/r3/corda/enterprise/perftestcordapp/flows/CashExitFlowTests.kt index 041aff1263..177d2ba983 100644 --- a/perftestcordapp/src/test/kotlin/com/r3/corda/enterprise/perftestcordapp/flows/CashExitFlowTests.kt +++ b/perftestcordapp/src/test/kotlin/com/r3/corda/enterprise/perftestcordapp/flows/CashExitFlowTests.kt @@ -7,7 +7,7 @@ import net.corda.core.identity.Party import net.corda.core.utilities.OpaqueBytes import net.corda.core.utilities.getOrThrow import net.corda.node.internal.StartedNode -import net.corda.testing.BOC_NAME +import net.corda.testing.core.BOC_NAME import net.corda.testing.node.InMemoryMessagingNetwork.ServicePeerAllocationStrategy.RoundRobin import net.corda.testing.node.MockNetwork import net.corda.testing.node.MockNetwork.MockNode diff --git a/perftestcordapp/src/test/kotlin/com/r3/corda/enterprise/perftestcordapp/flows/CashIssueAndPaymentFlowTests.kt b/perftestcordapp/src/test/kotlin/com/r3/corda/enterprise/perftestcordapp/flows/CashIssueAndPaymentFlowTests.kt index f4f79e8bf8..06a6a1fc99 100644 --- a/perftestcordapp/src/test/kotlin/com/r3/corda/enterprise/perftestcordapp/flows/CashIssueAndPaymentFlowTests.kt +++ b/perftestcordapp/src/test/kotlin/com/r3/corda/enterprise/perftestcordapp/flows/CashIssueAndPaymentFlowTests.kt @@ -10,7 +10,7 @@ import net.corda.core.node.services.vault.QueryCriteria import net.corda.core.utilities.OpaqueBytes import net.corda.core.utilities.getOrThrow import net.corda.node.internal.StartedNode -import net.corda.testing.* +import net.corda.testing.core.* import net.corda.testing.node.InMemoryMessagingNetwork.ServicePeerAllocationStrategy.RoundRobin import net.corda.testing.node.MockNetwork import net.corda.testing.node.MockNetwork.MockNode diff --git a/perftestcordapp/src/test/kotlin/com/r3/corda/enterprise/perftestcordapp/flows/CashIssueAndPaymentNoSelectionFlowTest.kt b/perftestcordapp/src/test/kotlin/com/r3/corda/enterprise/perftestcordapp/flows/CashIssueAndPaymentNoSelectionFlowTest.kt index 56f4b09b08..fd316b6373 100644 --- a/perftestcordapp/src/test/kotlin/com/r3/corda/enterprise/perftestcordapp/flows/CashIssueAndPaymentNoSelectionFlowTest.kt +++ b/perftestcordapp/src/test/kotlin/com/r3/corda/enterprise/perftestcordapp/flows/CashIssueAndPaymentNoSelectionFlowTest.kt @@ -10,7 +10,7 @@ import net.corda.core.node.services.vault.QueryCriteria import net.corda.core.utilities.OpaqueBytes import net.corda.core.utilities.getOrThrow import net.corda.node.internal.StartedNode -import net.corda.testing.* +import net.corda.testing.core.* import net.corda.testing.node.InMemoryMessagingNetwork.ServicePeerAllocationStrategy.RoundRobin import net.corda.testing.node.MockNetwork import net.corda.testing.node.MockNetwork.MockNode diff --git a/perftestcordapp/src/test/kotlin/com/r3/corda/enterprise/perftestcordapp/flows/CashIssueFlowTests.kt b/perftestcordapp/src/test/kotlin/com/r3/corda/enterprise/perftestcordapp/flows/CashIssueFlowTests.kt index cf46c8cc06..870e316755 100644 --- a/perftestcordapp/src/test/kotlin/com/r3/corda/enterprise/perftestcordapp/flows/CashIssueFlowTests.kt +++ b/perftestcordapp/src/test/kotlin/com/r3/corda/enterprise/perftestcordapp/flows/CashIssueFlowTests.kt @@ -7,7 +7,7 @@ import net.corda.core.identity.Party import net.corda.core.utilities.OpaqueBytes import net.corda.core.utilities.getOrThrow import net.corda.node.internal.StartedNode -import net.corda.testing.BOC_NAME +import net.corda.testing.core.BOC_NAME import net.corda.testing.node.InMemoryMessagingNetwork.ServicePeerAllocationStrategy.RoundRobin import net.corda.testing.node.MockNetwork import net.corda.testing.node.MockNetwork.MockNode diff --git a/perftestcordapp/src/test/kotlin/com/r3/corda/enterprise/perftestcordapp/flows/CashPaymentFlowTests.kt b/perftestcordapp/src/test/kotlin/com/r3/corda/enterprise/perftestcordapp/flows/CashPaymentFlowTests.kt index a876d81420..3df1acd160 100644 --- a/perftestcordapp/src/test/kotlin/com/r3/corda/enterprise/perftestcordapp/flows/CashPaymentFlowTests.kt +++ b/perftestcordapp/src/test/kotlin/com/r3/corda/enterprise/perftestcordapp/flows/CashPaymentFlowTests.kt @@ -10,7 +10,7 @@ import net.corda.core.node.services.vault.QueryCriteria import net.corda.core.utilities.OpaqueBytes import net.corda.core.utilities.getOrThrow import net.corda.node.internal.StartedNode -import net.corda.testing.* +import net.corda.testing.core.* import net.corda.testing.node.InMemoryMessagingNetwork.ServicePeerAllocationStrategy.RoundRobin import net.corda.testing.node.MockNetwork import net.corda.testing.node.MockNetwork.MockNode diff --git a/perftestcordapp/src/test/kotlin/com/r3/corda/enterprise/perftestcordapp/flows/TwoPartyTradeFlowTest.kt b/perftestcordapp/src/test/kotlin/com/r3/corda/enterprise/perftestcordapp/flows/TwoPartyTradeFlowTest.kt index a70fd21d16..a805adb30a 100644 --- a/perftestcordapp/src/test/kotlin/com/r3/corda/enterprise/perftestcordapp/flows/TwoPartyTradeFlowTest.kt +++ b/perftestcordapp/src/test/kotlin/com/r3/corda/enterprise/perftestcordapp/flows/TwoPartyTradeFlowTest.kt @@ -46,7 +46,7 @@ import net.corda.node.services.api.WritableTransactionStorage import net.corda.node.services.persistence.DBTransactionStorage import net.corda.node.services.statemachine.Checkpoint import net.corda.nodeapi.internal.persistence.CordaPersistence -import net.corda.testing.* +import net.corda.testing.core.* import net.corda.testing.dsl.LedgerDSL import net.corda.testing.dsl.TestLedgerDSLInterpreter import net.corda.testing.dsl.TestTransactionDSLInterpreter diff --git a/samples/attachment-demo/src/integration-test/kotlin/net/corda/attachmentdemo/AttachmentDemoTest.kt b/samples/attachment-demo/src/integration-test/kotlin/net/corda/attachmentdemo/AttachmentDemoTest.kt index 1f87421849..8ecb0d98a3 100644 --- a/samples/attachment-demo/src/integration-test/kotlin/net/corda/attachmentdemo/AttachmentDemoTest.kt +++ b/samples/attachment-demo/src/integration-test/kotlin/net/corda/attachmentdemo/AttachmentDemoTest.kt @@ -6,13 +6,13 @@ import net.corda.node.services.Permissions.Companion.invokeRpc import net.corda.node.services.Permissions.Companion.startFlow import net.corda.testing.core.DUMMY_BANK_A_NAME import net.corda.testing.core.DUMMY_BANK_B_NAME -import net.corda.testing.DUMMY_NOTARY_NAME -import net.corda.testing.node.User +import net.corda.testing.core.DUMMY_NOTARY_NAME import net.corda.testing.driver.PortAllocation import net.corda.testing.driver.driver import net.corda.testing.internal.IntegrationTest import net.corda.testing.internal.IntegrationTestSchemas import net.corda.testing.internal.toDatabaseSchemaName +import net.corda.testing.node.User import org.junit.ClassRule import org.junit.Test import java.util.concurrent.CompletableFuture.supplyAsync diff --git a/samples/bank-of-corda-demo/src/integration-test/kotlin/net/corda/bank/BankOfCordaRPCClientTest.kt b/samples/bank-of-corda-demo/src/integration-test/kotlin/net/corda/bank/BankOfCordaRPCClientTest.kt index 8cb16e26c3..2214dfca5b 100644 --- a/samples/bank-of-corda-demo/src/integration-test/kotlin/net/corda/bank/BankOfCordaRPCClientTest.kt +++ b/samples/bank-of-corda-demo/src/integration-test/kotlin/net/corda/bank/BankOfCordaRPCClientTest.kt @@ -11,15 +11,12 @@ import net.corda.finance.contracts.asset.Cash import net.corda.finance.flows.CashIssueAndPaymentFlow import net.corda.node.services.Permissions.Companion.invokeRpc import net.corda.node.services.Permissions.Companion.startFlow -import net.corda.testing.core.BOC_NAME -import net.corda.testing.core.expect -import net.corda.testing.core.expectEvents -import net.corda.testing.core.sequence +import net.corda.testing.core.* import net.corda.testing.driver.driver -import net.corda.testing.node.User import net.corda.testing.internal.IntegrationTest import net.corda.testing.internal.IntegrationTestSchemas import net.corda.testing.internal.toDatabaseSchemaName +import net.corda.testing.node.User import org.junit.ClassRule import org.junit.Test diff --git a/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/RaftNotaryCordform.kt b/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/RaftNotaryCordform.kt index bf8918272b..079b06fa55 100644 --- a/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/RaftNotaryCordform.kt +++ b/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/RaftNotaryCordform.kt @@ -8,12 +8,9 @@ import net.corda.core.utilities.NetworkHostAndPort import net.corda.node.services.config.NotaryConfig import net.corda.node.services.config.RaftConfig import net.corda.nodeapi.internal.DevIdentityGenerator -import net.corda.testing.ALICE_NAME -import net.corda.testing.BOB_NAME -import net.corda.testing.node.internal.demorun.* -import net.corda.testing.node.internal.demorun.* import net.corda.testing.core.ALICE_NAME import net.corda.testing.core.BOB_NAME +import net.corda.testing.node.internal.demorun.* import java.nio.file.Paths fun main(args: Array) = RaftNotaryCordform().deployNodes() diff --git a/samples/simm-valuation-demo/src/integration-test/kotlin/net/corda/vega/SimmValuationTest.kt b/samples/simm-valuation-demo/src/integration-test/kotlin/net/corda/vega/SimmValuationTest.kt index 154de8ea5a..9c5c3a70c6 100644 --- a/samples/simm-valuation-demo/src/integration-test/kotlin/net/corda/vega/SimmValuationTest.kt +++ b/samples/simm-valuation-demo/src/integration-test/kotlin/net/corda/vega/SimmValuationTest.kt @@ -3,9 +3,9 @@ package net.corda.vega import com.opengamma.strata.product.common.BuySell import net.corda.core.identity.CordaX500Name import net.corda.core.utilities.getOrThrow -import net.corda.testing.* import net.corda.testing.core.DUMMY_BANK_A_NAME import net.corda.testing.core.DUMMY_BANK_B_NAME +import net.corda.testing.core.DUMMY_NOTARY_NAME import net.corda.testing.driver.driver import net.corda.testing.http.HttpApi import net.corda.testing.internal.IntegrationTest diff --git a/samples/trader-demo/src/integration-test/kotlin/net/corda/traderdemo/TraderDemoTest.kt b/samples/trader-demo/src/integration-test/kotlin/net/corda/traderdemo/TraderDemoTest.kt index 49b99656bc..5765601d29 100644 --- a/samples/trader-demo/src/integration-test/kotlin/net/corda/traderdemo/TraderDemoTest.kt +++ b/samples/trader-demo/src/integration-test/kotlin/net/corda/traderdemo/TraderDemoTest.kt @@ -8,10 +8,7 @@ import net.corda.finance.flows.CashIssueFlow import net.corda.finance.flows.CashPaymentFlow import net.corda.node.services.Permissions.Companion.all import net.corda.node.services.Permissions.Companion.startFlow -import net.corda.testing.core.BOC_NAME -import net.corda.testing.core.DUMMY_BANK_A_NAME -import net.corda.testing.core.DUMMY_BANK_B_NAME -import net.corda.testing.core.chooseIdentity +import net.corda.testing.core.* import net.corda.testing.driver.NodeHandle import net.corda.testing.driver.driver import net.corda.testing.internal.IntegrationTest diff --git a/testing/node-driver/src/integration-test/kotlin/net/corda/testing/driver/DriverTests.kt b/testing/node-driver/src/integration-test/kotlin/net/corda/testing/driver/DriverTests.kt index c515ede95a..0c4656e2f1 100644 --- a/testing/node-driver/src/integration-test/kotlin/net/corda/testing/driver/DriverTests.kt +++ b/testing/node-driver/src/integration-test/kotlin/net/corda/testing/driver/DriverTests.kt @@ -9,8 +9,9 @@ import net.corda.core.internal.readLines import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.getOrThrow import net.corda.node.internal.NodeStartup -import net.corda.testing.* import net.corda.testing.common.internal.ProjectStructure.projectRootDir +import net.corda.testing.core.DUMMY_BANK_A_NAME +import net.corda.testing.core.DUMMY_NOTARY_NAME import net.corda.testing.http.HttpApi import net.corda.testing.internal.IntegrationTest import net.corda.testing.internal.IntegrationTestSchemas @@ -19,10 +20,6 @@ import net.corda.testing.node.NotarySpec import net.corda.testing.node.internal.addressMustBeBound import net.corda.testing.node.internal.addressMustNotBeBound import net.corda.testing.node.internal.internalDriver -import net.corda.testing.core.DUMMY_BANK_A_NAME -import net.corda.testing.core.DUMMY_NOTARY_NAME -import net.corda.testing.http.HttpApi -import net.corda.testing.node.NotarySpec import org.assertj.core.api.Assertions.assertThat import org.json.simple.JSONObject import org.junit.ClassRule diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/NodeTestUtils.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/NodeTestUtils.kt index f7cc5028bb..0ce2359249 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/NodeTestUtils.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/NodeTestUtils.kt @@ -15,9 +15,6 @@ import net.corda.core.serialization.internal.effectiveSerializationEnv import net.corda.core.transactions.TransactionBuilder import net.corda.core.utilities.getOrThrow import net.corda.node.services.api.StartedNodeServices -import net.corda.testing.SerializationEnvironmentRule -import net.corda.testing.TestIdentity -import net.corda.testing.chooseIdentity import net.corda.testing.core.SerializationEnvironmentRule import net.corda.testing.core.TestIdentity import net.corda.testing.core.chooseIdentity diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/NodeBasedTest.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/NodeBasedTest.kt index 9e973e24f1..612f499510 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/NodeBasedTest.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/NodeBasedTest.kt @@ -13,10 +13,9 @@ import net.corda.node.internal.StartedNode import net.corda.node.internal.cordapp.CordappLoader import net.corda.node.services.config.* import net.corda.nodeapi.internal.config.toConfig -import net.corda.testing.core.SerializationEnvironmentRule import net.corda.nodeapi.internal.network.NetworkParametersCopier -import net.corda.testing.SerializationEnvironmentRule import net.corda.testing.common.internal.testNetworkParameters +import net.corda.testing.core.SerializationEnvironmentRule import net.corda.testing.core.getFreeLocalPorts import net.corda.testing.internal.IntegrationTest import net.corda.testing.internal.testThreadFactory diff --git a/testing/test-utils/src/main/kotlin/net/corda/testing/internal/TestNodeInfoBuilder.kt b/testing/test-utils/src/main/kotlin/net/corda/testing/internal/TestNodeInfoBuilder.kt index 90e0a18d26..c99ca190cf 100644 --- a/testing/test-utils/src/main/kotlin/net/corda/testing/internal/TestNodeInfoBuilder.kt +++ b/testing/test-utils/src/main/kotlin/net/corda/testing/internal/TestNodeInfoBuilder.kt @@ -11,14 +11,6 @@ import net.corda.nodeapi.internal.SignedNodeInfo import net.corda.nodeapi.internal.createDevNodeCa import net.corda.nodeapi.internal.crypto.CertificateAndKeyPair import net.corda.nodeapi.internal.crypto.CertificateType -import net.corda.nodeapi.internal.crypto.X509CertificateFactory -import net.corda.nodeapi.internal.crypto.X509Utilities -import net.corda.testing.DEV_INTERMEDIATE_CA -import net.corda.testing.DEV_ROOT_CA -import java.security.KeyPair -import net.corda.nodeapi.internal.createDevNodeCa -import net.corda.nodeapi.internal.crypto.CertificateAndKeyPair -import net.corda.nodeapi.internal.crypto.CertificateType import net.corda.nodeapi.internal.crypto.X509Utilities import net.corda.testing.core.DEV_INTERMEDIATE_CA import net.corda.testing.core.DEV_ROOT_CA diff --git a/tools/explorer/src/main/kotlin/net/corda/explorer/views/cordapps/iou/NewTransaction.kt b/tools/explorer/src/main/kotlin/net/corda/explorer/views/cordapps/iou/NewTransaction.kt index 281eaa0c5c..fde1d620b7 100644 --- a/tools/explorer/src/main/kotlin/net/corda/explorer/views/cordapps/iou/NewTransaction.kt +++ b/tools/explorer/src/main/kotlin/net/corda/explorer/views/cordapps/iou/NewTransaction.kt @@ -12,7 +12,10 @@ import javafx.scene.layout.GridPane import javafx.scene.text.Font import javafx.scene.text.FontWeight import javafx.stage.Window -import net.corda.client.jfx.model.* +import net.corda.client.jfx.model.NetworkIdentityModel +import net.corda.client.jfx.model.NodeMonitorModel +import net.corda.client.jfx.model.observableList +import net.corda.client.jfx.model.observableValue import net.corda.client.jfx.utils.isNotNull import net.corda.client.jfx.utils.map import net.corda.core.flows.FlowException @@ -24,12 +27,12 @@ import net.corda.core.messaging.startFlow import net.corda.core.transactions.SignedTransaction import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.loggerFor -import net.corda.sample.businessnetwork.iou.IOUFlow import net.corda.explorer.formatters.PartyNameFormatter import net.corda.explorer.model.MembershipListModel import net.corda.explorer.views.bigDecimalFormatter import net.corda.explorer.views.stringConverter -import net.corda.testing.chooseIdentityAndCert +import net.corda.sample.businessnetwork.iou.IOUFlow +import net.corda.testing.core.chooseIdentityAndCert import org.controlsfx.dialog.ExceptionDialog import tornadofx.* diff --git a/tools/jmeter/src/test/kotlin/com/r3/corda/jmeter/StartLocalPerfCorDapp.kt b/tools/jmeter/src/test/kotlin/com/r3/corda/jmeter/StartLocalPerfCorDapp.kt index 15939f8731..c7d4750744 100644 --- a/tools/jmeter/src/test/kotlin/com/r3/corda/jmeter/StartLocalPerfCorDapp.kt +++ b/tools/jmeter/src/test/kotlin/com/r3/corda/jmeter/StartLocalPerfCorDapp.kt @@ -2,7 +2,9 @@ package com.r3.corda.jmeter import net.corda.core.utilities.getOrThrow import net.corda.node.services.Permissions -import net.corda.testing.DUMMY_NOTARY_NAME +import net.corda.testing.core.DUMMY_BANK_A_NAME +import net.corda.testing.core.DUMMY_BANK_B_NAME +import net.corda.testing.core.DUMMY_NOTARY_NAME import net.corda.testing.node.NotarySpec import net.corda.testing.node.User import org.slf4j.LoggerFactory @@ -23,8 +25,8 @@ class StartLocalPerfCorDapp { notarySpecs = listOf(NotarySpec(DUMMY_NOTARY_NAME, validating = false)), extraCordappPackagesToScan = listOf("com.r3.corda.enterprise.perftestcordapp")) { val (nodeA, nodeB) = listOf( - startNode(providedName = net.corda.testing.DUMMY_BANK_A_NAME, rpcUsers = listOf(demoUser), maximumHeapSize = "1G"), - startNode(providedName = net.corda.testing.DUMMY_BANK_B_NAME, rpcUsers = listOf(demoUser), maximumHeapSize = "1G") + startNode(providedName = DUMMY_BANK_A_NAME, rpcUsers = listOf(demoUser), maximumHeapSize = "1G"), + startNode(providedName = DUMMY_BANK_B_NAME, rpcUsers = listOf(demoUser), maximumHeapSize = "1G") ).map { it.getOrThrow() } log.info("Nodes started!") val input = BufferedReader(InputStreamReader(System.`in`)) diff --git a/verify-enclave/src/integration-test/kotlin/com/r3/enclaves/verify/NativeSgxApiTest.kt b/verify-enclave/src/integration-test/kotlin/com/r3/enclaves/verify/NativeSgxApiTest.kt index c3d33fa536..70b28c4aa0 100644 --- a/verify-enclave/src/integration-test/kotlin/com/r3/enclaves/verify/NativeSgxApiTest.kt +++ b/verify-enclave/src/integration-test/kotlin/com/r3/enclaves/verify/NativeSgxApiTest.kt @@ -14,9 +14,9 @@ import net.corda.finance.POUNDS import net.corda.finance.`issued by` import net.corda.finance.contracts.asset.Cash import net.corda.node.services.api.IdentityServiceInternal -import net.corda.testing.DUMMY_NOTARY_NAME -import net.corda.testing.TestIdentity -import net.corda.testing.getTestPartyAndCertificate +import net.corda.testing.core.DUMMY_NOTARY_NAME +import net.corda.testing.core.TestIdentity +import net.corda.testing.core.getTestPartyAndCertificate import net.corda.testing.internal.rigorousMock import net.corda.testing.node.MockServices import net.corda.testing.node.ledger diff --git a/verify-enclave/src/test/kotlin/com/r3/enclaves/txverify/EnclaveletTest.kt b/verify-enclave/src/test/kotlin/com/r3/enclaves/txverify/EnclaveletTest.kt index 4a633f0841..4b3062f1d6 100644 --- a/verify-enclave/src/test/kotlin/com/r3/enclaves/txverify/EnclaveletTest.kt +++ b/verify-enclave/src/test/kotlin/com/r3/enclaves/txverify/EnclaveletTest.kt @@ -11,7 +11,7 @@ import net.corda.finance.POUNDS import net.corda.finance.`issued by` import net.corda.finance.contracts.asset.Cash import net.corda.node.services.api.IdentityServiceInternal -import net.corda.testing.* +import net.corda.testing.core.* import net.corda.testing.internal.rigorousMock import net.corda.testing.node.MockServices import net.corda.testing.node.ledger