From cfc90a221b700c83849df9aaaa598c1af2b99666 Mon Sep 17 00:00:00 2001 From: Andras Slemmer Date: Fri, 9 Feb 2018 18:23:42 +0000 Subject: [PATCH 01/10] CORDA-1003: Fix duplicate detection on cache evict --- .../node/utilities/AppendOnlyPersistentMap.kt | 1 + .../persistence/DBTransactionStorageTests.kt | 17 +++++++++++++++-- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/node/src/main/kotlin/net/corda/node/utilities/AppendOnlyPersistentMap.kt b/node/src/main/kotlin/net/corda/node/utilities/AppendOnlyPersistentMap.kt index 4912871b46..c6b875dbd9 100644 --- a/node/src/main/kotlin/net/corda/node/utilities/AppendOnlyPersistentMap.kt +++ b/node/src/main/kotlin/net/corda/node/utilities/AppendOnlyPersistentMap.kt @@ -57,6 +57,7 @@ abstract class AppendOnlyPersistentMapBase( // Depending on 'store' method, this may insert without checking key duplication or it may avoid inserting a duplicated key. val existingInDb = store(key, value) if (existingInDb != null) { // Always reuse an existing value from the storage of a duplicated key. + isUnique = false Optional.of(existingInDb) } else { Optional.of(value) 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 5247788410..4c2ffc2843 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 @@ -172,9 +172,22 @@ class DBTransactionStorageTests { assertEquals(expected, actual) } - private fun newTransactionStorage() { + @Test + fun `duplicates are detected when transaction is evicted from cache`() { + newTransactionStorage(cacheSizeBytesOverride = 0) + val transaction = newTransaction() database.transaction { - transactionStorage = DBTransactionStorage(NodeConfiguration.defaultTransactionCacheSize) + val firstInserted = transactionStorage.addTransaction(transaction) + val secondInserted = transactionStorage.addTransaction(transaction) + require(firstInserted) { "We inserted a fresh transaction" } + require(!secondInserted) { "Second time should be redundant" } + println("$firstInserted $secondInserted") + } + } + + private fun newTransactionStorage(cacheSizeBytesOverride: Long? = null) { + database.transaction { + transactionStorage = DBTransactionStorage(cacheSizeBytesOverride ?: NodeConfiguration.defaultTransactionCacheSize) } } From 3c4212a3d6d79d38afddd218ffc65dfbb8cbd5d5 Mon Sep 17 00:00:00 2001 From: Katelyn Baker Date: Mon, 12 Feb 2018 10:07:25 +0000 Subject: [PATCH 02/10] CORDA-992 - Make the finger printer pluggable for serialization factory (#2479) Facilitates easier testing --- docs/source/changelog.rst | 2 + .../serialization/amqp/ArraySerializer.kt | 3 +- .../amqp/CollectionSerializer.kt | 4 +- .../serialization/amqp/CustomSerializer.kt | 4 +- .../amqp/EnumEvolutionSerializer.kt | 3 +- .../serialization/amqp/EnumSerializer.kt | 3 +- .../serialization/amqp/FingerPrinter.kt | 202 ++++++++++++++++++ .../serialization/amqp/MapSerializer.kt | 3 +- .../serialization/amqp/ObjectSerializer.kt | 3 +- .../internal/serialization/amqp/Schema.kt | 173 +-------------- .../serialization/amqp/SerializerFactory.kt | 8 +- .../serialization/amqp/SingletonSerializer.kt | 3 +- .../serialization/amqp/ErrorMessageTests.java | 4 +- .../serialization/amqp/JavaGenericsTest.java | 12 +- .../amqp/JavaPrivatePropertyTests.java | 28 +-- .../amqp/JavaSerialiseEnumTests.java | 4 +- .../amqp/JavaSerializationOutputTests.java | 7 +- .../amqp/ListsSerializationJavaTest.java | 9 +- .../amqp/SetterConstructorTests.java | 20 +- .../amqp/FingerPrinterTesting.kt | 55 +++++ 20 files changed, 347 insertions(+), 203 deletions(-) create mode 100644 node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/FingerPrinter.kt create mode 100644 node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/FingerPrinterTesting.kt diff --git a/docs/source/changelog.rst b/docs/source/changelog.rst index bccaf5c085..23a00b1ada 100644 --- a/docs/source/changelog.rst +++ b/docs/source/changelog.rst @@ -6,6 +6,8 @@ from the previous milestone release. UNRELEASED ---------- +* Make the serialisation finger-printer a pluggable entity rather than hard wiring into the factory + * Removed blacklisted word checks in Corda X.500 name to allow "Server" or "Node" to be use as part of the legal name. * Separated our pre-existing Artemis broker into an RPC broker and a P2P broker. diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/ArraySerializer.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/ArraySerializer.kt index b6b20a204e..872709a119 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/ArraySerializer.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/ArraySerializer.kt @@ -31,7 +31,8 @@ open class ArraySerializer(override val type: Type, factory: SerializerFactory) "${type.componentType().typeName}$arrayType" } - override val typeDescriptor by lazy { Symbol.valueOf("$DESCRIPTOR_DOMAIN:${fingerprintForType(type, factory)}") } + override val typeDescriptor by lazy { + Symbol.valueOf("$DESCRIPTOR_DOMAIN:${factory.fingerPrinter.fingerprint(type)}") } internal val elementType: Type by lazy { type.componentType() } internal open val typeName by lazy { calcTypeName(type) } diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/CollectionSerializer.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/CollectionSerializer.kt index bd19e98599..5a6ab42f8e 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/CollectionSerializer.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/CollectionSerializer.kt @@ -17,7 +17,9 @@ import kotlin.collections.Set */ class CollectionSerializer(val declaredType: ParameterizedType, factory: SerializerFactory) : AMQPSerializer { override val type: Type = declaredType as? DeserializedParameterizedType ?: DeserializedParameterizedType.make(SerializerFactory.nameForType(declaredType)) - override val typeDescriptor = Symbol.valueOf("$DESCRIPTOR_DOMAIN:${fingerprintForType(type, factory)}") + override val typeDescriptor by lazy { + Symbol.valueOf("$DESCRIPTOR_DOMAIN:${factory.fingerPrinter.fingerprint(type)}") + } companion object { // NB: Order matters in this map, the most specific classes should be listed at the end 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 f2ee28d01b..fd02997c49 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 @@ -60,7 +60,9 @@ 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))}") + override val typeDescriptor by lazy { + Symbol.valueOf("$DESCRIPTOR_DOMAIN:${SerializerFingerPrinter().fingerprintForDescriptors(superClassSerializer.typeDescriptor.toString(), nameForType(clazz))}") + } private val typeNotation: TypeNotation = RestrictedType( SerializerFactory.nameForType(clazz), null, diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/EnumEvolutionSerializer.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/EnumEvolutionSerializer.kt index a9bc0916b1..a6d9ebb055 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/EnumEvolutionSerializer.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/EnumEvolutionSerializer.kt @@ -39,7 +39,8 @@ class EnumEvolutionSerializer( factory: SerializerFactory, private val conversions: Map, private val ordinals: Map) : AMQPSerializer { - override val typeDescriptor = Symbol.valueOf("$DESCRIPTOR_DOMAIN:${fingerprintForType(type, factory)}")!! + override val typeDescriptor = Symbol.valueOf( + "$DESCRIPTOR_DOMAIN:${factory.fingerPrinter.fingerprint(type)}")!! companion object { private fun MutableMap.mapInPlace(f: (String) -> String) { diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/EnumSerializer.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/EnumSerializer.kt index 5678f094ed..c3d9fdd732 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/EnumSerializer.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/EnumSerializer.kt @@ -11,8 +11,9 @@ import java.lang.reflect.Type */ class EnumSerializer(declaredType: Type, declaredClass: Class<*>, factory: SerializerFactory) : AMQPSerializer { override val type: Type = declaredType - override val typeDescriptor = Symbol.valueOf("$DESCRIPTOR_DOMAIN:${fingerprintForType(type, factory)}")!! private val typeNotation: TypeNotation + override val typeDescriptor = Symbol.valueOf( + "$DESCRIPTOR_DOMAIN:${factory.fingerPrinter.fingerprint(type)}")!! init { typeNotation = RestrictedType( diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/FingerPrinter.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/FingerPrinter.kt new file mode 100644 index 0000000000..e5cc478acb --- /dev/null +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/FingerPrinter.kt @@ -0,0 +1,202 @@ +package net.corda.nodeapi.internal.serialization.amqp + +import com.google.common.hash.Hasher +import com.google.common.hash.Hashing +import net.corda.core.utilities.loggerFor +import net.corda.core.utilities.toBase64 +import java.io.NotSerializableException +import java.lang.reflect.* +import java.util.* + +/** + * Should be implemented by classes which wish to provide plugable fingerprinting og types for a [SerializerFactory] + */ +interface FingerPrinter { + /** + * Return a unique identifier for a type, usually this will take into account the constituent elements + * of said type such that any modification to any sub element wll generate a different fingerprint + */ + fun fingerprint(type: Type): String + + /** + * If required, associate an instance of the fingerprinter with a specific serializer factory + */ + fun setOwner(factory: SerializerFactory) +} + +/** + * Implementation of the finger printing mechanism used by default + */ +class SerializerFingerPrinter : FingerPrinter { + private var factory: SerializerFactory? = null + + private val ARRAY_HASH: String = "Array = true" + private val ENUM_HASH: String = "Enum = true" + private val ALREADY_SEEN_HASH: String = "Already seen = true" + private val NULLABLE_HASH: String = "Nullable = true" + private val NOT_NULLABLE_HASH: String = "Nullable = false" + private val ANY_TYPE_HASH: String = "Any type = true" + private val TYPE_VARIABLE_HASH: String = "Type variable = true" + private val WILDCARD_TYPE_HASH: String = "Wild card = true" + + private val logger by lazy { loggerFor() } + + override fun setOwner(factory: SerializerFactory) { + this.factory = factory + } + + /** + * The method generates a fingerprint for a given JVM [Type] that should be unique to the schema representation. + * Thus it only takes into account properties and types and only supports the same object graph subset as the overall + * serialization code. + * + * The idea being that even for two classes that share the same name but differ in a minor way, the fingerprint will be + * different. + */ + override fun fingerprint(type: Type): String { + return fingerprintForType( + type, null, HashSet(), Hashing.murmur3_128().newHasher(), debugIndent = 1).hash().asBytes().toBase64() + } + + private fun isCollectionOrMap(type: Class<*>) = + (Collection::class.java.isAssignableFrom(type) || Map::class.java.isAssignableFrom(type)) + && !EnumSet::class.java.isAssignableFrom(type) + + internal fun fingerprintForDescriptors(vararg typeDescriptors: String): String { + val hasher = Hashing.murmur3_128().newHasher() + for (typeDescriptor in typeDescriptors) { + hasher.putUnencodedChars(typeDescriptor) + } + return hasher.hash().asBytes().toBase64() + } + + private fun Hasher.fingerprintWithCustomSerializerOrElse( + factory: SerializerFactory, + clazz: Class<*>, + declaredType: Type, + block: () -> Hasher): Hasher { + // Need to check if a custom serializer is applicable + val customSerializer = factory.findCustomSerializer(clazz, declaredType) + return if (customSerializer != null) { + putUnencodedChars(customSerializer.typeDescriptor) + } else { + block() + } + } + + // This method concatenates various elements of the types recursively as unencoded strings into the hasher, + // effectively creating a unique string for a type which we then hash in the calling function above. + private fun fingerprintForType(type: Type, contextType: Type?, alreadySeen: MutableSet, + hasher: Hasher, debugIndent: Int = 1): Hasher { + // We don't include Example and Example where type is ? or T in this otherwise we + // generate different fingerprints for class Outer(val a: Inner) when serialising + // and deserializing (assuming deserialization is occurring in a factory that didn't + // serialise the object in the first place (and thus the cache lookup fails). This is also + // true of Any, where we need Example and Example to have the same fingerprint + return if ((type in alreadySeen) + && (type !is SerializerFactory.AnyType) + && (type !is TypeVariable<*>) + && (type !is WildcardType)) { + hasher.putUnencodedChars(ALREADY_SEEN_HASH) + } else { + alreadySeen += type + try { + when (type) { + is ParameterizedType -> { + // Hash the rawType + params + val clazz = type.rawType as Class<*> + + val startingHash = if (isCollectionOrMap(clazz)) { + hasher.putUnencodedChars(clazz.name) + } else { + hasher.fingerprintWithCustomSerializerOrElse(factory!!, clazz, type) { + fingerprintForObject(type, type, alreadySeen, hasher, factory!!, debugIndent + 1) + } + } + + // ... and concatenate the type data for each parameter type. + type.actualTypeArguments.fold(startingHash) { orig, paramType -> + fingerprintForType(paramType, type, alreadySeen, orig, debugIndent + 1) + } + } + // Previously, we drew a distinction between TypeVariable, WildcardType, and AnyType, changing + // the signature of the fingerprinted object. This, however, doesn't work as it breaks bi- + // directional fingerprints. That is, fingerprinting a concrete instance of a generic + // type (Example), creates a different fingerprint from the generic type itself (Example) + // + // On serialization Example is treated as Example, a TypeVariable + // On deserialisation it is seen as Example, A WildcardType *and* a TypeVariable + // Note: AnyType is a special case of WildcardType used in other parts of the + // serializer so both cases need to be dealt with here + // + // If we treat these types as fundamentally different and alter the fingerprint we will + // end up breaking into the evolver when we shouldn't or, worse, evoking the carpenter. + is SerializerFactory.AnyType, + is WildcardType, + is TypeVariable<*> -> { + hasher.putUnencodedChars("?").putUnencodedChars(ANY_TYPE_HASH) + } + is Class<*> -> { + if (type.isArray) { + fingerprintForType(type.componentType, contextType, alreadySeen, hasher, debugIndent + 1) + .putUnencodedChars(ARRAY_HASH) + } else if (SerializerFactory.isPrimitive(type)) { + hasher.putUnencodedChars(type.name) + } else if (isCollectionOrMap(type)) { + hasher.putUnencodedChars(type.name) + } else if (type.isEnum) { + // ensures any change to the enum (adding constants) will trigger the need for evolution + hasher.apply { + type.enumConstants.forEach { + putUnencodedChars(it.toString()) + } + }.putUnencodedChars(type.name).putUnencodedChars(ENUM_HASH) + } else { + hasher.fingerprintWithCustomSerializerOrElse(factory!!, type, type) { + if (type.kotlin.objectInstance != null) { + // TODO: name collision is too likely for kotlin objects, we need to introduce some reference + // to the CorDapp but maybe reference to the JAR in the short term. + hasher.putUnencodedChars(type.name) + } else { + fingerprintForObject(type, type, alreadySeen, hasher, factory!!, debugIndent + 1) + } + } + } + } + // Hash the element type + some array hash + is GenericArrayType -> { + fingerprintForType(type.genericComponentType, contextType, alreadySeen, + hasher, debugIndent + 1).putUnencodedChars(ARRAY_HASH) + } + else -> throw NotSerializableException("Don't know how to hash") + } + } catch (e: NotSerializableException) { + val msg = "${e.message} -> $type" + logger.error(msg, e) + throw NotSerializableException(msg) + } + } + } + + private fun fingerprintForObject( + type: Type, + contextType: Type?, + alreadySeen: MutableSet, + hasher: Hasher, + factory: SerializerFactory, + debugIndent: 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) + .serializationOrder + .fold(hasher.putUnencodedChars(name)) { orig, prop -> + fingerprintForType(prop.getter.resolvedType, type, alreadySeen, orig, debugIndent + 1) + .putUnencodedChars(prop.getter.name) + .putUnencodedChars(if (prop.getter.mandatory) NOT_NULLABLE_HASH else NULLABLE_HASH) + } + interfacesForSerialization(type, factory).map { fingerprintForType(it, type, alreadySeen, hasher, debugIndent + 1) } + return hasher + } +} \ No newline at end of file diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/MapSerializer.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/MapSerializer.kt index 5472074d62..138326dde6 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/MapSerializer.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/MapSerializer.kt @@ -19,7 +19,8 @@ private typealias MapCreationFunction = (Map<*, *>) -> Map<*, *> */ class MapSerializer(private val declaredType: ParameterizedType, factory: SerializerFactory) : AMQPSerializer { override val type: Type = declaredType as? DeserializedParameterizedType ?: DeserializedParameterizedType.make(SerializerFactory.nameForType(declaredType)) - override val typeDescriptor: Symbol = Symbol.valueOf("$DESCRIPTOR_DOMAIN:${fingerprintForType(type, factory)}") + override val typeDescriptor: Symbol = Symbol.valueOf( + "$DESCRIPTOR_DOMAIN:${factory.fingerPrinter.fingerprint(type)}") companion object { // NB: Order matters in this map, the most specific classes should be listed at the end 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 fa723fd3de..e4dddec1da 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 @@ -31,7 +31,8 @@ open class ObjectSerializer(val clazz: Type, factory: SerializerFactory) : AMQPS private val typeName = nameForType(clazz) - override val typeDescriptor = Symbol.valueOf("$DESCRIPTOR_DOMAIN:${fingerprintForType(type, factory)}") + override val typeDescriptor = Symbol.valueOf( + "$DESCRIPTOR_DOMAIN:${factory.fingerPrinter.fingerprint(type)}") // We restrict to only those annotated or whitelisted private val interfaces = interfacesForSerialization(clazz, factory) 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 f5edd923c9..285a1f5d54 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 @@ -1,19 +1,13 @@ package net.corda.nodeapi.internal.serialization.amqp -import com.google.common.hash.Hasher -import com.google.common.hash.Hashing import net.corda.core.internal.uncheckedCast import net.corda.nodeapi.internal.serialization.CordaSerializationMagic -import net.corda.core.utilities.loggerFor -import net.corda.core.utilities.toBase64 import org.apache.qpid.proton.amqp.DescribedType import org.apache.qpid.proton.amqp.Symbol import org.apache.qpid.proton.amqp.UnsignedInteger import org.apache.qpid.proton.amqp.UnsignedLong import org.apache.qpid.proton.codec.DescribedTypeConstructor import java.io.NotSerializableException -import java.lang.reflect.* -import java.util.* import net.corda.nodeapi.internal.serialization.carpenter.Field as CarpenterField import net.corda.nodeapi.internal.serialization.carpenter.Schema as CarpenterSchema @@ -92,7 +86,14 @@ data class Descriptor(val name: Symbol?, val code: UnsignedLong? = null) : Descr } } -data class Field(val name: String, val type: String, val requires: List, val default: String?, val label: String?, val mandatory: Boolean, val multiple: Boolean) : DescribedType { +data class Field( + val name: String, + val type: String, + val requires: List, + val default: String?, + val label: String?, + val mandatory: Boolean, + val multiple: Boolean) : DescribedType { companion object : DescribedTypeConstructor { val DESCRIPTOR = AMQPDescriptorRegistry.FIELD.amqpDescriptor @@ -302,162 +303,4 @@ data class ReferencedObject(private val refCounter: Int) : DescribedType { override fun toString(): String = "" } -private val ARRAY_HASH: String = "Array = true" -private val ENUM_HASH: String = "Enum = true" -private val ALREADY_SEEN_HASH: String = "Already seen = true" -private val NULLABLE_HASH: String = "Nullable = true" -private val NOT_NULLABLE_HASH: String = "Nullable = false" -private val ANY_TYPE_HASH: String = "Any type = true" -private val TYPE_VARIABLE_HASH: String = "Type variable = true" -private val WILDCARD_TYPE_HASH: String = "Wild card = true" -private val logger by lazy { loggerFor() } - -/** - * The method generates a fingerprint for a given JVM [Type] that should be unique to the schema representation. - * Thus it only takes into account properties and types and only supports the same object graph subset as the overall - * serialization code. - * - * The idea being that even for two classes that share the same name but differ in a minor way, the fingerprint will be - * different. - */ -// TODO: write tests -internal fun fingerprintForType(type: Type, factory: SerializerFactory): String { - return fingerprintForType(type, null, HashSet(), Hashing.murmur3_128().newHasher(), factory).hash().asBytes().toBase64() -} - -internal fun fingerprintForDescriptors(vararg typeDescriptors: String): String { - val hasher = Hashing.murmur3_128().newHasher() - for (typeDescriptor in typeDescriptors) { - hasher.putUnencodedChars(typeDescriptor) - } - return hasher.hash().asBytes().toBase64() -} - -private fun Hasher.fingerprintWithCustomSerializerOrElse(factory: SerializerFactory, clazz: Class<*>, declaredType: Type, block: () -> Hasher): Hasher { - // Need to check if a custom serializer is applicable - val customSerializer = factory.findCustomSerializer(clazz, declaredType) - return if (customSerializer != null) { - putUnencodedChars(customSerializer.typeDescriptor) - } else { - block() - } -} - -// This method concatenates various elements of the types recursively as unencoded strings into the hasher, effectively -// creating a unique string for a type which we then hash in the calling function above. -private fun fingerprintForType(type: Type, contextType: Type?, alreadySeen: MutableSet, - hasher: Hasher, factory: SerializerFactory, debugIndent: Int = 1): Hasher { - // We don't include Example and Example where type is ? or T in this otherwise we - // generate different fingerprints for class Outer(val a: Inner) when serialising - // and deserializing (assuming deserialization is occurring in a factory that didn't - // serialise the object in the first place (and thus the cache lookup fails). This is also - // true of Any, where we need Example and Example to have the same fingerprint - return if ((type in alreadySeen) - && (type !is SerializerFactory.AnyType) - && (type !is TypeVariable<*>) - && (type !is WildcardType)) { - hasher.putUnencodedChars(ALREADY_SEEN_HASH) - } else { - alreadySeen += type - try { - when (type) { - is ParameterizedType -> { - // Hash the rawType + params - val clazz = type.rawType as Class<*> - - val startingHash = if (isCollectionOrMap(clazz)) { - hasher.putUnencodedChars(clazz.name) - } else { - hasher.fingerprintWithCustomSerializerOrElse(factory, clazz, type) { - fingerprintForObject(type, type, alreadySeen, hasher, factory, debugIndent+1) - } - } - - // ... and concatenate the type data for each parameter type. - type.actualTypeArguments.fold(startingHash) { orig, paramType -> - fingerprintForType(paramType, type, alreadySeen, orig, factory, debugIndent+1) - } - } - // Previously, we drew a distinction between TypeVariable, WildcardType, and AnyType, changing - // the signature of the fingerprinted object. This, however, doesn't work as it breaks bi- - // directional fingerprints. That is, fingerprinting a concrete instance of a generic - // type (Example), creates a different fingerprint from the generic type itself (Example) - // - // On serialization Example is treated as Example, a TypeVariable - // On deserialisation it is seen as Example, A WildcardType *and* a TypeVariable - // Note: AnyType is a special case of WildcardType used in other parts of the - // serializer so both cases need to be dealt with here - // - // If we treat these types as fundamentally different and alter the fingerprint we will - // end up breaking into the evolver when we shouldn't or, worse, evoking the carpenter. - is SerializerFactory.AnyType, - is WildcardType, - is TypeVariable<*> -> { - hasher.putUnencodedChars("?").putUnencodedChars(ANY_TYPE_HASH) - } - is Class<*> -> { - if (type.isArray) { - fingerprintForType(type.componentType, contextType, alreadySeen, hasher, factory, debugIndent+1) - .putUnencodedChars(ARRAY_HASH) - } else if (SerializerFactory.isPrimitive(type)) { - hasher.putUnencodedChars(type.name) - } else if (isCollectionOrMap(type)) { - hasher.putUnencodedChars(type.name) - } else if (type.isEnum) { - // ensures any change to the enum (adding constants) will trigger the need for evolution - hasher.apply { - type.enumConstants.forEach { - putUnencodedChars(it.toString()) - } - }.putUnencodedChars(type.name).putUnencodedChars(ENUM_HASH) - } else { - hasher.fingerprintWithCustomSerializerOrElse(factory, type, type) { - if (type.kotlin.objectInstance != null) { - // TODO: name collision is too likely for kotlin objects, we need to introduce some reference - // to the CorDapp but maybe reference to the JAR in the short term. - hasher.putUnencodedChars(type.name) - } else { - fingerprintForObject(type, type, alreadySeen, hasher, factory, debugIndent+1) - } - } - } - } - // Hash the element type + some array hash - is GenericArrayType -> { - fingerprintForType(type.genericComponentType, contextType, alreadySeen, - hasher, factory, debugIndent+1).putUnencodedChars(ARRAY_HASH) - } - else -> throw NotSerializableException("Don't know how to hash") - } - } catch (e: NotSerializableException) { - val msg = "${e.message} -> $type" - logger.error(msg, e) - throw NotSerializableException(msg) - } - } -} - -private fun isCollectionOrMap(type: Class<*>) = (Collection::class.java.isAssignableFrom(type) || Map::class.java.isAssignableFrom(type)) && - !EnumSet::class.java.isAssignableFrom(type) - -private fun fingerprintForObject( - type: Type, - contextType: Type?, - alreadySeen: MutableSet, - hasher: Hasher, - factory: SerializerFactory, - debugIndent: 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) - .serializationOrder - .fold(hasher.putUnencodedChars(name)) { orig, prop -> - fingerprintForType(prop.getter.resolvedType, type, alreadySeen, orig, factory, debugIndent+1) - .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, debugIndent+1) } - return hasher -} diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializerFactory.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializerFactory.kt index 7bd8178a6d..011b04dbf7 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializerFactory.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializerFactory.kt @@ -40,7 +40,13 @@ data class FactorySchemaAndDescriptor(val schemas: SerializationSchemas, val typ open class SerializerFactory( val whitelist: ClassWhitelist, cl: ClassLoader, - private val evolutionSerializerGetter: EvolutionSerializerGetterBase = EvolutionSerializerGetter()) { + private val evolutionSerializerGetter: EvolutionSerializerGetterBase = EvolutionSerializerGetter(), + val fingerPrinter: FingerPrinter = SerializerFingerPrinter()) { + + init { + fingerPrinter.setOwner(this) + } + private val serializersByType = ConcurrentHashMap>() private val serializersByDescriptor = ConcurrentHashMap>() private val customSerializers = CopyOnWriteArrayList() diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/SingletonSerializer.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/SingletonSerializer.kt index 0c70a18935..b54bb94393 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/SingletonSerializer.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/SingletonSerializer.kt @@ -10,7 +10,8 @@ import java.lang.reflect.Type * want converting back to that singleton instance on the receiving JVM. */ class SingletonSerializer(override val type: Class<*>, val singleton: Any, factory: SerializerFactory) : AMQPSerializer { - override val typeDescriptor = Symbol.valueOf("$DESCRIPTOR_DOMAIN:${fingerprintForType(type, factory)}") + override val typeDescriptor = Symbol.valueOf( + "$DESCRIPTOR_DOMAIN:${factory.fingerPrinter.fingerprint(type)}") private val interfaces = interfacesForSerialization(type, factory) diff --git a/node-api/src/test/java/net/corda/nodeapi/internal/serialization/amqp/ErrorMessageTests.java b/node-api/src/test/java/net/corda/nodeapi/internal/serialization/amqp/ErrorMessageTests.java index 7cd21c4f21..11db9fc643 100644 --- a/node-api/src/test/java/net/corda/nodeapi/internal/serialization/amqp/ErrorMessageTests.java +++ b/node-api/src/test/java/net/corda/nodeapi/internal/serialization/amqp/ErrorMessageTests.java @@ -32,10 +32,12 @@ public class ErrorMessageTests { @Test public void testJavaConstructorAnnotations() { EvolutionSerializerGetterBase evolutionSerialiserGetter = new EvolutionSerializerGetter(); + FingerPrinter fingerPrinter = new SerializerFingerPrinter(); SerializerFactory factory1 = new SerializerFactory( AllWhitelist.INSTANCE, ClassLoader.getSystemClassLoader(), - evolutionSerialiserGetter); + evolutionSerialiserGetter, + fingerPrinter); SerializationOutput ser = new SerializationOutput(factory1); diff --git a/node-api/src/test/java/net/corda/nodeapi/internal/serialization/amqp/JavaGenericsTest.java b/node-api/src/test/java/net/corda/nodeapi/internal/serialization/amqp/JavaGenericsTest.java index feb416e991..9c206ad8cd 100644 --- a/node-api/src/test/java/net/corda/nodeapi/internal/serialization/amqp/JavaGenericsTest.java +++ b/node-api/src/test/java/net/corda/nodeapi/internal/serialization/amqp/JavaGenericsTest.java @@ -29,7 +29,8 @@ public class JavaGenericsTest { SerializerFactory factory = new SerializerFactory( AllWhitelist.INSTANCE, ClassLoader.getSystemClassLoader(), - new EvolutionSerializerGetter()); + new EvolutionSerializerGetter(), + new SerializerFingerPrinter()); SerializationOutput ser = new SerializationOutput(factory); SerializedBytes bytes = ser.serialize(a1); @@ -44,7 +45,8 @@ public class JavaGenericsTest { SerializerFactory factory = new SerializerFactory( AllWhitelist.INSTANCE, ClassLoader.getSystemClassLoader(), - new EvolutionSerializerGetter()); + new EvolutionSerializerGetter(), + new SerializerFingerPrinter()); return (new SerializationOutput(factory)).serialize(a); } @@ -59,7 +61,8 @@ public class JavaGenericsTest { SerializerFactory factory = new SerializerFactory( AllWhitelist.INSTANCE, ClassLoader.getSystemClassLoader(), - new EvolutionSerializerGetter()); + new EvolutionSerializerGetter(), + new SerializerFingerPrinter()); DeserializationInput des = new DeserializationInput(factory); return des.deserialize(bytes, A.class); @@ -83,7 +86,8 @@ public class JavaGenericsTest { SerializerFactory factory = new SerializerFactory( AllWhitelist.INSTANCE, ClassLoader.getSystemClassLoader(), - new EvolutionSerializerGetter()); + new EvolutionSerializerGetter(), + new SerializerFingerPrinter()); SerializedBytes bytes = forceWildcardSerializeFactory(new A(new Inner(29)), factory); Inner i = (Inner)forceWildcardDeserializeFactory(bytes, factory).getT(); 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 b13cc2cc48..2ee89945f6 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 @@ -76,9 +76,9 @@ public class JavaPrivatePropertyTests { @Test public void singlePrivateBooleanWithConstructor() throws NotSerializableException, NoSuchFieldException, IllegalAccessException { - EvolutionSerializerGetterBase evolutionSerializerGetter = new EvolutionSerializerGetter(); SerializerFactory factory = new SerializerFactory(AllWhitelist.INSTANCE, ClassLoader.getSystemClassLoader(), - evolutionSerializerGetter); + new EvolutionSerializerGetter(), + new SerializerFingerPrinter()); SerializationOutput ser = new SerializationOutput(factory); DeserializationInput des = new DeserializationInput(factory); @@ -89,9 +89,10 @@ public class JavaPrivatePropertyTests { @Test public void singlePrivateBooleanWithNoConstructor() throws NotSerializableException, NoSuchFieldException, IllegalAccessException { - EvolutionSerializerGetterBase evolutionSerializerGetter = new EvolutionSerializerGetter(); SerializerFactory factory = new SerializerFactory(AllWhitelist.INSTANCE, ClassLoader.getSystemClassLoader(), - evolutionSerializerGetter); + new EvolutionSerializerGetter(), + new SerializerFingerPrinter()); + SerializationOutput ser = new SerializationOutput(factory); DeserializationInput des = new DeserializationInput(factory); @@ -103,9 +104,9 @@ public class JavaPrivatePropertyTests { @Test public void testCapitilsationOfIs() throws NotSerializableException, NoSuchFieldException, IllegalAccessException { - EvolutionSerializerGetterBase evolutionSerializerGetter = new EvolutionSerializerGetter(); SerializerFactory factory = new SerializerFactory(AllWhitelist.INSTANCE, ClassLoader.getSystemClassLoader(), - evolutionSerializerGetter); + new EvolutionSerializerGetter(), + new SerializerFingerPrinter()); SerializationOutput ser = new SerializationOutput(factory); DeserializationInput des = new DeserializationInput(factory); @@ -119,9 +120,9 @@ public class JavaPrivatePropertyTests { @Test public void singlePrivateIntWithBoolean() throws NotSerializableException, NoSuchFieldException, IllegalAccessException { - EvolutionSerializerGetterBase evolutionSerializerGetter = new EvolutionSerializerGetter(); SerializerFactory factory = new SerializerFactory(AllWhitelist.INSTANCE, ClassLoader.getSystemClassLoader(), - evolutionSerializerGetter); + new EvolutionSerializerGetter(), + new SerializerFingerPrinter()); SerializationOutput ser = new SerializationOutput(factory); DeserializationInput des = new DeserializationInput(factory); @@ -134,9 +135,10 @@ public class JavaPrivatePropertyTests { @Test public void singlePrivateWithConstructor() throws NotSerializableException, NoSuchFieldException, IllegalAccessException { - EvolutionSerializerGetterBase evolutionSerializerGetter = new EvolutionSerializerGetter(); SerializerFactory factory = new SerializerFactory(AllWhitelist.INSTANCE, ClassLoader.getSystemClassLoader(), - evolutionSerializerGetter); + new EvolutionSerializerGetter(), + new SerializerFingerPrinter()); + SerializationOutput ser = new SerializationOutput(factory); DeserializationInput des = new DeserializationInput(factory); @@ -164,9 +166,11 @@ public class JavaPrivatePropertyTests { @Test public void singlePrivateWithConstructorAndGetter() throws NotSerializableException, NoSuchFieldException, IllegalAccessException { - EvolutionSerializerGetterBase evolutionSerializerGetter = new EvolutionSerializerGetter(); SerializerFactory factory = new SerializerFactory(AllWhitelist.INSTANCE, - ClassLoader.getSystemClassLoader(), evolutionSerializerGetter); + ClassLoader.getSystemClassLoader(), + new EvolutionSerializerGetter(), + new SerializerFingerPrinter()); + SerializationOutput ser = new SerializationOutput(factory); DeserializationInput des = new DeserializationInput(factory); diff --git a/node-api/src/test/java/net/corda/nodeapi/internal/serialization/amqp/JavaSerialiseEnumTests.java b/node-api/src/test/java/net/corda/nodeapi/internal/serialization/amqp/JavaSerialiseEnumTests.java index a64f9c3d9e..9be95ca396 100644 --- a/node-api/src/test/java/net/corda/nodeapi/internal/serialization/amqp/JavaSerialiseEnumTests.java +++ b/node-api/src/test/java/net/corda/nodeapi/internal/serialization/amqp/JavaSerialiseEnumTests.java @@ -29,9 +29,9 @@ public class JavaSerialiseEnumTests { public void testJavaConstructorAnnotations() throws NotSerializableException { Bra bra = new Bra(Bras.UNDERWIRE); - EvolutionSerializerGetterBase evolutionSerialiserGetter = new EvolutionSerializerGetter(); SerializerFactory factory1 = new SerializerFactory(AllWhitelist.INSTANCE, ClassLoader.getSystemClassLoader(), - evolutionSerialiserGetter); + new EvolutionSerializerGetter(), + new SerializerFingerPrinter()); SerializationOutput ser = new SerializationOutput(factory1); SerializedBytes bytes = ser.serialize(bra); } diff --git a/node-api/src/test/java/net/corda/nodeapi/internal/serialization/amqp/JavaSerializationOutputTests.java b/node-api/src/test/java/net/corda/nodeapi/internal/serialization/amqp/JavaSerializationOutputTests.java index 4379718003..0441b2720f 100644 --- a/node-api/src/test/java/net/corda/nodeapi/internal/serialization/amqp/JavaSerializationOutputTests.java +++ b/node-api/src/test/java/net/corda/nodeapi/internal/serialization/amqp/JavaSerializationOutputTests.java @@ -173,10 +173,13 @@ public class JavaSerializationOutputTests { private Object serdes(Object obj) throws NotSerializableException { EvolutionSerializerGetterBase evolutionSerialiserGetter = new EvolutionSerializerGetter(); + FingerPrinter fingerPrinter = new SerializerFingerPrinter(); SerializerFactory factory1 = new SerializerFactory(AllWhitelist.INSTANCE, ClassLoader.getSystemClassLoader(), - evolutionSerialiserGetter); + evolutionSerialiserGetter, + fingerPrinter); SerializerFactory factory2 = new SerializerFactory(AllWhitelist.INSTANCE, ClassLoader.getSystemClassLoader(), - evolutionSerialiserGetter); + evolutionSerialiserGetter, + fingerPrinter); SerializationOutput ser = new SerializationOutput(factory1); SerializedBytes bytes = ser.serialize(obj); diff --git a/node-api/src/test/java/net/corda/nodeapi/internal/serialization/amqp/ListsSerializationJavaTest.java b/node-api/src/test/java/net/corda/nodeapi/internal/serialization/amqp/ListsSerializationJavaTest.java index c77f452b33..153681fa6a 100644 --- a/node-api/src/test/java/net/corda/nodeapi/internal/serialization/amqp/ListsSerializationJavaTest.java +++ b/node-api/src/test/java/net/corda/nodeapi/internal/serialization/amqp/ListsSerializationJavaTest.java @@ -125,9 +125,12 @@ public class ListsSerializationJavaTest { // Have to have own version as Kotlin inline functions cannot be easily called from Java private static void assertEqualAfterRoundTripSerialization(T container, Class clazz) throws Exception { - EvolutionSerializerGetterBase evolutionSerialiserGetter = new EvolutionSerializerGetter(); - SerializerFactory factory1 = new SerializerFactory(AllWhitelist.INSTANCE, ClassLoader.getSystemClassLoader(), - evolutionSerialiserGetter); + EvolutionSerializerGetterBase evolutionSerializerGetter = new EvolutionSerializerGetter(); + FingerPrinter fingerPrinter = new SerializerFingerPrinter(); + SerializerFactory factory1 = new SerializerFactory( + AllWhitelist.INSTANCE, ClassLoader.getSystemClassLoader(), + evolutionSerializerGetter, + fingerPrinter); SerializationOutput ser = new SerializationOutput(factory1); SerializedBytes bytes = ser.serialize(container); DeserializationInput des = new DeserializationInput(factory1); diff --git a/node-api/src/test/java/net/corda/nodeapi/internal/serialization/amqp/SetterConstructorTests.java b/node-api/src/test/java/net/corda/nodeapi/internal/serialization/amqp/SetterConstructorTests.java index defd86d948..a0ab7276dd 100644 --- a/node-api/src/test/java/net/corda/nodeapi/internal/serialization/amqp/SetterConstructorTests.java +++ b/node-api/src/test/java/net/corda/nodeapi/internal/serialization/amqp/SetterConstructorTests.java @@ -110,10 +110,12 @@ public class SetterConstructorTests { @Test public void serialiseC() throws NotSerializableException { EvolutionSerializerGetterBase evolutionSerialiserGetter = new EvolutionSerializerGetter(); + FingerPrinter fingerPrinter = new SerializerFingerPrinter(); SerializerFactory factory1 = new SerializerFactory( AllWhitelist.INSTANCE, ClassLoader.getSystemClassLoader(), - evolutionSerialiserGetter); + evolutionSerialiserGetter, + fingerPrinter); SerializationOutput ser = new SerializationOutput(factory1); @@ -184,10 +186,12 @@ public class SetterConstructorTests { @Test public void deserialiseC() throws NotSerializableException { EvolutionSerializerGetterBase evolutionSerialiserGetter = new EvolutionSerializerGetter(); + FingerPrinter fingerPrinter = new SerializerFingerPrinter(); SerializerFactory factory1 = new SerializerFactory( AllWhitelist.INSTANCE, ClassLoader.getSystemClassLoader(), - evolutionSerialiserGetter); + evolutionSerialiserGetter, + fingerPrinter); C cPre1 = new C(); @@ -251,10 +255,12 @@ public class SetterConstructorTests { @Test public void serialiseOuterAndInner() throws NotSerializableException { EvolutionSerializerGetterBase evolutionSerialiserGetter = new EvolutionSerializerGetter(); + FingerPrinter fingerPrinter = new SerializerFingerPrinter(); SerializerFactory factory1 = new SerializerFactory( AllWhitelist.INSTANCE, ClassLoader.getSystemClassLoader(), - evolutionSerialiserGetter); + evolutionSerialiserGetter, + fingerPrinter); Inner1 i1 = new Inner1("Hello"); Inner2 i2 = new Inner2(); @@ -277,10 +283,12 @@ public class SetterConstructorTests { @Test public void typeMistmatch() throws NotSerializableException { EvolutionSerializerGetterBase evolutionSerialiserGetter = new EvolutionSerializerGetter(); + FingerPrinter fingerPrinter = new SerializerFingerPrinter(); SerializerFactory factory1 = new SerializerFactory( AllWhitelist.INSTANCE, ClassLoader.getSystemClassLoader(), - evolutionSerialiserGetter); + evolutionSerialiserGetter, + fingerPrinter); TypeMismatch tm = new TypeMismatch(); tm.setA(10); @@ -293,10 +301,12 @@ public class SetterConstructorTests { @Test public void typeMistmatch2() throws NotSerializableException { EvolutionSerializerGetterBase evolutionSerialiserGetter = new EvolutionSerializerGetter(); + FingerPrinter fingerPrinter = new SerializerFingerPrinter(); SerializerFactory factory1 = new SerializerFactory( AllWhitelist.INSTANCE, ClassLoader.getSystemClassLoader(), - evolutionSerialiserGetter); + evolutionSerialiserGetter, + fingerPrinter); TypeMismatch2 tm = new TypeMismatch2(); tm.setA("10"); diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/FingerPrinterTesting.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/FingerPrinterTesting.kt new file mode 100644 index 0000000000..9b5ceb24df --- /dev/null +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/FingerPrinterTesting.kt @@ -0,0 +1,55 @@ +package net.corda.nodeapi.internal.serialization.amqp + +import org.junit.Test +import java.lang.reflect.Type +import kotlin.test.assertEquals +import net.corda.nodeapi.internal.serialization.AllWhitelist + +class FingerPrinterTesting : FingerPrinter { + private var index = 0 + private val cache = mutableMapOf() + + override fun fingerprint(type: Type): String { + return cache.computeIfAbsent(type) { index++.toString() } + } + + override fun setOwner(factory: SerializerFactory) { + return + } + + @Suppress("UNUSED") + fun changeFingerprint(type: Type) { + cache.computeIfAbsent(type) { "" }.apply { index++.toString() } + } +} + +class FingerPrinterTestingTests { + companion object { + val VERBOSE = true + } + @Test + fun testingTest() { + val fpt = FingerPrinterTesting() + assertEquals ("0", fpt.fingerprint(Integer::class.java)) + assertEquals ("1", fpt.fingerprint(String::class.java)) + assertEquals ("0", fpt.fingerprint(Integer::class.java)) + assertEquals ("1", fpt.fingerprint(String::class.java)) + } + + @Test + fun worksAsReplacement() { + data class C (val a: Int, val b: Long) + + val factory = SerializerFactory( + AllWhitelist, + ClassLoader.getSystemClassLoader(), + EvolutionSerializerGetterTesting(), + FingerPrinterTesting()) + + val blob = TestSerializationOutput(VERBOSE, factory).serializeAndReturnSchema(C(1, 2L)) + + assertEquals (1, blob.schema.types.size) + assertEquals ("", blob.schema.types[0].descriptor.toString()) + } + +} \ No newline at end of file From 7b65b7971ad065ff7cab1d90ce8643b5cde44008 Mon Sep 17 00:00:00 2001 From: Anthony Keenan Date: Mon, 12 Feb 2018 10:09:59 +0000 Subject: [PATCH 03/10] CORDA-939 - Don't expose StartedNode and AbstractNode as part of public test api (#2472) * Don't expose StartedNode via Node Driver * Dont expose StartedNode/Abstract Node via MockNetwork * Remove internal var from constructor as it doesn't hide from public api and change to internal initialisation method * Update api * Rename MockNode to StartedMockNode to avoid confusion Update documentation Update api-current.txt * Fix typo * Fix test failure * Modify flow tests to use internal mock network and remove additional internal exposures from StartedMockNode * Fix api-current * Change InProcess and OutOfProcess to interfaces * Explicitly declare MockNetwork parameters Dont expose StateMachineManager Move affected tests to use internal mock network * Fix api-current * Changes requested via review * Fix IRS Demo address * Fix api * Remove internal attribute from classes in internal package * Remove accidentally added code * Move useHttps into NodeHandleInternal * Remove duplicated code * Update api-current * Make webAddress internal on NodeHandle * Make sure parameters in public api are explicitly specified * Use correct address in IRS Demo * Get webaddress from webserver handle * Update api-current --- .ci/api-current.txt | 208 +++++------------- .../corda/client/jfx/NodeMonitorModelTest.kt | 4 +- .../net/corda/core/flows/FlowsInJavaTest.java | 6 +- .../net/corda/core/flows/AttachmentTests.kt | 7 +- .../core/flows/CollectSignaturesFlowTests.kt | 8 +- .../core/flows/ContractUpgradeFlowTest.kt | 25 +-- .../net/corda/core/flows/FlowTestsUtils.kt | 6 +- .../corda/core/flows/ReceiveAllFlowTests.kt | 4 +- .../internal/ResolveTransactionsFlowTest.kt | 8 +- .../AttachmentSerializationTest.kt | 11 +- docs/source/changelog.rst | 3 + .../corda/docs/IntegrationTestingTutorial.kt | 5 +- .../net/corda/docs/ClientRpcTutorial.kt | 3 +- .../mocknetwork/TutorialMockNetwork.kt | 11 +- .../net/corda/docs/CustomVaultQueryTest.kt | 6 +- .../docs/FxTransactionBuildTutorialTest.kt | 6 +- .../corda/finance/flows/CashExitFlowTests.kt | 5 +- .../corda/finance/flows/CashIssueFlowTests.kt | 5 +- .../finance/flows/CashPaymentFlowTests.kt | 7 +- .../kotlin/net/corda/node/BootTests.kt | 5 +- .../corda/node/CordappScanningDriverTest.kt | 3 +- .../net/corda/node/NodePerformanceTests.kt | 24 +- .../node/services/BFTNotaryServiceTests.kt | 7 +- .../node/services/DistributedServiceTests.kt | 8 +- .../node/services/RaftNotaryServiceTests.kt | 18 +- .../node/services/network/NetworkMapTest.kt | 4 +- .../net/corda/node/services/rpc/RpcSslTest.kt | 5 +- .../statemachine/LargeTransactionsTest.kt | 3 +- .../services/messaging/P2PMessagingTest.kt | 56 ++--- .../test/node/NodeStatePersistenceTests.kt | 9 +- .../net/corda/node/CordaRPCOpsImplTest.kt | 8 +- .../corda/node/internal/CordaServiceTest.kt | 3 +- .../node/internal/NetworkParametersTest.kt | 2 +- .../node/messaging/TwoPartyTradeFlowTests.kt | 27 +-- .../corda/node/services/NotaryChangeTests.kt | 20 +- .../services/events/ScheduledFlowTests.kt | 11 +- .../services/schema/NodeSchemaServiceTest.kt | 6 +- .../statemachine/FlowFrameworkTests.kt | 12 +- .../vault/VaultSoftLockManagerTest.kt | 7 +- .../attachmentdemo/AttachmentDemoTest.kt | 10 +- .../corda/bank/BankOfCordaRPCClientTest.kt | 5 +- .../kotlin/net/corda/irs/IRSDemoTest.kt | 7 +- .../net/corda/test/spring/SpringDriver.kt | 19 +- .../net/corda/netmap/NetworkMapVisualiser.kt | 7 +- .../net/corda/netmap/VisualiserViewModel.kt | 14 +- .../net/corda/netmap/simulation/Simulation.kt | 35 +-- .../net/corda/traderdemo/TraderDemoTest.kt | 12 +- .../net/corda/testing/driver/DriverTests.kt | 8 +- .../testing/node/FlowStackSnapshotTest.kt | 18 +- .../node/MockNetworkIntegrationTests.kt | 1 + .../InternalMockNetworkIntegrationTests.kt | 26 +++ .../kotlin/net/corda/testing/driver/Driver.kt | 96 +++----- .../testing/driver/internal/DriverInternal.kt | 74 +++++++ .../testing/node/InMemoryMessagingNetwork.kt | 2 +- .../net/corda/testing/node/MockNetwork.kt | 178 +++++++++++++++ .../testing/node/internal/DriverDSLImpl.kt | 15 +- .../InternalMockNetwork.kt} | 86 ++------ .../testing/node/internal/ProcessUtilities.kt | 2 +- .../node/MockNodeFactoryInJavaTest.java | 13 +- .../InternalMockNetworkTests.kt} | 8 +- .../net/corda/explorer/ExplorerSimulation.kt | 11 +- .../net/corda/verifier/VerifierDriver.kt | 9 +- .../net/corda/verifier/VerifierTests.kt | 3 +- 63 files changed, 681 insertions(+), 554 deletions(-) create mode 100644 testing/node-driver/src/integration-test/kotlin/net/corda/testing/node/internal/InternalMockNetworkIntegrationTests.kt create mode 100644 testing/node-driver/src/main/kotlin/net/corda/testing/driver/internal/DriverInternal.kt create mode 100644 testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNetwork.kt rename testing/node-driver/src/main/kotlin/net/corda/testing/node/{MockNode.kt => internal/InternalMockNetwork.kt} (80%) rename testing/node-driver/src/test/kotlin/net/corda/testing/node/{MockNetworkTests.kt => internal/InternalMockNetworkTests.kt} (75%) diff --git a/.ci/api-current.txt b/.ci/api-current.txt index 4c7b632b0b..a9d2bebad3 100644 --- a/.ci/api-current.txt +++ b/.ci/api-current.txt @@ -1013,8 +1013,10 @@ public final class net.corda.core.crypto.SignatureScheme extends java.lang.Objec ## @net.corda.core.serialization.CordaSerializable public final class net.corda.core.crypto.TransactionSignature extends net.corda.core.crypto.DigitalSignature public (byte[], java.security.PublicKey, net.corda.core.crypto.SignatureMetadata) + public (byte[], java.security.PublicKey, net.corda.core.crypto.SignatureMetadata, net.corda.core.crypto.PartialMerkleTree) public boolean equals(Object) @org.jetbrains.annotations.NotNull public final java.security.PublicKey getBy() + @org.jetbrains.annotations.Nullable public final net.corda.core.crypto.PartialMerkleTree getPartialMerkleTree() @org.jetbrains.annotations.NotNull public final net.corda.core.crypto.SignatureMetadata getSignatureMetadata() public int hashCode() public final boolean isValid(net.corda.core.crypto.SecureHash) @@ -3696,6 +3698,11 @@ public final class net.corda.testing.driver.DriverParameters extends java.lang.O @org.jetbrains.annotations.NotNull public final net.corda.testing.driver.DriverParameters setWaitForAllNodesToFinish(boolean) public String toString() ## +@net.corda.core.DoNotImplement public interface net.corda.testing.driver.InProcess extends net.corda.testing.driver.NodeHandle + @org.jetbrains.annotations.NotNull public abstract net.corda.nodeapi.internal.persistence.CordaPersistence getDatabase() + @org.jetbrains.annotations.NotNull public abstract net.corda.node.services.api.StartedNodeServices getServices() + @org.jetbrains.annotations.NotNull public abstract rx.Observable registerInitiatedFlow(Class) +## public final class net.corda.testing.driver.JmxPolicy extends java.lang.Object public () public (boolean, net.corda.testing.driver.PortAllocation) @@ -3708,61 +3715,15 @@ public final class net.corda.testing.driver.JmxPolicy extends java.lang.Object public int hashCode() public String toString() ## -@net.corda.core.DoNotImplement public abstract class net.corda.testing.driver.NodeHandle extends java.lang.Object implements java.lang.AutoCloseable - public void close() - @org.jetbrains.annotations.NotNull public abstract net.corda.node.services.config.NodeConfiguration getConfiguration() +@net.corda.core.DoNotImplement public interface net.corda.testing.driver.NodeHandle extends java.lang.AutoCloseable + @org.jetbrains.annotations.NotNull public abstract java.nio.file.Path getBaseDirectory() @org.jetbrains.annotations.NotNull public abstract net.corda.core.node.NodeInfo getNodeInfo() + @org.jetbrains.annotations.NotNull public abstract net.corda.core.utilities.NetworkHostAndPort getP2pAddress() @org.jetbrains.annotations.NotNull public abstract net.corda.core.messaging.CordaRPCOps getRpc() - public abstract boolean getUseHTTPS() - @org.jetbrains.annotations.NotNull public abstract net.corda.core.utilities.NetworkHostAndPort getWebAddress() - @org.jetbrains.annotations.NotNull public final net.corda.client.rpc.CordaRPCClient rpcClientToNode() - @org.jetbrains.annotations.NotNull public final net.corda.client.rpc.CordaRPCClient rpcClientToNode(net.corda.nodeapi.internal.config.SSLConfiguration) + @org.jetbrains.annotations.NotNull public abstract net.corda.core.utilities.NetworkHostAndPort getRpcAddress() + @org.jetbrains.annotations.NotNull public abstract List getRpcUsers() public abstract void stop() ## -public static final class net.corda.testing.driver.NodeHandle$InProcess extends net.corda.testing.driver.NodeHandle - public (net.corda.core.node.NodeInfo, net.corda.core.messaging.CordaRPCOps, net.corda.node.services.config.NodeConfiguration, net.corda.core.utilities.NetworkHostAndPort, boolean, net.corda.node.internal.StartedNode, Thread, kotlin.jvm.functions.Function0) - @org.jetbrains.annotations.NotNull public final net.corda.core.node.NodeInfo component1() - @org.jetbrains.annotations.NotNull public final net.corda.core.messaging.CordaRPCOps component2() - @org.jetbrains.annotations.NotNull public final net.corda.node.services.config.NodeConfiguration component3() - @org.jetbrains.annotations.NotNull public final net.corda.core.utilities.NetworkHostAndPort component4() - public final boolean component5() - @org.jetbrains.annotations.NotNull public final net.corda.node.internal.StartedNode component6() - @org.jetbrains.annotations.NotNull public final Thread component7() - @org.jetbrains.annotations.NotNull public final net.corda.testing.driver.NodeHandle$InProcess copy(net.corda.core.node.NodeInfo, net.corda.core.messaging.CordaRPCOps, net.corda.node.services.config.NodeConfiguration, net.corda.core.utilities.NetworkHostAndPort, boolean, net.corda.node.internal.StartedNode, Thread, kotlin.jvm.functions.Function0) - public boolean equals(Object) - @org.jetbrains.annotations.NotNull public net.corda.node.services.config.NodeConfiguration getConfiguration() - @org.jetbrains.annotations.NotNull public final net.corda.node.internal.StartedNode getNode() - @org.jetbrains.annotations.NotNull public net.corda.core.node.NodeInfo getNodeInfo() - @org.jetbrains.annotations.NotNull public final Thread getNodeThread() - @org.jetbrains.annotations.NotNull public net.corda.core.messaging.CordaRPCOps getRpc() - public boolean getUseHTTPS() - @org.jetbrains.annotations.NotNull public net.corda.core.utilities.NetworkHostAndPort getWebAddress() - public int hashCode() - public void stop() - public String toString() -## -public static final class net.corda.testing.driver.NodeHandle$OutOfProcess extends net.corda.testing.driver.NodeHandle - public (net.corda.core.node.NodeInfo, net.corda.core.messaging.CordaRPCOps, net.corda.node.services.config.NodeConfiguration, net.corda.core.utilities.NetworkHostAndPort, boolean, Integer, Process, kotlin.jvm.functions.Function0) - @org.jetbrains.annotations.NotNull public final net.corda.core.node.NodeInfo component1() - @org.jetbrains.annotations.NotNull public final net.corda.core.messaging.CordaRPCOps component2() - @org.jetbrains.annotations.NotNull public final net.corda.node.services.config.NodeConfiguration component3() - @org.jetbrains.annotations.NotNull public final net.corda.core.utilities.NetworkHostAndPort component4() - public final boolean component5() - @org.jetbrains.annotations.Nullable public final Integer component6() - @org.jetbrains.annotations.NotNull public final Process component7() - @org.jetbrains.annotations.NotNull public final net.corda.testing.driver.NodeHandle$OutOfProcess copy(net.corda.core.node.NodeInfo, net.corda.core.messaging.CordaRPCOps, net.corda.node.services.config.NodeConfiguration, net.corda.core.utilities.NetworkHostAndPort, boolean, Integer, Process, kotlin.jvm.functions.Function0) - public boolean equals(Object) - @org.jetbrains.annotations.NotNull public net.corda.node.services.config.NodeConfiguration getConfiguration() - @org.jetbrains.annotations.Nullable public final Integer getDebugPort() - @org.jetbrains.annotations.NotNull public net.corda.core.node.NodeInfo getNodeInfo() - @org.jetbrains.annotations.NotNull public final Process getProcess() - @org.jetbrains.annotations.NotNull public net.corda.core.messaging.CordaRPCOps getRpc() - public boolean getUseHTTPS() - @org.jetbrains.annotations.NotNull public net.corda.core.utilities.NetworkHostAndPort getWebAddress() - public int hashCode() - public void stop() - public String toString() -## public final class net.corda.testing.driver.NodeParameters extends java.lang.Object public () public (net.corda.core.identity.CordaX500Name, List, net.corda.node.services.config.VerifierType, Map, Boolean, String) @@ -3802,6 +3763,9 @@ public final class net.corda.testing.driver.NotaryHandle extends java.lang.Objec public int hashCode() public String toString() ## +@net.corda.core.DoNotImplement public interface net.corda.testing.driver.OutOfProcess extends net.corda.testing.driver.NodeHandle + @org.jetbrains.annotations.NotNull public abstract Process getProcess() +## @net.corda.core.DoNotImplement public abstract class net.corda.testing.driver.PortAllocation extends java.lang.Object @org.jetbrains.annotations.NotNull public final net.corda.core.utilities.NetworkHostAndPort nextHostAndPort() public abstract int nextPort() @@ -3951,143 +3915,67 @@ public final class net.corda.testing.node.MockKeyManagementService extends net.c public class net.corda.testing.node.MockNetwork extends java.lang.Object public (List) public (List, net.corda.testing.node.MockNetworkParameters) - public (List, net.corda.testing.node.MockNetworkParameters, boolean, boolean, net.corda.testing.node.InMemoryMessagingNetwork$ServicePeerAllocationStrategy, kotlin.jvm.functions.Function1, boolean, List, int) - @org.jetbrains.annotations.NotNull public final net.corda.testing.node.MockNetwork$MockNode addressToNode(net.corda.core.messaging.MessageRecipients) + public (List, net.corda.testing.node.MockNetworkParameters, boolean, boolean, net.corda.testing.node.InMemoryMessagingNetwork$ServicePeerAllocationStrategy, boolean, List, int) @org.jetbrains.annotations.NotNull public final java.nio.file.Path baseDirectory(int) - @org.jetbrains.annotations.NotNull public final net.corda.node.internal.StartedNode createNode(net.corda.testing.node.MockNodeParameters) - @org.jetbrains.annotations.NotNull public final net.corda.node.internal.StartedNode createNode(net.corda.testing.node.MockNodeParameters, kotlin.jvm.functions.Function1) - @org.jetbrains.annotations.NotNull public final net.corda.node.internal.StartedNode createPartyNode() - @org.jetbrains.annotations.NotNull public final net.corda.node.internal.StartedNode createPartyNode(net.corda.core.identity.CordaX500Name) - @org.jetbrains.annotations.NotNull public final net.corda.testing.node.MockNetwork$MockNode createUnstartedNode(net.corda.testing.node.MockNodeParameters) - @org.jetbrains.annotations.NotNull public final net.corda.testing.node.MockNetwork$MockNode createUnstartedNode(net.corda.testing.node.MockNodeParameters, kotlin.jvm.functions.Function1) + @org.jetbrains.annotations.NotNull public final net.corda.testing.node.StartedMockNode createNode(net.corda.testing.node.MockNodeParameters) + @org.jetbrains.annotations.NotNull public final net.corda.testing.node.StartedMockNode createPartyNode(net.corda.core.identity.CordaX500Name) + @org.jetbrains.annotations.NotNull public final net.corda.testing.node.UnstartedMockNode createUnstartedNode(net.corda.testing.node.MockNodeParameters) + @org.jetbrains.annotations.NotNull public final List getCordappPackages() @org.jetbrains.annotations.NotNull public final net.corda.core.identity.Party getDefaultNotaryIdentity() - @org.jetbrains.annotations.NotNull public final net.corda.core.identity.PartyAndCertificate getDefaultNotaryIdentityAndCert() - @org.jetbrains.annotations.NotNull public final net.corda.node.internal.StartedNode getDefaultNotaryNode() - @org.jetbrains.annotations.NotNull public final net.corda.testing.node.InMemoryMessagingNetwork getMessagingNetwork() + @org.jetbrains.annotations.NotNull public final net.corda.testing.node.StartedMockNode getDefaultNotaryNode() + @org.jetbrains.annotations.NotNull public final net.corda.testing.node.MockNetworkParameters getDefaultParameters() + public final boolean getInitialiseSerialization() + public final int getMaxTransactionSize() + public final boolean getNetworkSendManuallyPumped() public final int getNextNodeId() - @org.jetbrains.annotations.NotNull public final List getNodes() @org.jetbrains.annotations.NotNull public final List getNotaryNodes() + @org.jetbrains.annotations.NotNull public final List getNotarySpecs() + @org.jetbrains.annotations.NotNull public final net.corda.testing.node.InMemoryMessagingNetwork$ServicePeerAllocationStrategy getServicePeerAllocationStrategy() + public final boolean getThreadPerNode() public final void runNetwork() public final void runNetwork(int) public final void startNodes() public final void stopNodes() public final void waitQuiescent() ## -public static class net.corda.testing.node.MockNetwork$MockNode extends net.corda.node.internal.AbstractNode - public (net.corda.testing.node.MockNodeArgs) - protected int acceptableLiveFiberCountOnStop() - protected void checkNetworkMapIsInitialized() - public final void disableDBCloseOnStop() - @org.jetbrains.annotations.NotNull protected java.security.KeyPair generateKeyPair() - public final int getAcceptableLiveFiberCountOnStop() - @org.jetbrains.annotations.NotNull public final java.math.BigInteger getCounter() - public final int getId() - @org.jetbrains.annotations.NotNull protected org.slf4j.Logger getLog() - @org.jetbrains.annotations.NotNull public final net.corda.testing.node.MockNetwork getMockNet() - @org.jetbrains.annotations.NotNull protected rx.internal.schedulers.CachedThreadScheduler getRxIoScheduler() - @org.jetbrains.annotations.NotNull public List getSerializationWhitelists() - @org.jetbrains.annotations.NotNull protected net.corda.node.utilities.AffinityExecutor getServerThread() - @org.jetbrains.annotations.Nullable public net.corda.node.internal.StartedNode getStarted() - @org.jetbrains.annotations.NotNull public final List getTestSerializationWhitelists() - protected Object initialiseDatabasePersistence(net.corda.node.services.api.SchemaService, net.corda.core.node.services.IdentityService, kotlin.jvm.functions.Function1) - @org.jetbrains.annotations.NotNull protected net.corda.node.services.transactions.BFTSMaRt$Cluster makeBFTCluster(java.security.PublicKey, net.corda.node.services.config.BFTSMaRtConfiguration) - @org.jetbrains.annotations.NotNull protected net.corda.core.node.services.KeyManagementService makeKeyManagementService(net.corda.node.services.api.IdentityServiceInternal, Set) - @org.jetbrains.annotations.NotNull protected net.corda.node.services.messaging.MessagingService makeMessagingService(net.corda.nodeapi.internal.persistence.CordaPersistence, net.corda.core.node.NodeInfo) - @org.jetbrains.annotations.NotNull protected net.corda.node.services.transactions.InMemoryTransactionVerifierService makeTransactionVerifierService() - public final void manuallyCloseDB() - @org.jetbrains.annotations.NotNull protected List myAddresses() - public final void setAcceptableLiveFiberCountOnStop(int) - public final void setCounter(java.math.BigInteger) - public final void setMessagingServiceSpy(net.corda.testing.node.MessagingServiceSpy) - @org.jetbrains.annotations.NotNull public net.corda.node.internal.StartedNode start() - protected void startMessagingService(net.corda.core.messaging.RPCOps) - public void startShell(net.corda.core.messaging.CordaRPCOps) - public static final net.corda.testing.node.MockNetwork$MockNode$Companion Companion -## -public static final class net.corda.testing.node.MockNetwork$MockNode$Companion extends java.lang.Object -## -public static final class net.corda.testing.node.MockNetwork$MockNode$makeBFTCluster$1 extends java.lang.Object implements net.corda.node.services.transactions.BFTSMaRt$Cluster - public void waitUntilAllReplicasHaveInitialized() -## -public static final class net.corda.testing.node.MockNetwork$NotarySpec extends java.lang.Object +public final class net.corda.testing.node.MockNetworkNotarySpec extends java.lang.Object public (net.corda.core.identity.CordaX500Name) public (net.corda.core.identity.CordaX500Name, boolean) @org.jetbrains.annotations.NotNull public final net.corda.core.identity.CordaX500Name component1() public final boolean component2() - @org.jetbrains.annotations.NotNull public final net.corda.testing.node.MockNetwork$NotarySpec copy(net.corda.core.identity.CordaX500Name, boolean) + @org.jetbrains.annotations.NotNull public final net.corda.testing.node.MockNetworkNotarySpec copy(net.corda.core.identity.CordaX500Name, boolean) public boolean equals(Object) @org.jetbrains.annotations.NotNull public final net.corda.core.identity.CordaX500Name getName() public final boolean getValidating() public int hashCode() public String toString() ## -public static final class net.corda.testing.node.MockNetwork$sharedServerThread$1 extends net.corda.node.utilities.AffinityExecutor$ServiceAffinityExecutor - public boolean awaitTermination(long, concurrent.TimeUnit) - public void shutdown() -## public final class net.corda.testing.node.MockNetworkParameters extends java.lang.Object public () - public (boolean, boolean, net.corda.testing.node.InMemoryMessagingNetwork$ServicePeerAllocationStrategy, kotlin.jvm.functions.Function1, boolean, List) + public (boolean, boolean, net.corda.testing.node.InMemoryMessagingNetwork$ServicePeerAllocationStrategy, boolean, List, int) public final boolean component1() public final boolean component2() @org.jetbrains.annotations.NotNull public final net.corda.testing.node.InMemoryMessagingNetwork$ServicePeerAllocationStrategy component3() - @org.jetbrains.annotations.NotNull public final kotlin.jvm.functions.Function1 component4() - public final boolean component5() - @org.jetbrains.annotations.NotNull public final List component6() - @org.jetbrains.annotations.NotNull public final net.corda.testing.node.MockNetworkParameters copy(boolean, boolean, net.corda.testing.node.InMemoryMessagingNetwork$ServicePeerAllocationStrategy, kotlin.jvm.functions.Function1, boolean, List) + public final boolean component4() + @org.jetbrains.annotations.NotNull public final List component5() + public final int component6() + @org.jetbrains.annotations.NotNull public final net.corda.testing.node.MockNetworkParameters copy(boolean, boolean, net.corda.testing.node.InMemoryMessagingNetwork$ServicePeerAllocationStrategy, boolean, List, int) public boolean equals(Object) - @org.jetbrains.annotations.NotNull public final kotlin.jvm.functions.Function1 getDefaultFactory() public final boolean getInitialiseSerialization() + public final int getMaxTransactionSize() public final boolean getNetworkSendManuallyPumped() @org.jetbrains.annotations.NotNull public final List getNotarySpecs() @org.jetbrains.annotations.NotNull public final net.corda.testing.node.InMemoryMessagingNetwork$ServicePeerAllocationStrategy getServicePeerAllocationStrategy() public final boolean getThreadPerNode() public int hashCode() - @org.jetbrains.annotations.NotNull public final net.corda.testing.node.MockNetworkParameters setDefaultFactory(kotlin.jvm.functions.Function1) @org.jetbrains.annotations.NotNull public final net.corda.testing.node.MockNetworkParameters setInitialiseSerialization(boolean) + @org.jetbrains.annotations.NotNull public final net.corda.testing.node.MockNetworkParameters setMaxTransactionSize(int) @org.jetbrains.annotations.NotNull public final net.corda.testing.node.MockNetworkParameters setNetworkSendManuallyPumped(boolean) @org.jetbrains.annotations.NotNull public final net.corda.testing.node.MockNetworkParameters setNotarySpecs(List) @org.jetbrains.annotations.NotNull public final net.corda.testing.node.MockNetworkParameters setServicePeerAllocationStrategy(net.corda.testing.node.InMemoryMessagingNetwork$ServicePeerAllocationStrategy) @org.jetbrains.annotations.NotNull public final net.corda.testing.node.MockNetworkParameters setThreadPerNode(boolean) public String toString() ## -public final class net.corda.testing.node.MockNodeArgs extends java.lang.Object - public (net.corda.node.services.config.NodeConfiguration, net.corda.testing.node.MockNetwork, int, java.math.BigInteger, net.corda.node.VersionInfo) - @org.jetbrains.annotations.NotNull public final net.corda.node.services.config.NodeConfiguration component1() - @org.jetbrains.annotations.NotNull public final net.corda.testing.node.MockNetwork component2() - public final int component3() - @org.jetbrains.annotations.NotNull public final java.math.BigInteger component4() - @org.jetbrains.annotations.NotNull public final net.corda.node.VersionInfo component5() - @org.jetbrains.annotations.NotNull public final net.corda.testing.node.MockNodeArgs copy(net.corda.node.services.config.NodeConfiguration, net.corda.testing.node.MockNetwork, int, java.math.BigInteger, net.corda.node.VersionInfo) - public boolean equals(Object) - @org.jetbrains.annotations.NotNull public final net.corda.node.services.config.NodeConfiguration getConfig() - @org.jetbrains.annotations.NotNull public final java.math.BigInteger getEntropyRoot() - public final int getId() - @org.jetbrains.annotations.NotNull public final net.corda.testing.node.MockNetwork getNetwork() - @org.jetbrains.annotations.NotNull public final net.corda.node.VersionInfo getVersion() - public int hashCode() - public String toString() -## -public final class net.corda.testing.node.MockNodeKt extends java.lang.Object - @org.jetbrains.annotations.Nullable public static final net.corda.testing.node.InMemoryMessagingNetwork$MessageTransfer pumpReceive(net.corda.node.internal.StartedNode, boolean) - public static final void setMessagingServiceSpy(net.corda.node.internal.StartedNode, net.corda.testing.node.MessagingServiceSpy) -## -@net.corda.core.DoNotImplement public abstract static class net.corda.testing.node.MockNodeKt$mockNodeConfiguration$AbstractNodeConfiguration extends java.lang.Object implements net.corda.node.services.config.NodeConfiguration - public () - public long getAttachmentCacheBound() - public long getAttachmentContentCacheSizeBytes() - @org.jetbrains.annotations.NotNull public java.nio.file.Path getCertificatesDirectory() - public boolean getDetectPublicIp() - public boolean getNoLocalShell() - @org.jetbrains.annotations.NotNull public java.nio.file.Path getNodeKeystore() - @org.jetbrains.annotations.NotNull public java.nio.file.Path getSslKeystore() - public long getTransactionCacheSizeBytes() - @org.jetbrains.annotations.NotNull public java.nio.file.Path getTrustStoreFile() - public boolean getUseTestClock() - @org.jetbrains.annotations.NotNull public net.corda.nodeapi.internal.crypto.X509KeyStore loadNodeKeyStore(boolean) - @org.jetbrains.annotations.NotNull public net.corda.nodeapi.internal.crypto.X509KeyStore loadSslKeyStore(boolean) - @org.jetbrains.annotations.NotNull public net.corda.nodeapi.internal.crypto.X509KeyStore loadTrustStore(boolean) -## public final class net.corda.testing.node.MockNodeParameters extends java.lang.Object public () public (Integer, net.corda.core.identity.CordaX500Name, java.math.BigInteger, kotlin.jvm.functions.Function1, net.corda.node.VersionInfo) @@ -4234,11 +4122,33 @@ public final class net.corda.testing.node.NotarySpec extends java.lang.Object public int hashCode() public String toString() ## +public final class net.corda.testing.node.StartedMockNode extends java.lang.Object + @org.jetbrains.annotations.NotNull public final List findStateMachines(Class) + @org.jetbrains.annotations.NotNull public final net.corda.nodeapi.internal.persistence.CordaPersistence getDatabase() + public final int getId() + @org.jetbrains.annotations.NotNull public final net.corda.core.node.NodeInfo getInfo() + @org.jetbrains.annotations.NotNull public final net.corda.node.services.messaging.MessagingService getNetwork() + @org.jetbrains.annotations.NotNull public final net.corda.node.services.api.StartedNodeServices getServices() + @org.jetbrains.annotations.Nullable public final net.corda.testing.node.InMemoryMessagingNetwork$MessageTransfer pumpReceive(boolean) + @org.jetbrains.annotations.NotNull public final rx.Observable registerInitiatedFlow(Class) + public final void setMessagingServiceSpy(net.corda.testing.node.MessagingServiceSpy) + public final void stop() + public static final net.corda.testing.node.StartedMockNode$Companion Companion +## +public static final class net.corda.testing.node.StartedMockNode$Companion extends java.lang.Object +## @javax.annotation.concurrent.ThreadSafe public final class net.corda.testing.node.TestClock extends net.corda.node.internal.MutableClock public (java.time.Clock) public synchronized final void advanceBy(java.time.Duration) public synchronized final void setTo(java.time.Instant) ## +public final class net.corda.testing.node.UnstartedMockNode extends java.lang.Object + public final int getId() + @org.jetbrains.annotations.NotNull public final net.corda.testing.node.StartedMockNode start() + public static final net.corda.testing.node.UnstartedMockNode$Companion Companion +## +public static final class net.corda.testing.node.UnstartedMockNode$Companion extends java.lang.Object +## public final class net.corda.testing.node.User extends java.lang.Object public (String, String, Set) @org.jetbrains.annotations.NotNull public final String component1() 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 81006e9614..157fa8b4b1 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.rpcOptions.address!!, cashUser.username, cashUser.password) + monitor.register(aliceNodeHandle.rpcAddress, 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.rpcOptions.address!!, cashUser.username, cashUser.password) + monitorBob.register(bobNodeHandle.rpcAddress, cashUser.username, cashUser.password) rpcBob = monitorBob.proxyObservable.value!! runTest() } 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 b5b8371959..d7a1d863d7 100644 --- a/core/src/test/java/net/corda/core/flows/FlowsInJavaTest.java +++ b/core/src/test/java/net/corda/core/flows/FlowsInJavaTest.java @@ -3,9 +3,9 @@ package net.corda.core.flows; 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.core.TestConstants; import net.corda.testing.node.MockNetwork; +import net.corda.testing.node.StartedMockNode; import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -21,8 +21,8 @@ import static net.corda.testing.node.NodeTestUtils.startFlow; public class FlowsInJavaTest { private final MockNetwork mockNet = new MockNetwork(emptyList()); - private StartedNode aliceNode; - private StartedNode bobNode; + private StartedMockNode aliceNode; + private StartedMockNode bobNode; private Party bob; @Before 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 5499040845..6c31273511 100644 --- a/core/src/test/kotlin/net/corda/core/flows/AttachmentTests.kt +++ b/core/src/test/kotlin/net/corda/core/flows/AttachmentTests.kt @@ -15,6 +15,7 @@ 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.internal.InternalMockNetwork import net.corda.testing.node.startFlow import org.junit.After import org.junit.Before @@ -27,11 +28,11 @@ import kotlin.test.assertEquals import kotlin.test.assertFailsWith class AttachmentTests { - lateinit var mockNet: MockNetwork + lateinit var mockNet: InternalMockNetwork @Before fun setUp() { - mockNet = MockNetwork(emptyList()) + mockNet = InternalMockNetwork(emptyList()) } @After @@ -100,7 +101,7 @@ class AttachmentTests { fun maliciousResponse() { // Make a node that doesn't do sanity checking at load time. val aliceNode = mockNet.createNode(MockNodeParameters(legalName = ALICE_NAME), nodeFactory = { args -> - object : MockNetwork.MockNode(args) { + object : InternalMockNetwork.MockNode(args) { override fun start() = super.start().apply { attachments.checkAttachmentsOnLoad = false } } }) 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 d3bbcd9dc8..2dfbb59352 100644 --- a/core/src/test/kotlin/net/corda/core/flows/CollectSignaturesFlowTests.kt +++ b/core/src/test/kotlin/net/corda/core/flows/CollectSignaturesFlowTests.kt @@ -11,11 +11,11 @@ import net.corda.core.identity.groupAbstractPartyByWellKnownParty import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.TransactionBuilder import net.corda.core.utilities.getOrThrow -import net.corda.node.internal.StartedNode 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.StartedMockNode import net.corda.testing.node.MockServices import net.corda.testing.node.startFlow import org.junit.After @@ -30,9 +30,9 @@ class CollectSignaturesFlowTests { } private lateinit var mockNet: MockNetwork - private lateinit var aliceNode: StartedNode - private lateinit var bobNode: StartedNode - private lateinit var charlieNode: StartedNode + private lateinit var aliceNode: StartedMockNode + private lateinit var bobNode: StartedMockNode + private lateinit var charlieNode: StartedMockNode private lateinit var alice: Party private lateinit var bob: Party private lateinit var charlie: Party 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 61f50c1e05..efcc570ac4 100644 --- a/core/src/test/kotlin/net/corda/core/flows/ContractUpgradeFlowTest.kt +++ b/core/src/test/kotlin/net/corda/core/flows/ContractUpgradeFlowTest.kt @@ -19,17 +19,14 @@ 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.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 -import net.corda.testing.node.internal.RPCDriverDSL -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.core.ALICE_NAME +import net.corda.testing.core.BOB_NAME import net.corda.testing.core.singleIdentity +import net.corda.testing.node.User +import net.corda.testing.node.internal.* +import net.corda.testing.node.internal.InternalMockNetwork.MockNode import net.corda.testing.node.startFlow import org.junit.After import org.junit.Before @@ -40,16 +37,16 @@ import kotlin.test.assertFailsWith import kotlin.test.assertTrue class ContractUpgradeFlowTest { - private lateinit var mockNet: MockNetwork - private lateinit var aliceNode: StartedNode - private lateinit var bobNode: StartedNode + private lateinit var mockNet: InternalMockNetwork + private lateinit var aliceNode: StartedNode + private lateinit var bobNode: StartedNode private lateinit var notary: Party private lateinit var alice: Party private lateinit var bob: Party @Before fun setup() { - mockNet = MockNetwork(cordappPackages = listOf("net.corda.testing.contracts", "net.corda.finance.contracts.asset", "net.corda.core.flows")) + mockNet = InternalMockNetwork(cordappPackages = listOf("net.corda.testing.contracts", "net.corda.finance.contracts.asset", "net.corda.core.flows")) aliceNode = mockNet.createPartyNode(ALICE_NAME) bobNode = mockNet.createPartyNode(BOB_NAME) notary = mockNet.defaultNotaryIdentity @@ -103,7 +100,7 @@ class ContractUpgradeFlowTest { val result = resultFuture.getOrThrow() - fun check(node: StartedNode<*>) { + fun check(node: StartedNode) { val nodeStx = node.database.transaction { node.services.validatedTransactions.getTransaction(result.ref.txhash) } @@ -123,7 +120,7 @@ class ContractUpgradeFlowTest { check(bobNode) } - private fun RPCDriverDSL.startProxy(node: StartedNode<*>, user: User): CordaRPCOps { + private fun RPCDriverDSL.startProxy(node: StartedNode, user: User): CordaRPCOps { return startRpcClient( rpcAddress = startRpcServer( rpcUser = user, diff --git a/core/src/test/kotlin/net/corda/core/flows/FlowTestsUtils.kt b/core/src/test/kotlin/net/corda/core/flows/FlowTestsUtils.kt index 4d2d12335d..9ea4d254f1 100644 --- a/core/src/test/kotlin/net/corda/core/flows/FlowTestsUtils.kt +++ b/core/src/test/kotlin/net/corda/core/flows/FlowTestsUtils.kt @@ -5,6 +5,8 @@ import net.corda.core.utilities.UntrustworthyData import net.corda.core.utilities.unwrap import net.corda.node.internal.InitiatedFlowFactory import net.corda.node.internal.StartedNode +import net.corda.testing.node.StartedMockNode +import net.corda.testing.node.internal.InternalMockNetwork import kotlin.reflect.KClass /** @@ -37,14 +39,14 @@ class NoAnswer(private val closure: () -> Unit = {}) : FlowLogic() { /** * Allows to register a flow of type [R] against an initiating flow of type [I]. */ -inline fun , reified R : FlowLogic<*>> StartedNode<*>.registerInitiatedFlow(initiatingFlowType: KClass, crossinline construct: (session: FlowSession) -> R) { +inline fun , reified R : FlowLogic<*>> StartedNode.registerInitiatedFlow(initiatingFlowType: KClass, crossinline construct: (session: FlowSession) -> R) { internalRegisterFlowFactory(initiatingFlowType.java, InitiatedFlowFactory.Core { session -> construct(session) }, R::class.javaObjectType, true) } /** * Allows to register a flow of type [Answer] against an initiating flow of type [I], returning a valure of type [R]. */ -inline fun , reified R : Any> StartedNode<*>.registerAnswer(initiatingFlowType: KClass, value: R) { +inline fun , reified R : Any> StartedNode.registerAnswer(initiatingFlowType: KClass, value: R) { internalRegisterFlowFactory(initiatingFlowType.java, InitiatedFlowFactory.Core { session -> Answer(session, value) }, Answer::class.javaObjectType, true) } 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 1893e3607e..18fdf82f64 100644 --- a/core/src/test/kotlin/net/corda/core/flows/ReceiveAllFlowTests.kt +++ b/core/src/test/kotlin/net/corda/core/flows/ReceiveAllFlowTests.kt @@ -5,15 +5,15 @@ import net.corda.core.identity.Party 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.core.singleIdentity +import net.corda.testing.node.internal.InternalMockNetwork import net.corda.testing.node.startFlow import org.assertj.core.api.Assertions.assertThat import org.junit.After import org.junit.Test class ReceiveMultipleFlowTests { - private val mockNet = MockNetwork(emptyList()) + private val mockNet = InternalMockNetwork(emptyList()) private val nodes = (0..2).map { mockNet.createPartyNode() } @After fun stopNodes() { 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 9038232355..fa6ab2b0ff 100644 --- a/core/src/test/kotlin/net/corda/core/internal/ResolveTransactionsFlowTest.kt +++ b/core/src/test/kotlin/net/corda/core/internal/ResolveTransactionsFlowTest.kt @@ -8,10 +8,10 @@ import net.corda.core.identity.Party import net.corda.core.transactions.SignedTransaction import net.corda.core.utilities.getOrThrow 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.core.singleIdentity +import net.corda.testing.node.StartedMockNode import net.corda.testing.node.startFlow import org.junit.After import org.junit.Before @@ -28,9 +28,9 @@ import kotlin.test.assertNull // DOCSTART 3 class ResolveTransactionsFlowTest { private lateinit var mockNet: MockNetwork - private lateinit var notaryNode: StartedNode - private lateinit var megaCorpNode: StartedNode - private lateinit var miniCorpNode: StartedNode + private lateinit var notaryNode: StartedMockNode + private lateinit var megaCorpNode: StartedMockNode + private lateinit var miniCorpNode: StartedMockNode private lateinit var megaCorp: Party private lateinit var miniCorp: Party private lateinit var notary: Party 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 56959a0cec..8551845849 100644 --- a/core/src/test/kotlin/net/corda/core/serialization/AttachmentSerializationTest.kt +++ b/core/src/test/kotlin/net/corda/core/serialization/AttachmentSerializationTest.kt @@ -21,6 +21,7 @@ import net.corda.testing.core.BOB_NAME import net.corda.testing.node.MockNetwork import net.corda.testing.node.MockNodeParameters import net.corda.testing.core.singleIdentity +import net.corda.testing.node.internal.InternalMockNetwork import net.corda.testing.node.startFlow import org.junit.After import org.junit.Before @@ -63,14 +64,14 @@ private fun updateAttachment(attachmentId: SecureHash, data: ByteArray) { } class AttachmentSerializationTest { - private lateinit var mockNet: MockNetwork - private lateinit var server: StartedNode - private lateinit var client: StartedNode + private lateinit var mockNet: InternalMockNetwork + private lateinit var server: StartedNode + private lateinit var client: StartedNode private lateinit var serverIdentity: Party @Before fun setUp() { - mockNet = MockNetwork(emptyList()) + mockNet = InternalMockNetwork(emptyList()) server = mockNet.createNode(MockNodeParameters(legalName = ALICE_NAME)) client = mockNet.createNode(MockNodeParameters(legalName = BOB_NAME)) client.internals.disableDBCloseOnStop() // Otherwise the in-memory database may disappear (taking the checkpoint with it) while we reboot the client. @@ -161,7 +162,7 @@ class AttachmentSerializationTest { private fun rebootClientAndGetAttachmentContent(checkAttachmentsOnLoad: Boolean = true): String { client.dispose() client = mockNet.createNode(MockNodeParameters(client.internals.id, client.internals.configuration.myLegalName), { args -> - object : MockNetwork.MockNode(args) { + object : InternalMockNetwork.MockNode(args) { override fun start() = super.start().apply { attachments.checkAttachmentsOnLoad = checkAttachmentsOnLoad } } }) diff --git a/docs/source/changelog.rst b/docs/source/changelog.rst index 23a00b1ada..d5fbbcc6cc 100644 --- a/docs/source/changelog.rst +++ b/docs/source/changelog.rst @@ -183,6 +183,9 @@ UNRELEASED * Marked ``stateMachine`` on ``FlowLogic`` as ``CordaInternal`` to make clear that is it not part of the public api and is only for internal use +* Created new ``StartedMockNode`` and ``UnstartedMockNode`` classes which are wrappers around our MockNode implementation + that expose relevant methods for testing without exposing internals, create these using a ``MockNetwork``. + .. _changelog_v1: Release 1.0 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 99eb5df37f..164e6f08f8 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 @@ -1,5 +1,6 @@ package net.corda.docs +import net.corda.client.rpc.CordaRPCClient import net.corda.core.internal.concurrent.transpose import net.corda.core.messaging.CordaRPCOps import net.corda.core.messaging.startFlow @@ -45,10 +46,10 @@ class IntegrationTestingTutorial { // END 1 // START 2 - val aliceClient = alice.rpcClientToNode() + val aliceClient = CordaRPCClient(alice.rpcAddress) val aliceProxy = aliceClient.start("aliceUser", "testPassword1").proxy - val bobClient = bob.rpcClientToNode() + val bobClient = CordaRPCClient(bob.rpcAddress) val bobProxy = bobClient.start("bobUser", "testPassword2").proxy // END 2 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 896152398d..255caf2271 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 @@ -2,6 +2,7 @@ package net.corda.docs +import net.corda.client.rpc.CordaRPCClient import net.corda.core.contracts.Amount import net.corda.core.messaging.CordaRPCOps import net.corda.core.messaging.startFlow @@ -53,7 +54,7 @@ fun main(args: Array) { // END 1 // START 2 - val client = node.rpcClientToNode() + val client = CordaRPCClient(node.rpcAddress) val proxy = client.start("user", "password").proxy thread { diff --git a/docs/source/example-code/src/main/kotlin/net/corda/docs/tutorial/mocknetwork/TutorialMockNetwork.kt b/docs/source/example-code/src/main/kotlin/net/corda/docs/tutorial/mocknetwork/TutorialMockNetwork.kt index 1ab3513656..520304ee2f 100644 --- a/docs/source/example-code/src/main/kotlin/net/corda/docs/tutorial/mocknetwork/TutorialMockNetwork.kt +++ b/docs/source/example-code/src/main/kotlin/net/corda/docs/tutorial/mocknetwork/TutorialMockNetwork.kt @@ -13,15 +13,10 @@ import net.corda.core.serialization.serialize import net.corda.core.utilities.OpaqueBytes import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.unwrap -import net.corda.node.internal.StartedNode import net.corda.node.services.messaging.Message import net.corda.node.services.statemachine.DataSessionMessage import net.corda.node.services.statemachine.ExistingSessionMessage -import net.corda.testing.node.InMemoryMessagingNetwork -import net.corda.testing.node.MessagingServiceSpy -import net.corda.testing.node.MockNetwork -import net.corda.testing.node.setMessagingServiceSpy -import net.corda.testing.node.startFlow +import net.corda.testing.node.* import org.junit.After import org.junit.Before import org.junit.Rule @@ -58,8 +53,8 @@ class TutorialMockNetwork { } lateinit private var mockNet: MockNetwork - lateinit private var nodeA: StartedNode - lateinit private var nodeB: StartedNode + lateinit private var nodeA: StartedMockNode + lateinit private var nodeB: StartedMockNode @Rule @JvmField 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 efd1935059..2b194b1e94 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 @@ -7,9 +7,9 @@ import net.corda.core.utilities.getOrThrow 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.core.chooseIdentity import net.corda.testing.node.MockNetwork +import net.corda.testing.node.StartedMockNode import net.corda.testing.node.startFlow import org.junit.After import org.junit.Assert @@ -19,8 +19,8 @@ import java.util.* class CustomVaultQueryTest { private lateinit var mockNet: MockNetwork - private lateinit var nodeA: StartedNode - private lateinit var nodeB: StartedNode + private lateinit var nodeA: StartedMockNode + private lateinit var nodeB: StartedMockNode private lateinit var notary: Party @Before 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 8ecfb2c453..0805565e03 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 @@ -7,9 +7,9 @@ import net.corda.core.utilities.getOrThrow 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.core.chooseIdentity import net.corda.testing.node.MockNetwork +import net.corda.testing.node.StartedMockNode import net.corda.testing.node.startFlow import org.junit.After import org.junit.Before @@ -18,8 +18,8 @@ import kotlin.test.assertEquals class FxTransactionBuildTutorialTest { private lateinit var mockNet: MockNetwork - private lateinit var nodeA: StartedNode - private lateinit var nodeB: StartedNode + private lateinit var nodeA: StartedMockNode + private lateinit var nodeB: StartedMockNode private lateinit var notary: Party @Before 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 9c70c96316..40ba1deffc 100644 --- a/finance/src/test/kotlin/net/corda/finance/flows/CashExitFlowTests.kt +++ b/finance/src/test/kotlin/net/corda/finance/flows/CashExitFlowTests.kt @@ -6,11 +6,10 @@ import net.corda.core.utilities.getOrThrow 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.core.BOC_NAME import net.corda.testing.node.InMemoryMessagingNetwork.ServicePeerAllocationStrategy.RoundRobin import net.corda.testing.node.MockNetwork -import net.corda.testing.node.MockNetwork.MockNode +import net.corda.testing.node.StartedMockNode import net.corda.testing.node.startFlow import org.junit.After import org.junit.Before @@ -22,7 +21,7 @@ class CashExitFlowTests { private lateinit var mockNet: MockNetwork private val initialBalance = 2000.DOLLARS private val ref = OpaqueBytes.of(0x01) - private lateinit var bankOfCordaNode: StartedNode + private lateinit var bankOfCordaNode: StartedMockNode private lateinit var bankOfCorda: Party private lateinit var notary: Party 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 95c283415e..cd6522d901 100644 --- a/finance/src/test/kotlin/net/corda/finance/flows/CashIssueFlowTests.kt +++ b/finance/src/test/kotlin/net/corda/finance/flows/CashIssueFlowTests.kt @@ -6,11 +6,10 @@ import net.corda.core.utilities.getOrThrow 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.core.BOC_NAME import net.corda.testing.node.InMemoryMessagingNetwork.ServicePeerAllocationStrategy.RoundRobin import net.corda.testing.node.MockNetwork -import net.corda.testing.node.MockNetwork.MockNode +import net.corda.testing.node.StartedMockNode import net.corda.testing.node.startFlow import org.junit.After import org.junit.Before @@ -20,7 +19,7 @@ import kotlin.test.assertFailsWith class CashIssueFlowTests { private lateinit var mockNet: MockNetwork - private lateinit var bankOfCordaNode: StartedNode + private lateinit var bankOfCordaNode: StartedMockNode private lateinit var bankOfCorda: Party private lateinit var notary: Party 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 6380a4b9a6..2d3b901f9a 100644 --- a/finance/src/test/kotlin/net/corda/finance/flows/CashPaymentFlowTests.kt +++ b/finance/src/test/kotlin/net/corda/finance/flows/CashPaymentFlowTests.kt @@ -9,11 +9,10 @@ import net.corda.core.utilities.getOrThrow 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.core.* import net.corda.testing.node.InMemoryMessagingNetwork.ServicePeerAllocationStrategy.RoundRobin import net.corda.testing.node.MockNetwork -import net.corda.testing.node.MockNetwork.MockNode +import net.corda.testing.node.StartedMockNode import net.corda.testing.node.startFlow import org.junit.After import org.junit.Before @@ -25,9 +24,9 @@ class CashPaymentFlowTests { private lateinit var mockNet: MockNetwork private val initialBalance = 2000.DOLLARS private val ref = OpaqueBytes.of(0x01) - private lateinit var bankOfCordaNode: StartedNode + private lateinit var bankOfCordaNode: StartedMockNode private lateinit var bankOfCorda: Party - private lateinit var aliceNode: StartedNode + private lateinit var aliceNode: StartedMockNode @Before fun start() { 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 7bb503d744..11016c4bef 100644 --- a/node/src/integration-test/kotlin/net/corda/node/BootTests.kt +++ b/node/src/integration-test/kotlin/net/corda/node/BootTests.kt @@ -1,6 +1,7 @@ package net.corda.node import co.paralleluniverse.fibers.Suspendable +import net.corda.client.rpc.CordaRPCClient import net.corda.core.flows.FlowLogic import net.corda.core.flows.StartableByRPC import net.corda.core.internal.div @@ -25,7 +26,7 @@ class BootTests { fun `java deserialization is disabled`() { driver { val user = User("u", "p", setOf(startFlow())) - val future = startNode(rpcUsers = listOf(user)).getOrThrow().rpcClientToNode(). + val future = CordaRPCClient(startNode(rpcUsers = listOf(user)).getOrThrow().rpcAddress). start(user.username, user.password).proxy.startFlow(::ObjectInputStreamFlow).returnValue assertThatThrownBy { future.getOrThrow() }.isInstanceOf(InvalidClassException::class.java).hasMessage("filter status: REJECTED") } @@ -37,7 +38,7 @@ class BootTests { assertThat(logConfigFile).isRegularFile() driver(isDebug = true, systemProperties = mapOf("log4j.configurationFile" to logConfigFile.toString())) { val alice = startNode(providedName = ALICE_NAME).get() - val logFolder = alice.configuration.baseDirectory / NodeStartup.LOGS_DIRECTORY_NAME + val logFolder = alice.baseDirectory / NodeStartup.LOGS_DIRECTORY_NAME val logFile = logFolder.toFile().listFiles { _, name -> name.endsWith(".log") }.single() // Start second Alice, should fail assertThatThrownBy { 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 94f0034278..fad710317b 100644 --- a/node/src/integration-test/kotlin/net/corda/node/CordappScanningDriverTest.kt +++ b/node/src/integration-test/kotlin/net/corda/node/CordappScanningDriverTest.kt @@ -1,6 +1,7 @@ package net.corda.node import co.paralleluniverse.fibers.Suspendable +import net.corda.client.rpc.CordaRPCClient import net.corda.core.flows.* import net.corda.core.identity.Party import net.corda.core.internal.concurrent.transpose @@ -25,7 +26,7 @@ class CordappScanningDriverTest { val (alice, bob) = listOf( startNode(providedName = ALICE_NAME, rpcUsers = listOf(user)), startNode(providedName = BOB_NAME)).transpose().getOrThrow() - val initiatedFlowClass = alice.rpcClientToNode() + val initiatedFlowClass = CordaRPCClient(alice.rpcAddress) .start(user.username, user.password) .proxy .startFlow(::ReceiveFlow, bob.nodeInfo.chooseIdentity()) 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 594c402ba2..51604c1ea2 100644 --- a/node/src/integration-test/kotlin/net/corda/node/NodePerformanceTests.kt +++ b/node/src/integration-test/kotlin/net/corda/node/NodePerformanceTests.kt @@ -2,6 +2,7 @@ package net.corda.node import co.paralleluniverse.fibers.Suspendable import com.google.common.base.Stopwatch +import net.corda.client.rpc.CordaRPCClient import net.corda.core.flows.FlowLogic import net.corda.core.flows.StartableByRPC import net.corda.core.internal.concurrent.transpose @@ -14,19 +15,18 @@ import net.corda.finance.flows.CashIssueFlow import net.corda.finance.flows.CashPaymentFlow import net.corda.node.services.Permissions.Companion.startFlow import net.corda.testing.core.DUMMY_NOTARY_NAME +import net.corda.testing.driver.InProcess import net.corda.testing.node.User -import net.corda.testing.driver.NodeHandle import net.corda.testing.driver.driver import net.corda.testing.internal.performance.div import net.corda.testing.node.NotarySpec import net.corda.testing.node.internal.InternalDriverDSL +import net.corda.testing.node.internal.internalDriver 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.Ignore import org.junit.Test -import java.lang.management.ManagementFactory import java.util.* import java.util.concurrent.TimeUnit import kotlin.streams.toList @@ -50,7 +50,7 @@ class NodePerformanceTests { driver(startNodesInProcess = true) { val a = startNode(rpcUsers = listOf(User("A", "A", setOf(startFlow())))).get() - a.rpcClientToNode().use("A", "A") { connection -> + CordaRPCClient(a.rpcAddress).use("A", "A") { connection -> val timings = Collections.synchronizedList(ArrayList()) val N = 10000 val overallTiming = Stopwatch.createStarted().apply { @@ -77,11 +77,11 @@ class NodePerformanceTests { @Test fun `empty flow rate`() { - driver(startNodesInProcess = true) { + internalDriver(startNodesInProcess = true) { val a = startNode(rpcUsers = listOf(User("A", "A", setOf(startFlow())))).get() - a as NodeHandle.InProcess - val metricRegistry = startReporter((this as InternalDriverDSL).shutdownManager, a.node.services.monitoringService.metrics) - a.rpcClientToNode().use("A", "A") { connection -> + a as InProcess + val metricRegistry = startReporter(this.shutdownManager, a.services.monitoringService.metrics) + CordaRPCClient(a.rpcAddress).use("A", "A") { connection -> startPublishingFixedRateInjector(metricRegistry, 8, 5.minutes, 2000L / TimeUnit.SECONDS) { connection.proxy.startFlow(::EmptyFlow).returnValue.get() } @@ -92,14 +92,14 @@ class NodePerformanceTests { @Test fun `self pay rate`() { val user = User("A", "A", setOf(startFlow(), startFlow())) - driver( + internalDriver( notarySpecs = listOf(NotarySpec(DUMMY_NOTARY_NAME, rpcUsers = listOf(user))), startNodesInProcess = true, extraCordappPackagesToScan = listOf("net.corda.finance") ) { - val notary = defaultNotaryNode.getOrThrow() as NodeHandle.InProcess - val metricRegistry = startReporter((this as InternalDriverDSL).shutdownManager, notary.node.services.monitoringService.metrics) - notary.rpcClientToNode().use("A", "A") { connection -> + val notary = defaultNotaryNode.getOrThrow() as InProcess + val metricRegistry = startReporter(this.shutdownManager, notary.services.monitoringService.metrics) + CordaRPCClient(notary.rpcAddress).use("A", "A") { connection -> println("ISSUING") val doneFutures = (1..100).toList().parallelStream().map { connection.proxy.startFlow(::CashIssueFlow, 1.DOLLARS, OpaqueBytes.of(0), defaultNotaryIdentity).returnValue 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 c5a036f09f..e972d71468 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 @@ -18,7 +18,6 @@ import net.corda.core.transactions.TransactionBuilder import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.Try import net.corda.core.utilities.getOrThrow -import net.corda.node.internal.StartedNode import net.corda.node.services.config.BFTSMaRtConfiguration import net.corda.node.services.config.NotaryConfig import net.corda.node.services.transactions.minClusterSize @@ -31,7 +30,7 @@ import net.corda.testing.common.internal.testNetworkParameters import net.corda.testing.contracts.DummyContract import net.corda.testing.core.dummyCommand import net.corda.testing.node.MockNetwork -import net.corda.testing.node.MockNetwork.MockNode +import net.corda.testing.node.StartedMockNode import net.corda.testing.node.MockNodeParameters import net.corda.testing.node.startFlow import org.junit.After @@ -44,7 +43,7 @@ import kotlin.test.assertTrue class BFTNotaryServiceTests { private lateinit var mockNet: MockNetwork private lateinit var notary: Party - private lateinit var node: StartedNode + private lateinit var node: StartedMockNode @Before fun before() { @@ -154,7 +153,7 @@ class BFTNotaryServiceTests { } } - private fun StartedNode<*>.signInitialTransaction(notary: Party, block: TransactionBuilder.() -> Any?): SignedTransaction { + private fun StartedMockNode.signInitialTransaction(notary: Party, block: TransactionBuilder.() -> Any?): SignedTransaction { return services.signInitialTransaction( TransactionBuilder(notary).apply { addCommand(dummyCommand(services.myInfo.chooseIdentity().owningKey)) 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 aca792c913..639cce9aa9 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 @@ -1,5 +1,6 @@ package net.corda.node.services +import net.corda.client.rpc.CordaRPCClient import net.corda.core.contracts.Amount import net.corda.core.identity.Party import net.corda.core.internal.bufferUntilSubscribed @@ -15,6 +16,7 @@ import net.corda.node.services.Permissions.Companion.invokeRpc import net.corda.node.services.Permissions.Companion.startFlow import net.corda.testing.core.* import net.corda.testing.driver.NodeHandle +import net.corda.testing.driver.OutOfProcess import net.corda.testing.driver.driver import net.corda.testing.node.NotarySpec import net.corda.testing.node.User @@ -26,7 +28,7 @@ import java.util.* class DistributedServiceTests { private lateinit var alice: NodeHandle - private lateinit var notaryNodes: List + private lateinit var notaryNodes: List private lateinit var aliceProxy: CordaRPCOps private lateinit var raftNotaryIdentity: Party private lateinit var notaryStateMachines: Observable> @@ -49,7 +51,7 @@ class DistributedServiceTests { ) { alice = startNode(providedName = ALICE_NAME, rpcUsers = listOf(testUser)).getOrThrow() raftNotaryIdentity = defaultNotaryIdentity - notaryNodes = defaultNotaryHandle.nodeHandles.getOrThrow().map { it as NodeHandle.OutOfProcess } + notaryNodes = defaultNotaryHandle.nodeHandles.getOrThrow().map { it as OutOfProcess } assertThat(notaryNodes).hasSize(3) @@ -62,7 +64,7 @@ class DistributedServiceTests { // Connect to Alice and the notaries fun connectRpc(node: NodeHandle): CordaRPCOps { - val client = node.rpcClientToNode() + val client = CordaRPCClient(node.rpcAddress) return client.start("test", "test").proxy } aliceProxy = connectRpc(alice) 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 6e56ee45d3..b7d3a046fa 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 @@ -10,13 +10,12 @@ import net.corda.core.identity.Party 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.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.core.dummyCommand +import net.corda.testing.driver.InProcess import net.corda.testing.node.ClusterSpec import net.corda.testing.node.NotarySpec import net.corda.testing.node.startFlow @@ -35,7 +34,7 @@ class RaftNotaryServiceTests { extraCordappPackagesToScan = listOf("net.corda.testing.contracts"), notarySpecs = listOf(NotarySpec(notaryName, cluster = ClusterSpec.Raft(clusterSize = 3))) ) { - val bankA = startNode(providedName = DUMMY_BANK_A_NAME).map { (it as NodeHandle.InProcess).node }.getOrThrow() + val bankA = startNode(providedName = DUMMY_BANK_A_NAME).map { (it as InProcess) }.getOrThrow() val inputState = issueState(bankA, defaultNotaryIdentity) val firstTxBuilder = TransactionBuilder(defaultNotaryIdentity) @@ -47,7 +46,7 @@ class RaftNotaryServiceTests { firstSpend.getOrThrow() val secondSpendBuilder = TransactionBuilder(defaultNotaryIdentity).withItems(inputState).run { - val dummyState = DummyContract.SingleOwnerState(0, bankA.info.chooseIdentity()) + val dummyState = DummyContract.SingleOwnerState(0, bankA.services.myInfo.chooseIdentity()) addOutputState(dummyState, DummyContract.PROGRAM_ID) addCommand(dummyCommand(bankA.services.myInfo.chooseIdentity().owningKey)) this @@ -61,11 +60,12 @@ class RaftNotaryServiceTests { } } - private fun issueState(node: StartedNode<*>, notary: Party): StateAndRef<*> { - return node.database.transaction { - val builder = DummyContract.generateInitial(Random().nextInt(), notary, node.info.chooseIdentity().ref(0)) - val stx = node.services.signInitialTransaction(builder) - node.services.recordTransactions(stx) + private fun issueState(nodeHandle: InProcess, notary: Party): StateAndRef<*> { + return nodeHandle.database.transaction { + + val builder = DummyContract.generateInitial(Random().nextInt(), notary, nodeHandle.services.myInfo.chooseIdentity().ref(0)) + val stx = nodeHandle.services.signInitialTransaction(builder) + nodeHandle.services.recordTransactions(stx) StateAndRef(builder.outputStates().first(), StateRef(stx.id, 0)) } } 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 53713ee6d2..a8c0f5d99b 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 @@ -63,7 +63,7 @@ class NetworkMapTest { notarySpecs = emptyList() ) { val alice = startNode(providedName = ALICE_NAME).getOrThrow() - val networkParameters = (alice.configuration.baseDirectory / NETWORK_PARAMS_FILE_NAME) + val networkParameters = (alice.baseDirectory / NETWORK_PARAMS_FILE_NAME) .readAll() .deserialize>() .verified() @@ -147,7 +147,7 @@ class NetworkMapTest { private fun NodeHandle.onlySees(vararg nodes: NodeInfo) { // Make sure the nodes aren't getting the node infos from their additional directories - val nodeInfosDir = configuration.baseDirectory / CordformNode.NODE_INFO_DIRECTORY + val nodeInfosDir = baseDirectory / CordformNode.NODE_INFO_DIRECTORY if (nodeInfosDir.exists()) { assertThat(nodeInfosDir.list { it.toList() }).isEmpty() } 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 index 03387d9a13..bbe96ed7a7 100644 --- 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 @@ -1,5 +1,6 @@ package net.corda.node.services.rpc +import net.corda.client.rpc.CordaRPCClient import net.corda.core.identity.CordaX500Name import net.corda.core.utilities.getOrThrow import net.corda.node.services.Permissions.Companion.all @@ -32,7 +33,7 @@ class RpcSslTest { 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 -> + CordaRPCClient(node.rpcAddress, sslConfiguration = clientSslOptions).start(user.username, user.password).use { connection -> connection.proxy.apply { nodeInfo() successful = true @@ -51,7 +52,7 @@ class RpcSslTest { 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 -> + CordaRPCClient(node.rpcAddress).start(user.username, user.password).use { connection -> connection.proxy.apply { nodeInfo() successful = true 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 23c3a987c3..ec2fd513e6 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 @@ -1,6 +1,7 @@ package net.corda.node.services.statemachine import co.paralleluniverse.fibers.Suspendable +import net.corda.client.rpc.CordaRPCClient import net.corda.core.crypto.SecureHash import net.corda.core.flows.* import net.corda.core.internal.InputStreamAndHash @@ -73,7 +74,7 @@ class LargeTransactionsTest { 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) { + CordaRPCClient(alice.rpcAddress).use(rpcUser.username, rpcUser.password) { val hash1 = it.proxy.uploadAttachment(bigFile1.inputStream) val hash2 = it.proxy.uploadAttachment(bigFile2.inputStream) val hash3 = it.proxy.uploadAttachment(bigFile3.inputStream) 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 3cea7b31e6..be183c8e3b 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 @@ -12,15 +12,13 @@ import net.corda.core.serialization.deserialize import net.corda.core.serialization.serialize import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.seconds -import net.corda.node.internal.Node -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.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.InProcess import net.corda.testing.driver.driver import net.corda.testing.node.ClusterSpec import net.corda.testing.node.NotarySpec @@ -44,13 +42,14 @@ class P2PMessagingTest { } } + @Test fun `distributed service requests are retried if one of the nodes in the cluster goes down without sending a response`() { startDriverWithDistributedService { distributedServiceNodes -> val alice = startAlice() val serviceAddress = alice.services.networkMapCache.run { val notaryParty = notaryIdentities.randomOrNull()!! - alice.network.getAddressOfParty(getPartyInfo(notaryParty)!!) + alice.services.networkService.getAddressOfParty(getPartyInfo(notaryParty)!!) } val responseMessage = "response" @@ -76,7 +75,7 @@ class P2PMessagingTest { val alice = startAlice() val serviceAddress = alice.services.networkMapCache.run { val notaryParty = notaryIdentities.randomOrNull()!! - alice.network.getAddressOfParty(getPartyInfo(notaryParty)!!) + alice.services.networkService.getAddressOfParty(getPartyInfo(notaryParty)!!) } val responseMessage = "response" @@ -89,7 +88,7 @@ class P2PMessagingTest { // Wait until the first request is received crashingNodes.firstRequestReceived.await() // Stop alice's node after we ensured that the first request was delivered and ignored. - alice.dispose() + alice.stop() val numberOfRequestsReceived = crashingNodes.requestsReceived.get() assertThat(numberOfRequestsReceived).isGreaterThanOrEqualTo(1) @@ -99,7 +98,7 @@ class P2PMessagingTest { val aliceRestarted = startAlice() val responseFuture = openFuture() - aliceRestarted.network.runOnNextMessage("test.response") { + aliceRestarted.services.networkService.runOnNextMessage("test.response") { responseFuture.set(it.data.deserialize()) } val response = responseFuture.getOrThrow() @@ -109,15 +108,16 @@ class P2PMessagingTest { } } - private fun startDriverWithDistributedService(dsl: DriverDSL.(List>) -> Unit) { + + private fun startDriverWithDistributedService(dsl: DriverDSL.(List) -> Unit) { driver(startNodesInProcess = true, notarySpecs = listOf(NotarySpec(DISTRIBUTED_SERVICE_NAME, cluster = ClusterSpec.Raft(clusterSize = 2)))) { - dsl(defaultNotaryHandle.nodeHandles.getOrThrow().map { (it as NodeHandle.InProcess).node }) + dsl(defaultNotaryHandle.nodeHandles.getOrThrow().map { (it as InProcess) }) } } - private fun DriverDSL.startAlice(): StartedNode { + private fun DriverDSL.startAlice(): InProcess { return startNode(providedName = ALICE_NAME, customOverrides = mapOf("messageRedeliveryDelaySeconds" to 1)) - .map { (it as NodeHandle.InProcess).node } + .map { (it as InProcess) } .getOrThrow() } @@ -133,7 +133,7 @@ class P2PMessagingTest { * initially set to true. This may be used to simulate scenarios where nodes receive request messages but crash * before sending back a response. */ - private fun simulateCrashingNodes(distributedServiceNodes: List>, responseMessage: String): CrashingNodes { + private fun simulateCrashingNodes(distributedServiceNodes: List, responseMessage: String): CrashingNodes { val crashingNodes = CrashingNodes( requestsReceived = AtomicInteger(0), firstRequestReceived = CountDownLatch(1), @@ -141,8 +141,8 @@ class P2PMessagingTest { ) distributedServiceNodes.forEach { - val nodeName = it.info.chooseIdentity().name - it.network.addMessageHandler("test.request") { netMessage, _ -> + val nodeName = it.services.myInfo.chooseIdentity().name + it.services.networkService.addMessageHandler("test.request") { netMessage, _ -> crashingNodes.requestsReceived.incrementAndGet() crashingNodes.firstRequestReceived.countDown() // The node which receives the first request will ignore all requests @@ -154,21 +154,21 @@ class P2PMessagingTest { } else { println("sending response") val request = netMessage.data.deserialize() - val response = it.network.createMessage("test.response", responseMessage.serialize().bytes) - it.network.send(response, request.replyTo) + val response = it.services.networkService.createMessage("test.response", responseMessage.serialize().bytes) + it.services.networkService.send(response, request.replyTo) } } } return crashingNodes } - private fun assertAllNodesAreUsed(participatingServiceNodes: List>, serviceName: CordaX500Name, originatingNode: StartedNode<*>) { + private fun assertAllNodesAreUsed(participatingServiceNodes: List, serviceName: CordaX500Name, originatingNode: InProcess) { // Setup each node in the distributed service to return back it's NodeInfo so that we can know which node is being used participatingServiceNodes.forEach { node -> - node.respondWith(node.info) + node.respondWith(node.services.myInfo) } val serviceAddress = originatingNode.services.networkMapCache.run { - originatingNode.network.getAddressOfParty(getPartyInfo(getNotary(serviceName)!!)!!) + originatingNode.services.networkService.getAddressOfParty(getPartyInfo(getNotary(serviceName)!!)!!) } val participatingNodes = HashSet() // Try several times so that we can be fairly sure that any node not participating is not due to Artemis' selection @@ -180,23 +180,23 @@ class P2PMessagingTest { break } } - assertThat(participatingNodes).containsOnlyElementsOf(participatingServiceNodes.map(StartedNode<*>::info)) + assertThat(participatingNodes).containsOnlyElementsOf(participatingServiceNodes.map { it.services.myInfo }) } - - private fun StartedNode<*>.respondWith(message: Any) { - network.addMessageHandler("test.request") { netMessage, _ -> + + private fun InProcess.respondWith(message: Any) { + services.networkService.addMessageHandler("test.request") { netMessage, _ -> val request = netMessage.data.deserialize() - val response = network.createMessage("test.response", message.serialize().bytes) - network.send(response, request.replyTo) + val response = services.networkService.createMessage("test.response", message.serialize().bytes) + services.networkService.send(response, request.replyTo) } } - private fun StartedNode<*>.receiveFrom(target: MessageRecipients, retryId: Long? = null): CordaFuture { + private fun InProcess.receiveFrom(target: MessageRecipients, retryId: Long? = null): CordaFuture { val response = openFuture() - network.runOnNextMessage("test.response") { netMessage -> + services.networkService.runOnNextMessage("test.response") { netMessage -> response.set(netMessage.data.deserialize()) } - network.send("test.request", TestRequest(replyTo = network.myAddress), target, retryId = retryId) + services.networkService.send("test.request", TestRequest(replyTo = services.networkService.myAddress), target, retryId = retryId) return response } 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 d711b59ebc..1df94d02d7 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 @@ -1,6 +1,7 @@ package net.corda.test.node import co.paralleluniverse.fibers.Suspendable +import net.corda.client.rpc.CordaRPCClient import net.corda.core.contracts.* import net.corda.core.flows.FinalityFlow import net.corda.core.flows.FlowLogic @@ -47,7 +48,7 @@ class NodeStatePersistenceTests { val nodeName = nodeHandle.nodeInfo.chooseIdentity().name // Ensure the notary node has finished starting up, before starting a flow that needs a notary defaultNotaryNode.getOrThrow() - nodeHandle.rpcClientToNode().start(user.username, user.password).use { + CordaRPCClient(nodeHandle.rpcAddress).start(user.username, user.password).use { it.proxy.startFlow(::SendMessageFlow, message, defaultNotaryIdentity).returnValue.getOrThrow() } nodeHandle.stop() @@ -55,7 +56,7 @@ class NodeStatePersistenceTests { }() val nodeHandle = startNode(providedName = nodeName, rpcUsers = listOf(user)).getOrThrow() - val result = nodeHandle.rpcClientToNode().start(user.username, user.password).use { + val result = CordaRPCClient(nodeHandle.rpcAddress).start(user.username, user.password).use { val page = it.proxy.vaultQuery(MessageState::class.java) page.states.singleOrNull() } @@ -81,7 +82,7 @@ class NodeStatePersistenceTests { val nodeName = nodeHandle.nodeInfo.chooseIdentity().name // Ensure the notary node has finished starting up, before starting a flow that needs a notary defaultNotaryNode.getOrThrow() - nodeHandle.rpcClientToNode().start(user.username, user.password).use { + CordaRPCClient(nodeHandle.rpcAddress).start(user.username, user.password).use { it.proxy.startFlow(::SendMessageFlow, message, defaultNotaryIdentity).returnValue.getOrThrow() } nodeHandle.stop() @@ -89,7 +90,7 @@ class NodeStatePersistenceTests { }() val nodeHandle = startNode(providedName = nodeName, rpcUsers = listOf(user), customOverrides = mapOf("devMode" to "false")).getOrThrow() - val result = nodeHandle.rpcClientToNode().start(user.username, user.password).use { + val result = CordaRPCClient(nodeHandle.rpcAddress).start(user.username, user.password).use { val page = it.proxy.vaultQuery(MessageState::class.java) page.states.singleOrNull() } diff --git a/node/src/test/kotlin/net/corda/node/CordaRPCOpsImplTest.kt b/node/src/test/kotlin/net/corda/node/CordaRPCOpsImplTest.kt index 0214d551f9..75ce1850d3 100644 --- a/node/src/test/kotlin/net/corda/node/CordaRPCOpsImplTest.kt +++ b/node/src/test/kotlin/net/corda/node/CordaRPCOpsImplTest.kt @@ -37,9 +37,9 @@ 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 +import net.corda.testing.node.internal.InternalMockNetwork +import net.corda.testing.node.internal.InternalMockNetwork.MockNode import net.corda.testing.node.testActor import org.apache.commons.io.IOUtils import org.assertj.core.api.Assertions.assertThatExceptionOfType @@ -68,7 +68,7 @@ class CordaRPCOpsImplTest { val testJar = "net/corda/node/testing/test.jar" } - private lateinit var mockNet: MockNetwork + private lateinit var mockNet: InternalMockNetwork private lateinit var aliceNode: StartedNode private lateinit var alice: Party private lateinit var notary: Party @@ -79,7 +79,7 @@ class CordaRPCOpsImplTest { @Before fun setup() { - mockNet = MockNetwork(cordappPackages = listOf("net.corda.finance.contracts.asset")) + mockNet = InternalMockNetwork(cordappPackages = listOf("net.corda.finance.contracts.asset")) aliceNode = mockNet.createNode(MockNodeParameters(legalName = ALICE_NAME)) rpc = SecureCordaRPCOps(aliceNode.services, aliceNode.smm, aliceNode.database, aliceNode.services) CURRENT_RPC_CONTEXT.set(RpcAuthContext(InvocationContext.rpc(testActor()), buildSubject("TEST_USER", emptySet()))) diff --git a/node/src/test/kotlin/net/corda/node/internal/CordaServiceTest.kt b/node/src/test/kotlin/net/corda/node/internal/CordaServiceTest.kt index 2842e9bab3..8e36ef8071 100644 --- a/node/src/test/kotlin/net/corda/node/internal/CordaServiceTest.kt +++ b/node/src/test/kotlin/net/corda/node/internal/CordaServiceTest.kt @@ -15,6 +15,7 @@ import net.corda.finance.DOLLARS import net.corda.finance.flows.CashIssueFlow import net.corda.node.internal.cordapp.DummyRPCFlow import net.corda.testing.node.MockNetwork +import net.corda.testing.node.StartedMockNode import org.junit.After import org.junit.Before import org.junit.Test @@ -75,7 +76,7 @@ class LegacyCordaService(@Suppress("UNUSED_PARAMETER") simpleServiceHub: Service class CordaServiceTest { private lateinit var mockNet: MockNetwork - private lateinit var nodeA: StartedNode + private lateinit var nodeA: StartedMockNode @Before fun start() { 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 0ece39f948..5ee95ed7e7 100644 --- a/node/src/test/kotlin/net/corda/node/internal/NetworkParametersTest.kt +++ b/node/src/test/kotlin/net/corda/node/internal/NetworkParametersTest.kt @@ -27,7 +27,7 @@ class NetworkParametersTest { private val mockNet = MockNetwork( emptyList(), MockNetworkParameters(networkSendManuallyPumped = true), - notarySpecs = listOf(MockNetwork.NotarySpec(DUMMY_NOTARY_NAME))) + notarySpecs = listOf(MockNetworkNotarySpec(DUMMY_NOTARY_NAME))) @After fun tearDown() { 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 e17201cf46..d9d2051e7e 100644 --- a/node/src/test/kotlin/net/corda/node/messaging/TwoPartyTradeFlowTests.kt +++ b/node/src/test/kotlin/net/corda/node/messaging/TwoPartyTradeFlowTests.kt @@ -48,6 +48,8 @@ import net.corda.testing.dsl.TestTransactionDSLInterpreter import net.corda.testing.internal.rigorousMock import net.corda.testing.internal.vault.VaultFiller import net.corda.testing.node.* +import net.corda.testing.node.internal.InternalMockNetwork +import net.corda.testing.node.internal.pumpReceive import org.assertj.core.api.Assertions.assertThat import org.junit.After import org.junit.Before @@ -83,7 +85,7 @@ class TwoPartyTradeFlowTests(private val anonymous: Boolean) { private val DUMMY_NOTARY get() = dummyNotary.party } - private lateinit var mockNet: MockNetwork + private lateinit var mockNet: InternalMockNetwork @Before fun before() { @@ -101,7 +103,7 @@ class TwoPartyTradeFlowTests(private val anonymous: Boolean) { // We run this in parallel threads to help catch any race conditions that may exist. The other tests // we run in the unit test thread exclusively to speed things up, ensure deterministic results and // allow interruption half way through. - mockNet = MockNetwork(threadPerNode = true, cordappPackages = cordappPackages) + mockNet = InternalMockNetwork(threadPerNode = true, cordappPackages = cordappPackages) val ledgerIdentityService = rigorousMock() MockServices(cordappPackages, ledgerIdentityService, MEGA_CORP.name).ledger(DUMMY_NOTARY) { val notaryNode = mockNet.defaultNotaryNode @@ -153,7 +155,7 @@ class TwoPartyTradeFlowTests(private val anonymous: Boolean) { @Test(expected = InsufficientBalanceException::class) fun `trade cash for commercial paper fails using soft locking`() { - mockNet = MockNetwork(threadPerNode = true, cordappPackages = cordappPackages) + mockNet = InternalMockNetwork(threadPerNode = true, cordappPackages = cordappPackages) val ledgerIdentityService = rigorousMock() MockServices(cordappPackages, ledgerIdentityService, MEGA_CORP.name).ledger(DUMMY_NOTARY) { val notaryNode = mockNet.defaultNotaryNode @@ -211,7 +213,7 @@ class TwoPartyTradeFlowTests(private val anonymous: Boolean) { @Test fun `shutdown and restore`() { - mockNet = MockNetwork(cordappPackages = cordappPackages) + mockNet = InternalMockNetwork(cordappPackages = cordappPackages) val ledgerIdentityService = rigorousMock() MockServices(cordappPackages, ledgerIdentityService, MEGA_CORP.name).ledger(DUMMY_NOTARY) { val notaryNode = mockNet.defaultNotaryNode @@ -308,11 +310,10 @@ class TwoPartyTradeFlowTests(private val anonymous: Boolean) { // Creates a mock node with an overridden storage service that uses a RecordingMap, that lets us test the order // of gets and puts. - private fun makeNodeWithTracking( - name: CordaX500Name): StartedNode { + private fun makeNodeWithTracking(name: CordaX500Name): StartedNode { // Create a node in the mock network ... return mockNet.createNode(MockNodeParameters(legalName = name), nodeFactory = { args -> - object : MockNetwork.MockNode(args) { + object : InternalMockNetwork.MockNode(args) { // That constructs a recording tx storage override fun makeTransactionStorage(database: CordaPersistence, transactionCacheSizeBytes: Long): WritableTransactionStorage { return RecordingTransactionStorage(database, super.makeTransactionStorage(database, transactionCacheSizeBytes)) @@ -323,7 +324,7 @@ class TwoPartyTradeFlowTests(private val anonymous: Boolean) { @Test fun `check dependencies of sale asset are resolved`() { - mockNet = MockNetwork(cordappPackages = cordappPackages) + mockNet = InternalMockNetwork(cordappPackages = cordappPackages) val notaryNode = mockNet.defaultNotaryNode val aliceNode = makeNodeWithTracking(ALICE_NAME) val bobNode = makeNodeWithTracking(BOB_NAME) @@ -427,7 +428,7 @@ class TwoPartyTradeFlowTests(private val anonymous: Boolean) { @Test fun `track works`() { - mockNet = MockNetwork(cordappPackages = cordappPackages) + mockNet = InternalMockNetwork(cordappPackages = cordappPackages) val notaryNode = mockNet.defaultNotaryNode val aliceNode = makeNodeWithTracking(ALICE_NAME) val bobNode = makeNodeWithTracking(BOB_NAME) @@ -505,7 +506,7 @@ class TwoPartyTradeFlowTests(private val anonymous: Boolean) { @Test fun `dependency with error on buyer side`() { - mockNet = MockNetwork(cordappPackages = cordappPackages) + mockNet = InternalMockNetwork(cordappPackages = cordappPackages) val ledgerIdentityService = rigorousMock() MockServices(cordappPackages, ledgerIdentityService, MEGA_CORP.name).ledger(DUMMY_NOTARY) { runWithError(ledgerIdentityService, true, false, "at least one cash input") @@ -514,7 +515,7 @@ class TwoPartyTradeFlowTests(private val anonymous: Boolean) { @Test fun `dependency with error on seller side`() { - mockNet = MockNetwork(cordappPackages = cordappPackages) + mockNet = InternalMockNetwork(cordappPackages = cordappPackages) val ledgerIdentityService = rigorousMock() MockServices(cordappPackages, ledgerIdentityService, MEGA_CORP.name).ledger(DUMMY_NOTARY) { runWithError(ledgerIdentityService, false, true, "Issuances have a time-window") @@ -530,8 +531,8 @@ class TwoPartyTradeFlowTests(private val anonymous: Boolean) { private fun runBuyerAndSeller(notary: Party, buyer: Party, - sellerNode: StartedNode, - buyerNode: StartedNode, + sellerNode: StartedNode, + buyerNode: StartedNode, assetToSell: StateAndRef): RunResult { val buyerFlows: Observable> = buyerNode.registerInitiatedFlow(BuyerAcceptor::class.java) val firstBuyerFiber = buyerFlows.toFuture().map { it.stateMachine } 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 bdb1381f57..9851246573 100644 --- a/node/src/test/kotlin/net/corda/node/services/NotaryChangeTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/NotaryChangeTests.kt @@ -12,13 +12,9 @@ import net.corda.core.transactions.TransactionBuilder 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.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 -import net.corda.testing.node.startFlow +import net.corda.testing.node.* import org.assertj.core.api.Assertions.assertThatExceptionOfType import org.junit.After import org.junit.Before @@ -30,9 +26,9 @@ import kotlin.test.assertTrue class NotaryChangeTests { private lateinit var mockNet: MockNetwork - private lateinit var oldNotaryNode: StartedNode - private lateinit var clientNodeA: StartedNode - private lateinit var clientNodeB: StartedNode + private lateinit var oldNotaryNode: StartedMockNode + private lateinit var clientNodeA: StartedMockNode + private lateinit var clientNodeB: StartedMockNode private lateinit var newNotaryParty: Party private lateinit var oldNotaryParty: Party private lateinit var clientA: Party @@ -41,7 +37,7 @@ class NotaryChangeTests { fun setUp() { val oldNotaryName = CordaX500Name("Regulator A", "Paris", "FR") mockNet = MockNetwork( - notarySpecs = listOf(NotarySpec(DUMMY_NOTARY_NAME), NotarySpec(oldNotaryName)), + notarySpecs = listOf(MockNetworkNotarySpec(DUMMY_NOTARY_NAME), MockNetworkNotarySpec(oldNotaryName)), cordappPackages = listOf("net.corda.testing.contracts") ) clientNodeA = mockNet.createNode(MockNodeParameters(legalName = ALICE_NAME)) @@ -145,7 +141,7 @@ class NotaryChangeTests { assertEquals(issued.state, changedNotaryBack.state) } - private fun changeNotary(movedState: StateAndRef, node: StartedNode<*>, newNotary: Party): StateAndRef { + private fun changeNotary(movedState: StateAndRef, node: StartedMockNode, newNotary: Party): StateAndRef { val flow = NotaryChangeFlow(movedState, newNotary) val future = node.services.startFlow(flow) mockNet.runNetwork() @@ -153,7 +149,7 @@ class NotaryChangeTests { return future.getOrThrow() } - private fun moveState(state: StateAndRef, fromNode: StartedNode<*>, toNode: StartedNode<*>): StateAndRef { + private fun moveState(state: StateAndRef, fromNode: StartedMockNode, toNode: StartedMockNode): StateAndRef { val tx = DummyContract.move(state, toNode.info.chooseIdentity()) val stx = fromNode.services.signInitialTransaction(tx) @@ -203,7 +199,7 @@ fun issueState(services: ServiceHub, nodeIdentity: Party, notaryIdentity: Party) return stx.tx.outRef(0) } -fun issueMultiPartyState(nodeA: StartedNode<*>, nodeB: StartedNode<*>, notaryNode: StartedNode<*>, notaryIdentity: Party): StateAndRef { +fun issueMultiPartyState(nodeA: StartedMockNode, nodeB: StartedMockNode, notaryNode: StartedMockNode, notaryIdentity: Party): StateAndRef { val participants = listOf(nodeA.info.chooseIdentity(), nodeB.info.chooseIdentity()) val state = TransactionState( DummyContract.MultiOwnerState(0, participants), 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 4e7405f47c..cc1559cf5b 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 @@ -25,8 +25,9 @@ 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.internal.InternalMockNetwork +import net.corda.testing.node.internal.InternalMockNetwork.MockNode import net.corda.testing.node.startFlow import org.junit.After import org.junit.Assert.* @@ -42,9 +43,9 @@ class ScheduledFlowTests { val SORTING = Sort(listOf(Sort.SortColumn(SortAttribute.Standard(Sort.CommonStateAttribute.STATE_REF_TXN_ID), Sort.Direction.DESC))) } - private lateinit var mockNet: MockNetwork - private lateinit var aliceNode: StartedNode - private lateinit var bobNode: StartedNode + private lateinit var mockNet: InternalMockNetwork + private lateinit var aliceNode: StartedNode + private lateinit var bobNode: StartedNode private lateinit var notary: Party private lateinit var alice: Party private lateinit var bob: Party @@ -102,7 +103,7 @@ class ScheduledFlowTests { @Before fun setup() { - mockNet = MockNetwork(threadPerNode = true, cordappPackages = listOf("net.corda.testing.contracts")) + mockNet = InternalMockNetwork(threadPerNode = true, cordappPackages = listOf("net.corda.testing.contracts")) aliceNode = mockNet.createNode(MockNodeParameters(legalName = ALICE_NAME)) bobNode = mockNet.createNode(MockNodeParameters(legalName = BOB_NAME)) notary = mockNet.defaultNotaryIdentity diff --git a/node/src/test/kotlin/net/corda/node/services/schema/NodeSchemaServiceTest.kt b/node/src/test/kotlin/net/corda/node/services/schema/NodeSchemaServiceTest.kt index 45bd2e5c7d..a7bd4a9b15 100644 --- a/node/src/test/kotlin/net/corda/node/services/schema/NodeSchemaServiceTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/schema/NodeSchemaServiceTest.kt @@ -11,7 +11,7 @@ import net.corda.core.utilities.getOrThrow import net.corda.node.services.api.ServiceHubInternal import net.corda.node.services.schema.NodeSchemaService.NodeCoreV1 import net.corda.node.services.schema.NodeSchemaService.NodeNotaryV1 -import net.corda.testing.driver.NodeHandle +import net.corda.testing.driver.InProcess import net.corda.testing.driver.driver import net.corda.testing.internal.vault.DummyLinearStateSchemaV1 import net.corda.testing.node.MockNetwork @@ -26,7 +26,7 @@ import kotlin.test.assertTrue class NodeSchemaServiceTest { /** - * Note: this test requires explicitly registering custom contract schemas with a MockNode + * Note: this test requires explicitly registering custom contract schemas with a StartedMockNode */ @Test fun `registering custom schemas for testing with MockNode`() { @@ -79,7 +79,7 @@ class NodeSchemaServiceTest { fun `custom schemas are loaded eagerly`() { val expected = setOf("PARENTS", "CHILDREN") val tables = driver(startNodesInProcess = true) { - (defaultNotaryNode.getOrThrow() as NodeHandle.InProcess).node.database.transaction { + (defaultNotaryNode.getOrThrow() as InProcess).database.transaction { session.createNativeQuery("SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES").list() } } 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 e16037795a..da80ff4b2e 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 @@ -34,11 +34,11 @@ 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 -import net.corda.testing.node.MockNetwork -import net.corda.testing.node.MockNetwork.MockNode import net.corda.testing.node.MockNodeParameters +import net.corda.testing.node.internal.InternalMockNetwork +import net.corda.testing.node.internal.InternalMockNetwork.MockNode +import net.corda.testing.node.internal.pumpReceive import net.corda.testing.node.internal.startFlow -import net.corda.testing.node.pumpReceive import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThatThrownBy import org.assertj.core.api.AssertionsForClassTypes.assertThatExceptionOfType @@ -62,7 +62,7 @@ class FlowFrameworkTests { } } - private lateinit var mockNet: MockNetwork + private lateinit var mockNet: InternalMockNetwork private val receivedSessionMessages = ArrayList() private lateinit var aliceNode: StartedNode private lateinit var bobNode: StartedNode @@ -76,7 +76,7 @@ class FlowFrameworkTests { @Before fun start() { - mockNet = MockNetwork( + mockNet = InternalMockNetwork( servicePeerAllocationStrategy = RoundRobin(), cordappPackages = listOf("net.corda.finance.contracts", "net.corda.testing.contracts") ) @@ -145,7 +145,7 @@ class FlowFrameworkTests { val restoredFlow = bobNode.restartAndGetRestoredFlow() assertThat(restoredFlow.receivedPayloads[0]).isEqualTo("Hello") } - + @Test fun `flow loaded from checkpoint will respond to messages from before start`() { aliceNode.registerFlowFactory(ReceiveFlow::class) { InitiatedSendFlow("Hello", it) } 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 c165ab7ef9..02e5e1c1dd 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 @@ -30,6 +30,7 @@ import net.corda.testing.core.chooseIdentity import net.corda.testing.node.MockNetwork import net.corda.testing.internal.rigorousMock import net.corda.testing.node.MockNodeParameters +import net.corda.testing.node.internal.InternalMockNetwork import net.corda.testing.node.startFlow import org.junit.After import org.junit.Test @@ -38,7 +39,7 @@ import java.util.concurrent.atomic.AtomicBoolean import kotlin.reflect.jvm.jvmName import kotlin.test.assertEquals -class NodePair(private val mockNet: MockNetwork) { +class NodePair(private val mockNet: InternalMockNetwork) { private class ServerLogic(private val session: FlowSession, private val running: AtomicBoolean) : FlowLogic() { @Suspendable override fun call() { @@ -81,8 +82,8 @@ class VaultSoftLockManagerTest { private val mockVault = rigorousMock().also { doNothing().whenever(it).softLockRelease(any(), anyOrNull()) } - private val mockNet = MockNetwork(cordappPackages = listOf(ContractImpl::class.packageName), defaultFactory = { args -> - object : MockNetwork.MockNode(args) { + private val mockNet = InternalMockNetwork(cordappPackages = listOf(ContractImpl::class.packageName), defaultFactory = { args -> + object : InternalMockNetwork.MockNode(args) { override fun makeVaultService(keyManagementService: KeyManagementService, stateLoader: StateLoader, hibernateConfig: HibernateConfiguration): VaultServiceInternal { val realVault = super.makeVaultService(keyManagementService, stateLoader, hibernateConfig) return object : VaultServiceInternal by realVault { 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 637efa9d55..e480657b6d 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 @@ -1,5 +1,6 @@ package net.corda.attachmentdemo +import net.corda.client.rpc.CordaRPCClient import net.corda.core.messaging.CordaRPCOps import net.corda.core.utilities.getOrThrow import net.corda.node.services.Permissions.Companion.invokeRpc @@ -9,6 +10,7 @@ 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 +import net.corda.testing.driver.internal.NodeHandleInternal import org.junit.Test import java.util.concurrent.CompletableFuture.supplyAsync @@ -30,17 +32,17 @@ class AttachmentDemoTest { startNode(providedName = DUMMY_BANK_A_NAME, rpcUsers = demoUser, maximumHeapSize = "1g"), startNode(providedName = DUMMY_BANK_B_NAME, rpcUsers = demoUser, maximumHeapSize = "1g") ).map { it.getOrThrow() } - startWebserver(nodeB).getOrThrow() + val webserverHandle = startWebserver(nodeB).getOrThrow() val senderThread = supplyAsync { - nodeA.rpcClientToNode().start(demoUser[0].username, demoUser[0].password).use { + CordaRPCClient(nodeA.rpcAddress).start(demoUser[0].username, demoUser[0].password).use { sender(it.proxy, numOfExpectedBytes) } } val recipientThread = supplyAsync { - nodeB.rpcClientToNode().start(demoUser[0].username, demoUser[0].password).use { - recipient(it.proxy, nodeB.webAddress.port) + CordaRPCClient(nodeB.rpcAddress).start(demoUser[0].username, demoUser[0].password).use { + recipient(it.proxy, webserverHandle.listenAddress.port) } } 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 dfa5199b13..c10a266934 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 @@ -1,5 +1,6 @@ package net.corda.bank +import net.corda.client.rpc.CordaRPCClient import net.corda.core.messaging.CordaRPCOps import net.corda.core.messaging.startFlow import net.corda.core.node.services.Vault @@ -37,11 +38,11 @@ class BankOfCordaRPCClientTest { ).map { it.getOrThrow() } // Bank of Corda RPC Client - val bocClient = nodeBankOfCorda.rpcClientToNode() + val bocClient = CordaRPCClient(nodeBankOfCorda.rpcAddress) val bocProxy = bocClient.start("bocManager", "password1").proxy // Big Corporation RPC Client - val bigCorpClient = nodeBigCorporation.rpcClientToNode() + val bigCorpClient = CordaRPCClient(nodeBigCorporation.rpcAddress) val bigCorpProxy = bigCorpClient.start("bigCorpCFO", "password2").proxy // Register for Bank of Corda Vault updates 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 9a4c73a772..02e258e1e8 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 @@ -15,6 +15,7 @@ import net.corda.core.identity.CordaX500Name import net.corda.core.identity.Party import net.corda.core.messaging.vaultTrackBy import net.corda.core.toFuture +import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.contextLogger import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.seconds @@ -78,7 +79,7 @@ class IRSDemoTest { registerIRSModule(mapper) HttpApi.fromHostAndPort(it.second, "api/irs", mapper = mapper) } - val nextFixingDates = getFixingDateObservable(nodeA.configuration) + val nextFixingDates = getFixingDateObservable(nodeA.rpcAddress) val numADeals = getTradeCount(nodeAApi) val numBDeals = getTradeCount(nodeBApi) @@ -102,8 +103,8 @@ class IRSDemoTest { return getTrades(nodeApi)[0].calculation.floatingLegPaymentSchedule.count { it.value.rate.ratioUnit != null } } - private fun getFixingDateObservable(config: NodeConfiguration): Observable { - val client = CordaRPCClient(config.rpcOptions.address!!) + private fun getFixingDateObservable(address: NetworkHostAndPort): Observable { + val client = CordaRPCClient(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 4d05a61d85..f425688c59 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 @@ -7,6 +7,7 @@ import net.corda.testing.driver.DriverParameters import net.corda.testing.driver.NodeHandle import net.corda.testing.driver.PortAllocation import net.corda.testing.driver.WebserverHandle +import net.corda.testing.driver.internal.NodeHandleInternal import net.corda.testing.node.NotarySpec import net.corda.testing.node.internal.* import okhttp3.OkHttpClient @@ -68,12 +69,12 @@ data class SpringBootDriverDSL(private val driverDSL: DriverDSLImpl) : InternalD val debugPort = if (driverDSL.isDebug) driverDSL.debugPortAllocation.nextPort() else null val process = startApplication(handle, debugPort, clazz) driverDSL.shutdownManager.registerProcessShutdown(process) - val webReadyFuture = addressMustBeBoundFuture(driverDSL.executorService, handle.webAddress, process) + val webReadyFuture = addressMustBeBoundFuture(driverDSL.executorService, (handle as NodeHandleInternal).webAddress, process) return webReadyFuture.map { queryWebserver(handle, process, checkUrl) } } private fun queryWebserver(handle: NodeHandle, process: Process, checkUrl: String): WebserverHandle { - val protocol = if (handle.useHTTPS) "https://" else "http://" + val protocol = if ((handle as NodeHandleInternal).useHTTPS) "https://" else "http://" val url = URL(URL("$protocol${handle.webAddress}"), checkUrl) val client = OkHttpClient.Builder().connectTimeout(5, TimeUnit.SECONDS).readTimeout(10, TimeUnit.SECONDS).build() @@ -102,19 +103,19 @@ data class SpringBootDriverDSL(private val driverDSL: DriverDSLImpl) : InternalD className = className, // cannot directly get class for this, so just use string jdwpPort = debugPort, extraJvmArguments = listOf( - "-Dname=node-${handle.configuration.p2pAddress}-webserver", + "-Dname=node-${handle.p2pAddress}-webserver", "-Djava.io.tmpdir=${System.getProperty("java.io.tmpdir")}" // Inherit from parent process ), classpath = ProcessUtilities.defaultClassPath, - workingDirectory = handle.configuration.baseDirectory, + workingDirectory = handle.baseDirectory, errorLogPath = Paths.get("error.$className.log"), arguments = listOf( - "--base-directory", handle.configuration.baseDirectory.toString(), - "--server.port=${handle.webAddress.port}", - "--corda.host=${handle.configuration.rpcOptions.address}", - "--corda.user=${handle.configuration.rpcUsers.first().username}", - "--corda.password=${handle.configuration.rpcUsers.first().password}" + "--base-directory", handle.baseDirectory.toString(), + "--server.port=${(handle as NodeHandleInternal).webAddress.port}", + "--corda.host=${handle.rpcAddress}", + "--corda.user=${handle.rpcUsers.first().username}", + "--corda.password=${handle.rpcUsers.first().password}" ), maximumHeapSize = null ) 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 bbf21b1ece..9d370c22f6 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 @@ -19,6 +19,7 @@ import net.corda.node.services.statemachine.* import net.corda.testing.core.chooseIdentity import net.corda.testing.node.InMemoryMessagingNetwork import net.corda.testing.node.MockNetwork +import net.corda.testing.node.internal.InternalMockNetwork import rx.Scheduler import rx.schedulers.Schedulers import java.time.format.DateTimeFormatter @@ -105,8 +106,8 @@ class NetworkMapVisualiser : Application() { } // Fire the message bullets between nodes. simulation.mockNet.messagingNetwork.sentMessages.observeOn(uiThread).subscribe { msg: InMemoryMessagingNetwork.MessageTransfer -> - val senderNode: MockNetwork.MockNode = simulation.mockNet.addressToNode(msg.sender) - val destNode: MockNetwork.MockNode = simulation.mockNet.addressToNode(msg.recipients) + val senderNode: InternalMockNetwork.MockNode = simulation.mockNet.addressToNode(msg.sender) + val destNode: InternalMockNetwork.MockNode = simulation.mockNet.addressToNode(msg.recipients) if (transferIsInteresting(msg)) { viewModel.nodesToWidgets[senderNode]!!.pulseAnim.play() @@ -114,7 +115,7 @@ class NetworkMapVisualiser : Application() { } } // Pulse all parties in a trade when the trade completes - simulation.doneSteps.observeOn(uiThread).subscribe { nodes: Collection -> + simulation.doneSteps.observeOn(uiThread).subscribe { nodes: Collection -> nodes.forEach { viewModel.nodesToWidgets[it]!!.longPulseAnim.play() } } 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 cb4fa0c488..5ba0385023 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 @@ -13,7 +13,7 @@ import net.corda.finance.utils.ScreenCoordinate import net.corda.netmap.simulation.IRSSimulation import net.corda.netmap.simulation.place import net.corda.testing.core.chooseIdentity -import net.corda.testing.node.MockNetwork +import net.corda.testing.node.internal.InternalMockNetwork import java.util.* class VisualiserViewModel { @@ -25,10 +25,10 @@ class VisualiserViewModel { } } - inner class NodeWidget(val node: MockNetwork.MockNode, val innerDot: Circle, val outerDot: Circle, val longPulseDot: Circle, + inner class NodeWidget(val node: InternalMockNetwork.MockNode, val innerDot: Circle, val outerDot: Circle, val longPulseDot: Circle, val pulseAnim: Animation, val longPulseAnim: Animation, val nameLabel: Label, val statusLabel: Label) { - fun position(nodeCoords: (node: MockNetwork.MockNode) -> ScreenCoordinate) { + fun position(nodeCoords: (node: InternalMockNetwork.MockNode) -> ScreenCoordinate) { val (x, y) = nodeCoords(node) innerDot.centerX = x innerDot.centerY = y @@ -47,7 +47,7 @@ class VisualiserViewModel { val trackerBoxes = HashMap() val doneTrackers = ArrayList() - val nodesToWidgets = HashMap() + val nodesToWidgets = HashMap() var bankCount: Int = 0 var serviceCount: Int = 0 @@ -78,7 +78,7 @@ class VisualiserViewModel { } } - fun nodeMapCoords(node: MockNetwork.MockNode): ScreenCoordinate { + fun nodeMapCoords(node: InternalMockNetwork.MockNode): ScreenCoordinate { // For an image of the whole world, we use: // return node.place.coordinate.project(mapImage.fitWidth, mapImage.fitHeight, 85.0511, -85.0511, -180.0, 180.0) @@ -128,7 +128,7 @@ class VisualiserViewModel { } } - fun makeNodeWidget(forNode: MockNetwork.MockNode, type: String, label: CordaX500Name = CordaX500Name(organisation = "Bank of Bologna", locality = "Bologna", country = "IT"), + fun makeNodeWidget(forNode: InternalMockNetwork.MockNode, type: String, label: CordaX500Name = CordaX500Name(organisation = "Bank of Bologna", locality = "Bologna", country = "IT"), nodeType: NetworkMapVisualiser.NodeType, index: Int): NodeWidget { fun emitRadarPulse(initialRadius: Double, targetRadius: Double, duration: Double): Pair { val pulse = Circle(initialRadius).apply { @@ -180,7 +180,7 @@ class VisualiserViewModel { return widget } - fun fireBulletBetweenNodes(senderNode: MockNetwork.MockNode, destNode: MockNetwork.MockNode, startType: String, endType: String) { + fun fireBulletBetweenNodes(senderNode: InternalMockNetwork.MockNode, destNode: InternalMockNetwork.MockNode, startType: String, endType: String) { val sx = nodesToWidgets[senderNode]!!.innerDot.centerX val sy = nodesToWidgets[senderNode]!!.innerDot.centerY val dx = nodesToWidgets[destNode]!!.innerDot.centerX 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 8fafa86192..66ba4e7cbd 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 @@ -10,9 +10,12 @@ import net.corda.irs.api.NodeInterestRates import net.corda.node.internal.StartedNode import net.corda.node.services.statemachine.StateMachineManager import net.corda.testing.core.TestIdentity -import net.corda.testing.node.* -import net.corda.testing.node.MockNetwork.MockNode +import net.corda.testing.node.InMemoryMessagingNetwork +import net.corda.testing.node.MockNodeParameters import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties +import net.corda.testing.node.TestClock +import net.corda.testing.node.internal.InternalMockNetwork +import net.corda.testing.node.internal.MockNodeArgs import rx.Observable import rx.subjects.PublishSubject import java.math.BigInteger @@ -24,7 +27,7 @@ import java.util.concurrent.CompletableFuture import java.util.concurrent.CompletableFuture.allOf import java.util.concurrent.Future -internal val MockNode.place get() = configuration.myLegalName.locality.let { CityDatabase[it] }!! +internal val InternalMockNetwork.MockNode.place get() = configuration.myLegalName.locality.let { CityDatabase[it] }!! /** * Base class for network simulations that are based on the unit test / mock environment. @@ -50,7 +53,7 @@ abstract class Simulation(val networkSendManuallyPumped: Boolean, val bankLocations = listOf(Pair("London", "GB"), Pair("Frankfurt", "DE"), Pair("Rome", "IT")) - class RatesOracleNode(args: MockNodeArgs) : MockNode(args) { + class RatesOracleNode(args: MockNodeArgs) : InternalMockNetwork.MockNode(args) { companion object { // TODO: Make a more realistic legal name val RATES_SERVICE_NAME = CordaX500Name(organisation = "Rates Service Provider", locality = "Madrid", country = "ES") @@ -67,7 +70,7 @@ abstract class Simulation(val networkSendManuallyPumped: Boolean, } } - val mockNet = MockNetwork( + val mockNet = InternalMockNetwork( networkSendManuallyPumped = networkSendManuallyPumped, threadPerNode = runAsync, cordappPackages = listOf("net.corda.finance.contract", "net.corda.irs")) @@ -77,8 +80,8 @@ abstract class Simulation(val networkSendManuallyPumped: Boolean, val regulators = listOf(mockNet.createUnstartedNode(defaultParams.copy(legalName = DUMMY_REGULATOR.name))) val ratesOracle = mockNet.createUnstartedNode(defaultParams.copy(legalName = RatesOracleNode.RATES_SERVICE_NAME), ::RatesOracleNode) // All nodes must be in one of these two lists for the purposes of the visualiser tool. - val serviceProviders: List = listOf(mockNet.defaultNotaryNode.internals, ratesOracle) - val banks: List = bankLocations.mapIndexed { i, (city, country) -> + val serviceProviders: List = listOf(mockNet.defaultNotaryNode.internals, ratesOracle) + val banks: List = bankLocations.mapIndexed { i, (city, country) -> val legalName = CordaX500Name(organisation = "Bank ${'A' + i}", locality = city, country = country) // Use deterministic seeds so the simulation is stable. Needed so that party owning keys are stable. mockNet.createUnstartedNode(defaultParams.copy(legalName = legalName, entropyRoot = BigInteger.valueOf(i.toLong()))) @@ -86,12 +89,12 @@ abstract class Simulation(val networkSendManuallyPumped: Boolean, val clocks = (serviceProviders + regulators + banks).map { it.platformClock as TestClock } // These are used from the network visualiser tool. - private val _allFlowSteps = PublishSubject.create>() - private val _doneSteps = PublishSubject.create>() + private val _allFlowSteps = PublishSubject.create>() + private val _doneSteps = PublishSubject.create>() @Suppress("unused") - val allFlowSteps: Observable> = _allFlowSteps + val allFlowSteps: Observable> = _allFlowSteps @Suppress("unused") - val doneSteps: Observable> = _doneSteps + val doneSteps: Observable> = _doneSteps private var pumpCursor = 0 @@ -120,7 +123,7 @@ abstract class Simulation(val networkSendManuallyPumped: Boolean, * A place for simulations to stash human meaningful text about what the node is "thinking", which might appear * in the UI somewhere. */ - val extraNodeLabels: MutableMap = Collections.synchronizedMap(HashMap()) + val extraNodeLabels: MutableMap = Collections.synchronizedMap(HashMap()) /** * Iterates the simulation by one step. @@ -151,7 +154,7 @@ abstract class Simulation(val networkSendManuallyPumped: Boolean, return null } - protected fun showProgressFor(nodes: List>) { + protected fun showProgressFor(nodes: List>) { nodes.forEach { node -> node.smm.changes.filter { it is StateMachineManager.Change.Add }.subscribe { linkFlowProgress(node.internals, it.logic) @@ -159,7 +162,7 @@ abstract class Simulation(val networkSendManuallyPumped: Boolean, } } - private fun linkFlowProgress(node: MockNode, flow: FlowLogic<*>) { + private fun linkFlowProgress(node: InternalMockNetwork.MockNode, flow: FlowLogic<*>) { val pt = flow.progressTracker ?: return pt.changes.subscribe { change: ProgressTracker.Change -> // Runs on node thread. @@ -168,14 +171,14 @@ abstract class Simulation(val networkSendManuallyPumped: Boolean, } - protected fun showConsensusFor(nodes: List) { + protected fun showConsensusFor(nodes: List) { val node = nodes.first() node.started!!.smm.changes.filter { it is StateMachineManager.Change.Add }.first().subscribe { linkConsensus(nodes, it.logic) } } - private fun linkConsensus(nodes: Collection, flow: FlowLogic<*>) { + private fun linkConsensus(nodes: Collection, flow: FlowLogic<*>) { flow.progressTracker?.changes?.subscribe { _: ProgressTracker.Change -> // Runs on node thread. if (flow.progressTracker!!.currentStep == ProgressTracker.DONE) { 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 10e20e070c..f76f400ef3 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 @@ -12,7 +12,7 @@ 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.InProcess import net.corda.testing.driver.driver import net.corda.testing.node.User import net.corda.testing.node.internal.poll @@ -37,14 +37,14 @@ class TraderDemoTest { startNode(providedName = DUMMY_BANK_A_NAME, rpcUsers = listOf(demoUser)), startNode(providedName = DUMMY_BANK_B_NAME, rpcUsers = listOf(demoUser)), startNode(providedName = BOC_NAME, rpcUsers = listOf(bankUser)) - ).map { (it.getOrThrow() as NodeHandle.InProcess).node } + ).map { (it.getOrThrow() as InProcess) } nodeA.registerInitiatedFlow(BuyerFlow::class.java) val (nodeARpc, nodeBRpc) = listOf(nodeA, nodeB).map { - val client = CordaRPCClient(it.internals.configuration.rpcOptions.address!!) + val client = CordaRPCClient(it.rpcAddress) client.start(demoUser.username, demoUser.password).proxy } val nodeBankRpc = let { - val client = CordaRPCClient(bankNode.internals.configuration.rpcOptions.address!!) + val client = CordaRPCClient(bankNode.rpcAddress) client.start(bankUser.username, bankUser.password).proxy } @@ -56,8 +56,8 @@ class TraderDemoTest { val expectedBCash = clientB.cashCount + 1 val expectedPaper = listOf(clientA.commercialPaperCount + 1, clientB.commercialPaperCount) - clientBank.runIssuer(amount = 100.DOLLARS, buyerName = nodeA.info.chooseIdentity().name, sellerName = nodeB.info.chooseIdentity().name) - clientB.runSeller(buyerName = nodeA.info.chooseIdentity().name, amount = 5.DOLLARS) + clientBank.runIssuer(amount = 100.DOLLARS, buyerName = nodeA.services.myInfo.chooseIdentity().name, sellerName = nodeB.services.myInfo.chooseIdentity().name) + clientB.runSeller(buyerName = nodeA.services.myInfo.chooseIdentity().name, amount = 5.DOLLARS) assertThat(clientA.cashCount).isGreaterThan(originalACash) assertThat(clientB.cashCount).isEqualTo(expectedBCash) 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 9778fbd44e..bf3bdf648e 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 @@ -15,6 +15,7 @@ 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.driver.internal.NodeHandleInternal import net.corda.testing.http.HttpApi import net.corda.testing.node.NotarySpec import org.assertj.core.api.Assertions.assertThat @@ -27,10 +28,11 @@ class DriverTests { private companion object { 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.single() // Check that the port is bound - addressMustBeBound(executorService, hostAndPort, (this as? NodeHandle.OutOfProcess)?.process) + addressMustBeBound(executorService, hostAndPort, (this as? OutOfProcess)?.process) } fun nodeMustBeDown(handle: NodeHandle) { @@ -80,7 +82,7 @@ class DriverTests { val logConfigFile = projectRootDir / "config" / "dev" / "log4j2.xml" assertThat(logConfigFile).isRegularFile() driver(isDebug = true, systemProperties = mapOf("log4j.configurationFile" to logConfigFile.toString())) { - val baseDirectory = startNode(providedName = DUMMY_BANK_A_NAME).getOrThrow().configuration.baseDirectory + val baseDirectory = startNode(providedName = DUMMY_BANK_A_NAME).getOrThrow().baseDirectory val logFile = (baseDirectory / NodeStartup.LOGS_DIRECTORY_NAME).list { it.sorted().findFirst().get() } val debugLinesPresent = logFile.readLines { lines -> lines.anyMatch { line -> line.startsWith("[DEBUG]") } } assertThat(debugLinesPresent).isTrue() @@ -105,7 +107,7 @@ class DriverTests { // First check that the process-id file is created by the node on startup, so that we can be sure our check that // it's deleted on shutdown isn't a false-positive. driver { - val baseDirectory = defaultNotaryNode.getOrThrow().configuration.baseDirectory + val baseDirectory = defaultNotaryNode.getOrThrow().baseDirectory assertThat(baseDirectory / "process-id").exists() } diff --git a/testing/node-driver/src/integration-test/kotlin/net/corda/testing/node/FlowStackSnapshotTest.kt b/testing/node-driver/src/integration-test/kotlin/net/corda/testing/node/FlowStackSnapshotTest.kt index e2c03d66a1..423b14c014 100644 --- a/testing/node-driver/src/integration-test/kotlin/net/corda/testing/node/FlowStackSnapshotTest.kt +++ b/testing/node-driver/src/integration-test/kotlin/net/corda/testing/node/FlowStackSnapshotTest.kt @@ -2,6 +2,7 @@ package net.corda.testing.node import co.paralleluniverse.fibers.Suspendable import net.corda.client.jackson.JacksonSupport +import net.corda.client.rpc.CordaRPCClient import net.corda.core.flows.* import net.corda.core.internal.div import net.corda.core.internal.list @@ -228,7 +229,7 @@ class FlowStackSnapshotTest { fun `flowStackSnapshot contains full frames when methods with side effects are called`() { driver(startNodesInProcess = true) { val a = startNode(rpcUsers = listOf(User(Constants.USER, Constants.PASSWORD, setOf(startFlow())))).get() - a.rpcClientToNode().use(Constants.USER, Constants.PASSWORD) { connection -> + CordaRPCClient(a.rpcAddress).use(Constants.USER, Constants.PASSWORD) { connection -> val stackSnapshotFrames = connection.proxy.startFlow(::SideEffectFlow).returnValue.get() val iterator = stackSnapshotFrames.listIterator() assertFrame("run", false, iterator.next()) @@ -243,7 +244,7 @@ class FlowStackSnapshotTest { fun `flowStackSnapshot contains empty frames when methods with no side effects are called`() { driver(startNodesInProcess = true) { val a = startNode(rpcUsers = listOf(User(Constants.USER, Constants.PASSWORD, setOf(startFlow())))).get() - a.rpcClientToNode().use(Constants.USER, Constants.PASSWORD) { connection -> + CordaRPCClient(a.rpcAddress).use(Constants.USER, Constants.PASSWORD) { connection -> val stackSnapshotFrames = connection.proxy.startFlow(::NoSideEffectFlow).returnValue.get() val iterator = stackSnapshotFrames.listIterator() assertFrame("run", false, iterator.next()) @@ -258,10 +259,9 @@ class FlowStackSnapshotTest { fun `persistFlowStackSnapshot persists empty frames to a file when methods with no side effects are called`() { driver(startNodesInProcess = true) { val a = startNode(rpcUsers = listOf(User(Constants.USER, Constants.PASSWORD, setOf(startFlow())))).get() - - a.rpcClientToNode().use(Constants.USER, Constants.PASSWORD) { connection -> + CordaRPCClient(a.rpcAddress).use(Constants.USER, Constants.PASSWORD) { connection -> val flowId = connection.proxy.startFlow(::PersistingNoSideEffectFlow).returnValue.get() - val snapshotFromFile = readFlowStackSnapshotFromDir(a.configuration.baseDirectory, flowId) + val snapshotFromFile = readFlowStackSnapshotFromDir(a.baseDirectory, flowId) val stackSnapshotFrames = convertToStackSnapshotFrames(snapshotFromFile) val iterator = stackSnapshotFrames.listIterator() assertFrame("call", true, iterator.next()) @@ -276,10 +276,10 @@ class FlowStackSnapshotTest { driver(startNodesInProcess = true) { val a = startNode(rpcUsers = listOf(User(Constants.USER, Constants.PASSWORD, setOf(startFlow())))).get() - a.rpcClientToNode().use(Constants.USER, Constants.PASSWORD) { connection -> + CordaRPCClient(a.rpcAddress).use(Constants.USER, Constants.PASSWORD) { connection -> val numberOfFlowSnapshots = 5 val flowId = connection.proxy.startFlow(::MultiplePersistingSideEffectFlow, 5).returnValue.get() - val fileCount = countFilesInDir(a.configuration.baseDirectory, flowId) + val fileCount = countFilesInDir(a.baseDirectory, flowId) assertEquals(numberOfFlowSnapshots, fileCount) } } @@ -307,9 +307,9 @@ class FlowStackSnapshotTest { driver(startNodesInProcess = true) { val a = startNode(rpcUsers = listOf(User(Constants.USER, Constants.PASSWORD, setOf(startFlow())))).get() - a.rpcClientToNode().use(Constants.USER, Constants.PASSWORD) { connection -> + CordaRPCClient(a.rpcAddress).use(Constants.USER, Constants.PASSWORD) { connection -> val flowId = connection.proxy.startFlow(::PersistingSideEffectFlow).returnValue.get() - val snapshotFromFile = readFlowStackSnapshotFromDir(a.configuration.baseDirectory, flowId) + val snapshotFromFile = readFlowStackSnapshotFromDir(a.baseDirectory, flowId) var inCallCount = 0 var inPersistCount = 0 snapshotFromFile.stackFrames.forEach { 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 index cf82aec871..eee030d580 100644 --- 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 @@ -2,6 +2,7 @@ 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.InternalMockNetwork import net.corda.testing.node.internal.ProcessUtilities.startJavaProcess import org.junit.Test import kotlin.test.assertEquals diff --git a/testing/node-driver/src/integration-test/kotlin/net/corda/testing/node/internal/InternalMockNetworkIntegrationTests.kt b/testing/node-driver/src/integration-test/kotlin/net/corda/testing/node/internal/InternalMockNetworkIntegrationTests.kt new file mode 100644 index 0000000000..b630143a71 --- /dev/null +++ b/testing/node-driver/src/integration-test/kotlin/net/corda/testing/node/internal/InternalMockNetworkIntegrationTests.kt @@ -0,0 +1,26 @@ +package net.corda.testing.node.internal + +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 InternalMockNetworkIntegrationTests { + companion object { + @JvmStatic + fun main(args: Array) { + InternalMockNetwork(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/driver/Driver.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/driver/Driver.kt index 826a49f16c..6a74e2d673 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 @@ -2,9 +2,9 @@ package net.corda.testing.driver -import net.corda.client.rpc.CordaRPCClient import net.corda.core.DoNotImplement import net.corda.core.concurrent.CordaFuture +import net.corda.core.flows.FlowLogic import net.corda.core.identity.CordaX500Name import net.corda.core.identity.Party import net.corda.core.messaging.CordaRPCOps @@ -12,15 +12,17 @@ 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.node.services.api.StartedNodeServices import net.corda.node.services.config.NodeConfiguration import net.corda.node.services.config.VerifierType -import net.corda.nodeapi.internal.config.SSLConfiguration +import net.corda.nodeapi.internal.persistence.CordaPersistence import net.corda.testing.core.DUMMY_NOTARY_NAME 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 +import rx.Observable import java.net.InetSocketAddress import java.net.ServerSocket import java.nio.file.Path @@ -33,73 +35,37 @@ import java.util.concurrent.atomic.AtomicInteger data class NotaryHandle(val identity: Party, val validating: Boolean, val nodeHandles: CordaFuture>) @DoNotImplement -sealed class NodeHandle : AutoCloseable { - abstract val nodeInfo: NodeInfo +interface NodeHandle : AutoCloseable { + 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 * will be added and that will be used. */ - abstract val rpc: CordaRPCOps - abstract val configuration: NodeConfiguration - abstract val webAddress: NetworkHostAndPort - abstract val useHTTPS: Boolean - + val rpc: CordaRPCOps + val p2pAddress: NetworkHostAndPort + val rpcAddress: NetworkHostAndPort + val rpcUsers: List + val baseDirectory: Path /** * Stops the referenced node. */ - abstract fun stop() + fun stop() +} + +@DoNotImplement +interface OutOfProcess : NodeHandle { + val process: Process +} + +@DoNotImplement +interface InProcess : NodeHandle { + val database: CordaPersistence + val services: StartedNodeServices /** - * Closes and stops the node. + * Register a flow that is initiated by another flow */ - override fun close() = stop() - - data class OutOfProcess( - override val nodeInfo: NodeInfo, - override val rpc: CordaRPCOps, - override val configuration: NodeConfiguration, - override val webAddress: NetworkHostAndPort, - override val useHTTPS: Boolean, - val debugPort: Int?, - val process: Process, - private val onStopCallback: () -> Unit - ) : NodeHandle() { - override fun stop() { - with(process) { - destroy() - waitFor() - } - onStopCallback() - } - } - - data class InProcess( - override val nodeInfo: NodeInfo, - override val rpc: CordaRPCOps, - override val configuration: NodeConfiguration, - override val webAddress: NetworkHostAndPort, - override val useHTTPS: Boolean, - val node: StartedNode, - val nodeThread: Thread, - private val onStopCallback: () -> Unit - ) : NodeHandle() { - override fun stop() { - node.dispose() - with(nodeThread) { - interrupt() - join() - } - onStopCallback() - } - } - - /** - * Connects to node through RPC. - * - * @param sslConfiguration specifies SSL options. - */ - @JvmOverloads - fun rpcClientToNode(sslConfiguration: SSLConfiguration? = null): CordaRPCClient = CordaRPCClient(configuration.rpcOptions.address!!, sslConfiguration = sslConfiguration) + fun > registerInitiatedFlow(initiatedFlowClass: Class): Observable } data class WebserverHandle( @@ -137,12 +103,12 @@ data class NodeParameters( val startInSameProcess: Boolean? = null, val maximumHeapSize: String = "200m" ) { - fun setProvidedName(providedName: CordaX500Name?) = copy(providedName = providedName) - fun setRpcUsers(rpcUsers: List) = copy(rpcUsers = rpcUsers) - fun setVerifierType(verifierType: VerifierType) = copy(verifierType = verifierType) - fun setCustomerOverrides(customOverrides: Map) = copy(customOverrides = customOverrides) - fun setStartInSameProcess(startInSameProcess: Boolean?) = copy(startInSameProcess = startInSameProcess) - fun setMaximumHeapSize(maximumHeapSize: String) = copy(maximumHeapSize = maximumHeapSize) + fun setProvidedName(providedName: CordaX500Name?): NodeParameters = copy(providedName = providedName) + fun setRpcUsers(rpcUsers: List): NodeParameters = copy(rpcUsers = rpcUsers) + fun setVerifierType(verifierType: VerifierType): NodeParameters = copy(verifierType = verifierType) + fun setCustomerOverrides(customOverrides: Map): NodeParameters = copy(customOverrides = customOverrides) + fun setStartInSameProcess(startInSameProcess: Boolean?): NodeParameters = copy(startInSameProcess = startInSameProcess) + fun setMaximumHeapSize(maximumHeapSize: String): NodeParameters = copy(maximumHeapSize = maximumHeapSize) } data class JmxPolicy(val startJmxHttpServer: Boolean = false, diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/driver/internal/DriverInternal.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/driver/internal/DriverInternal.kt new file mode 100644 index 0000000000..3f658d6086 --- /dev/null +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/driver/internal/DriverInternal.kt @@ -0,0 +1,74 @@ +package net.corda.testing.driver.internal + +import net.corda.core.flows.FlowLogic +import net.corda.core.messaging.CordaRPCOps +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.node.services.api.StartedNodeServices +import net.corda.node.services.config.NodeConfiguration +import net.corda.nodeapi.internal.persistence.CordaPersistence +import net.corda.testing.driver.InProcess +import net.corda.testing.driver.NodeHandle +import net.corda.testing.driver.OutOfProcess +import net.corda.testing.node.User +import rx.Observable +import java.nio.file.Path + +interface NodeHandleInternal : NodeHandle { + val configuration: NodeConfiguration + val useHTTPS: Boolean + val webAddress: NetworkHostAndPort + override val p2pAddress: NetworkHostAndPort get() = configuration.p2pAddress + override val rpcAddress: NetworkHostAndPort get() = configuration.rpcOptions.address!! + override val baseDirectory: Path get() = configuration.baseDirectory +} + +data class OutOfProcessImpl( + override val nodeInfo: NodeInfo, + override val rpc: CordaRPCOps, + override val configuration: NodeConfiguration, + override val webAddress: NetworkHostAndPort, + override val useHTTPS: Boolean, + val debugPort: Int?, + override val process: Process, + private val onStopCallback: () -> Unit +) : OutOfProcess, NodeHandleInternal { + override val rpcUsers: List = configuration.rpcUsers.map { User(it.username, it.password, it.permissions) } + override fun stop() { + with(process) { + destroy() + waitFor() + } + onStopCallback() + } + + override fun close() = stop() +} + +data class InProcessImpl( + override val nodeInfo: NodeInfo, + override val rpc: CordaRPCOps, + override val configuration: NodeConfiguration, + override val webAddress: NetworkHostAndPort, + override val useHTTPS: Boolean, + private val nodeThread: Thread, + private val onStopCallback: () -> Unit, + private val node: StartedNode +) : InProcess, NodeHandleInternal { + override val database: CordaPersistence get() = node.database + override val services: StartedNodeServices get() = node.services + override val rpcUsers: List = configuration.rpcUsers.map { User(it.username, it.password, it.permissions) } + override fun stop() { + node.dispose() + with(nodeThread) { + interrupt() + join() + } + onStopCallback() + } + + override fun close() = stop() + override fun > registerInitiatedFlow(initiatedFlowClass: Class): Observable = node.registerInitiatedFlow(initiatedFlowClass) +} 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 f1d45b1028..42e5793d75 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 @@ -304,7 +304,7 @@ class InMemoryMessagingNetwork internal constructor( override fun getAddressOfParty(partyInfo: PartyInfo): MessageRecipients { return when (partyInfo) { - is PartyInfo.SingleNode -> peersMapping[partyInfo.party.name] ?: throw IllegalArgumentException("No MockNode for party ${partyInfo.party.name}") + is PartyInfo.SingleNode -> peersMapping[partyInfo.party.name] ?: throw IllegalArgumentException("No StartedMockNode for party ${partyInfo.party.name}") is PartyInfo.DistributedNode -> ServiceHandle(partyInfo.party) } } diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNetwork.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNetwork.kt new file mode 100644 index 0000000000..070fec8ef9 --- /dev/null +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNetwork.kt @@ -0,0 +1,178 @@ +package net.corda.testing.node + +import com.google.common.jimfs.Jimfs +import net.corda.core.concurrent.CordaFuture +import net.corda.core.crypto.random63BitValue +import net.corda.core.flows.FlowLogic +import net.corda.core.identity.CordaX500Name +import net.corda.core.identity.Party +import net.corda.core.node.NodeInfo +import net.corda.node.VersionInfo +import net.corda.node.internal.StartedNode +import net.corda.node.services.api.StartedNodeServices +import net.corda.node.services.config.NodeConfiguration +import net.corda.node.services.messaging.MessagingService +import net.corda.nodeapi.internal.persistence.CordaPersistence +import net.corda.testing.core.DUMMY_NOTARY_NAME +import net.corda.testing.node.internal.InternalMockNetwork +import net.corda.testing.node.internal.setMessagingServiceSpy +import rx.Observable +import java.math.BigInteger +import java.nio.file.Path + +/** + * Extend this class in order to intercept and modify messages passing through the [MessagingService] when using the [InMemoryMessagingNetwork]. + */ +open class MessagingServiceSpy(val messagingService: MessagingService) : MessagingService by messagingService + +/** + * @param entropyRoot the initial entropy value to use when generating keys. Defaults to an (insecure) random value, + * but can be overridden to cause nodes to have stable or colliding identity/service keys. + * @param configOverrides add/override behaviour of the [NodeConfiguration] mock object. + */ +@Suppress("unused") +data class MockNodeParameters( + val forcedID: Int? = null, + val legalName: CordaX500Name? = null, + val entropyRoot: BigInteger = BigInteger.valueOf(random63BitValue()), + val configOverrides: (NodeConfiguration) -> Any? = {}, + val version: VersionInfo = MockServices.MOCK_VERSION_INFO) { + fun setForcedID(forcedID: Int?): MockNodeParameters = copy(forcedID = forcedID) + fun setLegalName(legalName: CordaX500Name?): MockNodeParameters = copy(legalName = legalName) + fun setEntropyRoot(entropyRoot: BigInteger): MockNodeParameters = copy(entropyRoot = entropyRoot) + fun setConfigOverrides(configOverrides: (NodeConfiguration) -> Any?): MockNodeParameters = copy(configOverrides = configOverrides) +} + +/** Helper builder for configuring a [InternalMockNetwork] from Java. */ +@Suppress("unused") +data class MockNetworkParameters( + val networkSendManuallyPumped: Boolean = false, + val threadPerNode: Boolean = false, + val servicePeerAllocationStrategy: InMemoryMessagingNetwork.ServicePeerAllocationStrategy = InMemoryMessagingNetwork.ServicePeerAllocationStrategy.Random(), + val initialiseSerialization: Boolean = true, + val notarySpecs: List = listOf(MockNetworkNotarySpec(DUMMY_NOTARY_NAME)), + val maxTransactionSize: Int = Int.MAX_VALUE) { + fun setNetworkSendManuallyPumped(networkSendManuallyPumped: Boolean): MockNetworkParameters = copy(networkSendManuallyPumped = networkSendManuallyPumped) + fun setThreadPerNode(threadPerNode: Boolean): MockNetworkParameters = copy(threadPerNode = threadPerNode) + fun setServicePeerAllocationStrategy(servicePeerAllocationStrategy: InMemoryMessagingNetwork.ServicePeerAllocationStrategy): MockNetworkParameters = copy(servicePeerAllocationStrategy = servicePeerAllocationStrategy) + fun setInitialiseSerialization(initialiseSerialization: Boolean): MockNetworkParameters = copy(initialiseSerialization = initialiseSerialization) + fun setNotarySpecs(notarySpecs: List): MockNetworkParameters = copy(notarySpecs = notarySpecs) + fun setMaxTransactionSize(maxTransactionSize: Int): MockNetworkParameters = copy(maxTransactionSize = maxTransactionSize) +} + +/** Represents a node configuration for injection via [MockNetworkParameters] **/ +data class MockNetworkNotarySpec(val name: CordaX500Name, val validating: Boolean = true) { + constructor(name: CordaX500Name) : this(name, validating = true) +} + +/** A class that represents an unstarted mock node for testing. **/ +class UnstartedMockNode private constructor(private val node: InternalMockNetwork.MockNode) { + companion object { + internal fun create(node: InternalMockNetwork.MockNode): UnstartedMockNode { + return UnstartedMockNode(node) + } + } + + val id get() : Int = node.id + /** Start the node **/ + fun start() = StartedMockNode.create(node.start()) +} + +/** A class that represents a started mock node for testing. **/ +class StartedMockNode private constructor(private val node: StartedNode) { + companion object { + internal fun create(node: StartedNode): StartedMockNode { + return StartedMockNode(node) + } + } + + val services get() : StartedNodeServices = node.services + val database get() : CordaPersistence = node.database + val id get() : Int = node.internals.id + val info get() : NodeInfo = node.services.myInfo + val network get() : MessagingService = node.network + /** Register a flow that is initiated by another flow **/ + fun > registerInitiatedFlow(initiatedFlowClass: Class): Observable = node.registerInitiatedFlow(initiatedFlowClass) + + /** + * Attach a [MessagingServiceSpy] to the [InternalMockNetwork.MockNode] allowing + * interception and modification of messages. + */ + fun setMessagingServiceSpy(messagingServiceSpy: MessagingServiceSpy) = node.setMessagingServiceSpy(messagingServiceSpy) + + /** Stop the node **/ + fun stop() = node.internals.stop() + + /** Receive a message from the queue. */ + fun pumpReceive(block: Boolean = false): InMemoryMessagingNetwork.MessageTransfer? { + return (services.networkService as InMemoryMessagingNetwork.TestMessagingService).pumpReceive(block) + } + + /** Returns the currently live flows of type [flowClass], and their corresponding result future. */ + fun > findStateMachines(flowClass: Class): List>> = node.smm.findStateMachines(flowClass) +} + +/** + * A mock node brings up a suite of in-memory services in a fast manner suitable for unit testing. + * Components that do IO are either swapped out for mocks, or pointed to a [Jimfs] in memory filesystem or an in + * memory H2 database instance. + * + * Mock network nodes require manual pumping by default: they will not run asynchronous. This means that + * for message exchanges to take place (and associated handlers to run), you must call the [runNetwork] + * method. + * + * You can get a printout of every message sent by using code like: + * + * LogHelper.setLevel("+messages") + * + * By default a single notary node is automatically started, which forms part of the network parameters for all the nodes. + * This node is available by calling [defaultNotaryNode]. + */ +open class MockNetwork( + val cordappPackages: List, + val defaultParameters: MockNetworkParameters = MockNetworkParameters(), + val networkSendManuallyPumped: Boolean = defaultParameters.networkSendManuallyPumped, + val threadPerNode: Boolean = defaultParameters.threadPerNode, + val servicePeerAllocationStrategy: InMemoryMessagingNetwork.ServicePeerAllocationStrategy = defaultParameters.servicePeerAllocationStrategy, + val initialiseSerialization: Boolean = defaultParameters.initialiseSerialization, + val notarySpecs: List = defaultParameters.notarySpecs, + val maxTransactionSize: Int = defaultParameters.maxTransactionSize) { + @JvmOverloads + constructor(cordappPackages: List, parameters: MockNetworkParameters = MockNetworkParameters()) : this(cordappPackages, defaultParameters = parameters) + + private val internalMockNetwork: InternalMockNetwork = InternalMockNetwork(cordappPackages, defaultParameters, networkSendManuallyPumped, threadPerNode, servicePeerAllocationStrategy, initialiseSerialization, notarySpecs, maxTransactionSize) + val defaultNotaryNode get() : StartedMockNode = StartedMockNode.create(internalMockNetwork.defaultNotaryNode) + val defaultNotaryIdentity get() : Party = internalMockNetwork.defaultNotaryIdentity + val notaryNodes get() : List = internalMockNetwork.notaryNodes.map { StartedMockNode.create(it) } + val nextNodeId get() : Int = internalMockNetwork.nextNodeId + + /** Create a started node with the given identity. **/ + fun createPartyNode(legalName: CordaX500Name? = null): StartedMockNode = StartedMockNode.create(internalMockNetwork.createPartyNode(legalName)) + + /** Create a started node with the given parameters. **/ + fun createNode(parameters: MockNodeParameters = MockNodeParameters()): StartedMockNode = StartedMockNode.create(internalMockNetwork.createNode(parameters)) + + /** Create an unstarted node with the given parameters. **/ + fun createUnstartedNode(parameters: MockNodeParameters = MockNodeParameters()): UnstartedMockNode = UnstartedMockNode.create(internalMockNetwork.createUnstartedNode(parameters)) + + /** Start all nodes that aren't already started. **/ + fun startNodes() = internalMockNetwork.startNodes() + + /** Stop all nodes. **/ + fun stopNodes() = internalMockNetwork.stopNodes() + + /** Block until all scheduled activity, active flows and network activity has ceased. **/ + fun waitQuiescent() = internalMockNetwork.waitQuiescent() + + /** + * Asks every node in order to process any queued up inbound messages. This may in turn result in nodes + * sending more messages to each other, thus, a typical usage is to call runNetwork with the [rounds] + * parameter set to -1 (the default) which simply runs as many rounds as necessary to result in network + * stability (no nodes sent any messages in the last round). + */ + @JvmOverloads + fun runNetwork(rounds: Int = -1) = internalMockNetwork.runNetwork(rounds) + + /** Get the base directory for the given node id. **/ + fun baseDirectory(nodeId: Int): Path = internalMockNetwork.baseDirectory(nodeId) +} \ No newline at end of file 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 bdd40ada5a..3f7bc3d6b5 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 @@ -44,6 +44,9 @@ import net.corda.testing.core.BOB_NAME import net.corda.testing.core.DUMMY_BANK_A_NAME import net.corda.testing.core.setGlobalSerialization import net.corda.testing.driver.* +import net.corda.testing.driver.internal.InProcessImpl +import net.corda.testing.driver.internal.NodeHandleInternal +import net.corda.testing.driver.internal.OutOfProcessImpl import net.corda.testing.node.ClusterSpec import net.corda.testing.node.MockServices.Companion.MOCK_VERSION_INFO import net.corda.testing.node.NotarySpec @@ -355,7 +358,7 @@ class DriverDSLImpl( } private fun queryWebserver(handle: NodeHandle, process: Process): WebserverHandle { - val protocol = if (handle.useHTTPS) "https://" else "http://" + val protocol = if ((handle as NodeHandleInternal).useHTTPS) "https://" else "http://" val url = URL("$protocol${handle.webAddress}/api/status") val client = OkHttpClient.Builder().connectTimeout(5, TimeUnit.SECONDS).readTimeout(60, TimeUnit.SECONDS).build() @@ -375,7 +378,7 @@ class DriverDSLImpl( val debugPort = if (isDebug) debugPortAllocation.nextPort() else null val process = startWebserver(handle, debugPort, maximumHeapSize) shutdownManager.registerProcessShutdown(process) - val webReadyFuture = addressMustBeBoundFuture(executorService, handle.webAddress, process) + val webReadyFuture = addressMustBeBoundFuture(executorService, (handle as NodeHandleInternal).webAddress, process) return webReadyFuture.map { queryWebserver(handle, process) } } @@ -654,7 +657,7 @@ class DriverDSLImpl( return nodeAndThreadFuture.flatMap { (node, thread) -> establishRpc(config, openFuture()).flatMap { rpc -> allNodesConnected(rpc).map { - NodeHandle.InProcess(rpc.nodeInfo(), rpc, config.corda, webAddress, useHTTPS, node, thread, onNodeExit) + InProcessImpl(rpc.nodeInfo(), rpc, config.corda, webAddress, useHTTPS, thread, onNodeExit, node) } } } @@ -683,7 +686,7 @@ class DriverDSLImpl( } processDeathFuture.cancel(false) log.info("Node handle is ready. NodeInfo: ${rpc.nodeInfo()}, WebAddress: $webAddress") - NodeHandle.OutOfProcess(rpc.nodeInfo(), rpc, config.corda, webAddress, useHTTPS, debugPort, process, onNodeExit) + OutOfProcessImpl(rpc.nodeInfo(), rpc, config.corda, webAddress, useHTTPS, debugPort, process, onNodeExit) } } } @@ -833,10 +836,10 @@ class DriverDSLImpl( val className = "net.corda.webserver.WebServer" return ProcessUtilities.startCordaProcess( className = className, // cannot directly get class for this, so just use string - arguments = listOf("--base-directory", handle.configuration.baseDirectory.toString()), + arguments = listOf("--base-directory", handle.baseDirectory.toString()), jdwpPort = debugPort, extraJvmArguments = listOf( - "-Dname=node-${handle.configuration.p2pAddress}-webserver", + "-Dname=node-${handle.p2pAddress}-webserver", "-Djava.io.tmpdir=${System.getProperty("java.io.tmpdir")}" // Inherit from parent process ), errorLogPath = Paths.get("error.$className.log"), 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/internal/InternalMockNetwork.kt similarity index 80% rename from testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNode.kt rename to testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/InternalMockNetwork.kt index 5db5bb6fcd..a60dc7d13d 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/internal/InternalMockNetwork.kt @@ -1,4 +1,4 @@ -package net.corda.testing.node +package net.corda.testing.node.internal import com.google.common.jimfs.Configuration.unix import com.google.common.jimfs.Jimfs @@ -7,6 +7,7 @@ import com.nhaarman.mockito_kotlin.whenever import net.corda.core.DoNotImplement import net.corda.core.crypto.Crypto import net.corda.core.crypto.random63BitValue +import net.corda.core.flows.FlowLogic import net.corda.core.identity.CordaX500Name import net.corda.core.identity.Party import net.corda.core.identity.PartyAndCertificate @@ -28,6 +29,7 @@ import net.corda.core.utilities.contextLogger import net.corda.core.utilities.seconds import net.corda.node.VersionInfo import net.corda.node.internal.AbstractNode +import net.corda.node.internal.InitiatedFlowFactory import net.corda.node.internal.StartedNode import net.corda.node.internal.cordapp.CordappLoader import net.corda.node.services.api.IdentityServiceInternal @@ -52,8 +54,11 @@ 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.core.setGlobalSerialization +import net.corda.testing.node.* import org.apache.activemq.artemis.utils.ReusableLatch import org.apache.sshd.common.util.security.SecurityUtils +import rx.Observable import rx.internal.schedulers.CachedThreadScheduler import java.math.BigInteger import java.nio.file.Path @@ -63,48 +68,13 @@ import java.time.Clock import java.util.concurrent.TimeUnit import java.util.concurrent.atomic.AtomicInteger -fun StartedNode.pumpReceive(block: Boolean = false): InMemoryMessagingNetwork.MessageTransfer? { +fun StartedNode.pumpReceive(block: Boolean = false): InMemoryMessagingNetwork.MessageTransfer? { return (network as InMemoryMessagingNetwork.TestMessagingService).pumpReceive(block) } -/** Helper builder for configuring a [MockNetwork] from Java. */ -@Suppress("unused") -data class MockNetworkParameters( - val networkSendManuallyPumped: Boolean = false, - val threadPerNode: Boolean = false, - val servicePeerAllocationStrategy: InMemoryMessagingNetwork.ServicePeerAllocationStrategy = InMemoryMessagingNetwork.ServicePeerAllocationStrategy.Random(), - val defaultFactory: (MockNodeArgs) -> MockNetwork.MockNode = MockNetwork::MockNode, - val initialiseSerialization: Boolean = true, - val notarySpecs: List = listOf(MockNetwork.NotarySpec(DUMMY_NOTARY_NAME))) { - fun setNetworkSendManuallyPumped(networkSendManuallyPumped: Boolean) = copy(networkSendManuallyPumped = networkSendManuallyPumped) - fun setThreadPerNode(threadPerNode: Boolean) = copy(threadPerNode = threadPerNode) - fun setServicePeerAllocationStrategy(servicePeerAllocationStrategy: InMemoryMessagingNetwork.ServicePeerAllocationStrategy) = copy(servicePeerAllocationStrategy = servicePeerAllocationStrategy) - fun setDefaultFactory(defaultFactory: (MockNodeArgs) -> MockNetwork.MockNode) = copy(defaultFactory = defaultFactory) - fun setInitialiseSerialization(initialiseSerialization: Boolean) = copy(initialiseSerialization = initialiseSerialization) - fun setNotarySpecs(notarySpecs: List) = copy(notarySpecs = notarySpecs) -} - -/** - * @param entropyRoot the initial entropy value to use when generating keys. Defaults to an (insecure) random value, - * but can be overridden to cause nodes to have stable or colliding identity/service keys. - * @param configOverrides add/override behaviour of the [NodeConfiguration] mock object. - */ -@Suppress("unused") -data class MockNodeParameters( - val forcedID: Int? = null, - val legalName: CordaX500Name? = null, - val entropyRoot: BigInteger = BigInteger.valueOf(random63BitValue()), - val configOverrides: (NodeConfiguration) -> Any? = {}, - val version: VersionInfo = MOCK_VERSION_INFO) { - fun setForcedID(forcedID: Int?) = copy(forcedID = forcedID) - fun setLegalName(legalName: CordaX500Name?) = copy(legalName = legalName) - fun setEntropyRoot(entropyRoot: BigInteger) = copy(entropyRoot = entropyRoot) - fun setConfigOverrides(configOverrides: (NodeConfiguration) -> Any?) = copy(configOverrides = configOverrides) -} - data class MockNodeArgs( val config: NodeConfiguration, - val network: MockNetwork, + val network: InternalMockNetwork, val id: Int, val entropyRoot: BigInteger, val version: VersionInfo = MOCK_VERSION_INFO @@ -126,19 +96,15 @@ data class MockNodeArgs( * By default a single notary node is automatically started, which forms part of the network parameters for all the nodes. * This node is available by calling [defaultNotaryNode]. */ -open class MockNetwork(private val cordappPackages: List, - defaultParameters: MockNetworkParameters = MockNetworkParameters(), - private val networkSendManuallyPumped: Boolean = defaultParameters.networkSendManuallyPumped, - private val threadPerNode: Boolean = defaultParameters.threadPerNode, - servicePeerAllocationStrategy: InMemoryMessagingNetwork.ServicePeerAllocationStrategy = defaultParameters.servicePeerAllocationStrategy, - private val defaultFactory: (MockNodeArgs) -> MockNode = defaultParameters.defaultFactory, - initialiseSerialization: Boolean = defaultParameters.initialiseSerialization, - private val notarySpecs: List = defaultParameters.notarySpecs, - maxTransactionSize: Int = Int.MAX_VALUE) { - /** Helper constructor for creating a [MockNetwork] with custom parameters from Java. */ - @JvmOverloads - constructor(cordappPackages: List, parameters: MockNetworkParameters = MockNetworkParameters()) : this(cordappPackages, defaultParameters = parameters) - +open class InternalMockNetwork(private val cordappPackages: List, + defaultParameters: MockNetworkParameters = MockNetworkParameters(), + val networkSendManuallyPumped: Boolean = defaultParameters.networkSendManuallyPumped, + val threadPerNode: Boolean = defaultParameters.threadPerNode, + servicePeerAllocationStrategy: InMemoryMessagingNetwork.ServicePeerAllocationStrategy = defaultParameters.servicePeerAllocationStrategy, + initialiseSerialization: Boolean = defaultParameters.initialiseSerialization, + val notarySpecs: List = defaultParameters.notarySpecs, + maxTransactionSize: Int = Int.MAX_VALUE, + val defaultFactory: (MockNodeArgs) -> MockNode = InternalMockNetwork::MockNode) { init { // Apache SSHD for whatever reason registers a SFTP FileSystemProvider - which gets loaded by JimFS. // This SFTP support loads BouncyCastle, which we want to avoid. @@ -158,7 +124,7 @@ open class MockNetwork(private val cordappPackages: List, private val serializationEnv = try { setGlobalSerialization(initialiseSerialization) } catch (e: IllegalStateException) { - throw IllegalStateException("Using more than one MockNetwork simultaneously is not supported.", e) + throw IllegalStateException("Using more than one InternalMockNetwork simultaneously is not supported.", e) } private val sharedUserCount = AtomicInteger(0) @@ -329,12 +295,12 @@ open class MockNetwork(private val cordappPackages: List, // 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) - // The MockNode specifically uses EdDSA keys as they are fixed and stored in json files for some tests (e.g IRSSimulation). + // The StartedMockNode specifically uses EdDSA keys as they are fixed and stored in json files for some tests (e.g IRSSimulation). return Crypto.deriveKeyPairFromEntropy(Crypto.EDDSA_ED25519_SHA512, counter) } /** - * MockNetwork will ensure nodes are connected to each other. The nodes themselves + * InternalMockNetwork will ensure nodes are connected to each other. The nodes themselves * won't be able to tell if that happened already or not. */ override fun checkNetworkMapIsInitialized() = Unit @@ -478,20 +444,12 @@ open class MockNetwork(private val cordappPackages: List, busyLatch.await() } - data class NotarySpec(val name: CordaX500Name, val validating: Boolean = true) { - constructor(name: CordaX500Name) : this(name, validating = true) - } } /** - * Extend this class in order to intercept and modify messages passing through the [MessagingService] when using the [InMemoryMessagingNetwork]. + * Attach a [MessagingServiceSpy] to the [InternalMockNetwork.MockNode] allowing interception and modification of messages. */ -open class MessagingServiceSpy(val messagingService: MessagingService) : MessagingService by messagingService - -/** - * Attach a [MessagingServiceSpy] to the [MockNetwork.MockNode] allowing interception and modification of messages. - */ -fun StartedNode.setMessagingServiceSpy(messagingServiceSpy: MessagingServiceSpy) { +fun StartedNode.setMessagingServiceSpy(messagingServiceSpy: MessagingServiceSpy) { internals.setMessagingServiceSpy(messagingServiceSpy) } 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 8d2a8d5486..6354cde0d0 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 @@ -60,4 +60,4 @@ object ProcessUtilities { } val defaultClassPath: String get() = System.getProperty("java.class.path") -} +} \ No newline at end of file diff --git a/testing/node-driver/src/test/java/net/corda/testing/node/MockNodeFactoryInJavaTest.java b/testing/node-driver/src/test/java/net/corda/testing/node/MockNodeFactoryInJavaTest.java index b6b336ec7b..fbf6bb01f4 100644 --- a/testing/node-driver/src/test/java/net/corda/testing/node/MockNodeFactoryInJavaTest.java +++ b/testing/node-driver/src/test/java/net/corda/testing/node/MockNodeFactoryInJavaTest.java @@ -6,22 +6,15 @@ import static java.util.Collections.emptyList; @SuppressWarnings("unused") public class MockNodeFactoryInJavaTest { - private static class CustomNode extends MockNetwork.MockNode { - private CustomNode(@NotNull MockNodeArgs args) { - super(args); - } - } - /** * Does not need to run, only compile. */ @SuppressWarnings("unused") private static void factoryIsEasyToPassInUsingJava() { //noinspection Convert2MethodRef - new MockNetwork(emptyList(), new MockNetworkParameters().setDefaultFactory(args -> new CustomNode(args))); - new MockNetwork(emptyList(), new MockNetworkParameters().setDefaultFactory(CustomNode::new)); + new MockNetwork(emptyList()); + new MockNetwork(emptyList(), new MockNetworkParameters().setInitialiseSerialization(false)); //noinspection Convert2MethodRef - new MockNetwork(emptyList()).createNode(new MockNodeParameters(), args -> new CustomNode(args)); - new MockNetwork(emptyList()).createNode(new MockNodeParameters(), CustomNode::new); + new MockNetwork(emptyList()).createNode(new MockNodeParameters()); } } diff --git a/testing/node-driver/src/test/kotlin/net/corda/testing/node/MockNetworkTests.kt b/testing/node-driver/src/test/kotlin/net/corda/testing/node/internal/InternalMockNetworkTests.kt similarity index 75% rename from testing/node-driver/src/test/kotlin/net/corda/testing/node/MockNetworkTests.kt rename to testing/node-driver/src/test/kotlin/net/corda/testing/node/internal/InternalMockNetworkTests.kt index 10d9358243..478585af17 100644 --- a/testing/node-driver/src/test/kotlin/net/corda/testing/node/MockNetworkTests.kt +++ b/testing/node-driver/src/test/kotlin/net/corda/testing/node/internal/InternalMockNetworkTests.kt @@ -1,18 +1,18 @@ -package net.corda.testing.node +package net.corda.testing.node.internal import net.corda.core.serialization.internal.effectiveSerializationEnv import org.assertj.core.api.Assertions.assertThatThrownBy import org.junit.Test -class MockNetworkTests { +class InternalMockNetworkTests { @Test fun `does not leak serialization env if init fails`() { val e = Exception("didn't work") assertThatThrownBy { - object : MockNetwork(emptyList(), initialiseSerialization = true) { + object : InternalMockNetwork(emptyList(), initialiseSerialization = true) { override fun createNotaries() = throw e } }.isSameAs(e) assertThatThrownBy { effectiveSerializationEnv }.isInstanceOf(IllegalStateException::class.java) } -} +} \ No newline at end of file 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 9480d13b6b..62d9fc9dca 100644 --- a/tools/explorer/src/main/kotlin/net/corda/explorer/ExplorerSimulation.kt +++ b/tools/explorer/src/main/kotlin/net/corda/explorer/ExplorerSimulation.kt @@ -4,6 +4,7 @@ import joptsimple.OptionSet import net.corda.client.mock.ErrorFlowsEventGenerator import net.corda.client.mock.EventGenerator import net.corda.client.mock.Generator +import net.corda.client.rpc.CordaRPCClient import net.corda.client.rpc.CordaRPCConnection import net.corda.core.contracts.Amount import net.corda.core.identity.CordaX500Name @@ -83,7 +84,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.rpcOptions.address}") + println("${it.nodeInfo.legalIdentities.first()} started on ${it.rpcAddress}") } when { @@ -95,19 +96,19 @@ class ExplorerSimulation(private val options: OptionSet) { private fun setUpRPC() { // Register with alice to use alice's RPC proxy to create random events. - val aliceClient = aliceNode.rpcClientToNode() + val aliceClient = CordaRPCClient(aliceNode.rpcAddress) val aliceConnection = aliceClient.start(user.username, user.password) val aliceRPC = aliceConnection.proxy - val bobClient = bobNode.rpcClientToNode() + val bobClient = CordaRPCClient(bobNode.rpcAddress) val bobConnection = bobClient.start(user.username, user.password) val bobRPC = bobConnection.proxy - val issuerClientGBP = issuerNodeGBP.rpcClientToNode() + val issuerClientGBP = CordaRPCClient(issuerNodeGBP.rpcAddress) val issuerGBPConnection = issuerClientGBP.start(manager.username, manager.password) val issuerRPCGBP = issuerGBPConnection.proxy - val issuerClientUSD = issuerNodeUSD.rpcClientToNode() + val issuerClientUSD = CordaRPCClient(issuerNodeUSD.rpcAddress) val issuerUSDConnection = issuerClientUSD.start(manager.username, manager.password) val issuerRPCUSD = issuerUSDConnection.proxy diff --git a/verifier/src/integration-test/kotlin/net/corda/verifier/VerifierDriver.kt b/verifier/src/integration-test/kotlin/net/corda/verifier/VerifierDriver.kt index ad436e30d6..70de05b28e 100644 --- a/verifier/src/integration-test/kotlin/net/corda/verifier/VerifierDriver.kt +++ b/verifier/src/integration-test/kotlin/net/corda/verifier/VerifierDriver.kt @@ -28,6 +28,7 @@ import net.corda.testing.driver.JmxPolicy import net.corda.testing.driver.NodeHandle import net.corda.testing.driver.PortAllocation import net.corda.testing.driver.driver +import net.corda.testing.driver.internal.NodeHandleInternal import net.corda.testing.node.NotarySpec import net.corda.testing.node.internal.* import org.apache.activemq.artemis.api.core.SimpleString @@ -255,7 +256,7 @@ data class VerifierDriverDSL(private val driverDSL: DriverDSLImpl) : InternalDri /** Starts a verifier connecting to the specified node */ fun startVerifier(nodeHandle: NodeHandle): CordaFuture { - return startVerifier(nodeHandle.configuration.p2pAddress) + return startVerifier(nodeHandle.p2pAddress) } /** Starts a verifier connecting to the specified requestor */ @@ -263,8 +264,8 @@ data class VerifierDriverDSL(private val driverDSL: DriverDSLImpl) : InternalDri return startVerifier(verificationRequestorHandle.p2pAddress) } - private fun NodeHandle.connectToNode(closure: (ClientSession) -> A): A { - val transport = ArtemisTcpTransport.tcpTransport(ConnectionDirection.Outbound(), configuration.p2pAddress, configuration) + private fun NodeHandleInternal.connectToNode(closure: (ClientSession) -> A): A { + val transport = ArtemisTcpTransport.tcpTransport(ConnectionDirection.Outbound(), p2pAddress, configuration) val locator = ActiveMQClient.createServerLocatorWithoutHA(transport) val sessionFactory = locator.createSessionFactory() val session = sessionFactory.createSession(NODE_USER, NODE_USER, false, true, true, locator.isPreAcknowledge, locator.ackBatchSize) @@ -277,7 +278,7 @@ data class VerifierDriverDSL(private val driverDSL: DriverDSLImpl) : InternalDri * Waits until [number] verifiers are listening for verification requests coming from the Node. Check * [VerificationRequestorHandle.waitUntilNumberOfVerifiers] for an equivalent for requestors. */ - fun NodeHandle.waitUntilNumberOfVerifiers(number: Int) { + fun NodeHandleInternal.waitUntilNumberOfVerifiers(number: Int) { connectToNode { session -> poll(driverDSL.executorService, "$number verifiers to come online") { if (session.queueQuery(SimpleString(VerifierApi.VERIFICATION_REQUESTS_QUEUE_NAME)).consumerCount >= number) { 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 2138b1876f..6de9c38700 100644 --- a/verifier/src/integration-test/kotlin/net/corda/verifier/VerifierTests.kt +++ b/verifier/src/integration-test/kotlin/net/corda/verifier/VerifierTests.kt @@ -15,6 +15,7 @@ import net.corda.node.services.config.VerifierType 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.driver.internal.NodeHandleInternal import net.corda.testing.node.NotarySpec import org.junit.Rule import org.junit.Test @@ -139,7 +140,7 @@ class VerifierTests { notarySpecs = listOf(NotarySpec(DUMMY_NOTARY_NAME, verifierType = VerifierType.OutOfProcess)) ) { val aliceNode = startNode(providedName = ALICE_NAME).getOrThrow() - val notaryNode = defaultNotaryNode.getOrThrow() + val notaryNode = defaultNotaryNode.getOrThrow() as NodeHandleInternal val alice = aliceNode.rpc.wellKnownPartyFromX500Name(ALICE_NAME)!! startVerifier(notaryNode) aliceNode.rpc.startFlow(::CashIssueFlow, 10.DOLLARS, OpaqueBytes.of(0), defaultNotaryIdentity).returnValue.get() From 1218fa037e14538ba89a535cb7cbee0012feb65e Mon Sep 17 00:00:00 2001 From: Maksymilian Pawlak <120831+m4ksio@users.noreply.github.com> Date: Mon, 12 Feb 2018 13:10:03 +0100 Subject: [PATCH 04/10] Allows configuring maxParallelForks as system property (#2482) * Allows configuring maxParallelForks as system property --- build.gradle | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/build.gradle b/build.gradle index 09199763b3..b20d78d4d3 100644 --- a/build.gradle +++ b/build.gradle @@ -168,6 +168,10 @@ allprojects { tasks.withType(Test) { // Prevent the project from creating temporary files outside of the build directory. systemProperties['java.io.tmpdir'] = buildDir + + if (System.getProperty("test.maxParallelForks") != null) { + maxParallelForks = Integer.valueOf(System.getProperty("test.maxParallelForks")) + } } group 'net.corda' From d1627fed5bcf64327229dbed52ab5b32b0e07fa3 Mon Sep 17 00:00:00 2001 From: Michal Kit Date: Mon, 12 Feb 2018 12:12:16 +0000 Subject: [PATCH 05/10] Backporting introduction of constants for the dev certs from enterprise (#2502) --- .../nodeapi/internal/KeyStoreConfigHelpers.kt | 10 +++++++--- .../nodeapi/internal/crypto/X509Utilities.kt | 2 +- .../resources/certificates/cordadevcakeys.jks | Bin 2146 -> 2274 bytes .../certificates/cordatruststore.jks | Bin 1170 -> 631 bytes .../node/services/config/ConfigUtilities.kt | 8 ++++---- 5 files changed, 12 insertions(+), 8 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 70c32ed6cd..644ceeb711 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 @@ -95,14 +95,18 @@ fun createDevNodeCa(intermediateCa: CertificateAndKeyPair, } val DEV_INTERMEDIATE_CA: CertificateAndKeyPair get() = DevCaHelper.loadDevCa(X509Utilities.CORDA_INTERMEDIATE_CA) - val DEV_ROOT_CA: CertificateAndKeyPair get() = DevCaHelper.loadDevCa(X509Utilities.CORDA_ROOT_CA) +const val DEV_CA_PRIVATE_KEY_PASS: String = "cordacadevkeypass" +const val DEV_CA_KEY_STORE_FILE: String = "cordadevcakeys.jks" +const val DEV_CA_KEY_STORE_PASS: String = "cordacadevpass" +const val DEV_CA_TRUST_STORE_FILE: String = "cordatruststore.jks" +const val DEV_CA_TRUST_STORE_PASS: String = "trustpass" // We need a class so that we can get hold of the class loader internal object DevCaHelper { fun loadDevCa(alias: String): CertificateAndKeyPair { // TODO: Should be identity scheme - val caKeyStore = loadKeyStore(javaClass.classLoader.getResourceAsStream("certificates/cordadevcakeys.jks"), "cordacadevpass") - return caKeyStore.getCertificateAndKeyPair(alias, "cordacadevkeypass") + val caKeyStore = loadKeyStore(javaClass.classLoader.getResourceAsStream("certificates/$DEV_CA_KEY_STORE_FILE"), DEV_CA_KEY_STORE_PASS) + return caKeyStore.getCertificateAndKeyPair(alias, DEV_CA_PRIVATE_KEY_PASS) } } 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 0b545290df..5b103222a3 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 @@ -156,7 +156,7 @@ object X509Utilities { val builder = JcaX509v3CertificateBuilder(issuer, serial, validityWindow.first, validityWindow.second, subject, subjectPublicKey) .addExtension(Extension.subjectKeyIdentifier, false, BcX509ExtensionUtils().createSubjectKeyIdentifier(subjectPublicKeyInfo)) - .addExtension(Extension.basicConstraints, certificateType.isCA, BasicConstraints(certificateType.isCA)) + .addExtension(Extension.basicConstraints, true, BasicConstraints(certificateType.isCA)) .addExtension(Extension.keyUsage, false, certificateType.keyUsage) .addExtension(Extension.extendedKeyUsage, false, keyPurposes) diff --git a/node-api/src/main/resources/certificates/cordadevcakeys.jks b/node-api/src/main/resources/certificates/cordadevcakeys.jks index af5ff8fce8ec35a10697322c659e76613c8b25dd..3dc11f1fc59841b78a2c1052d03c3e0ffe7966d0 100644 GIT binary patch literal 2274 zcmezO_TO6u1_mZ5W@Hdf&M!(y%*-oEEy_(z$xJLsO-^KBU`%xPm^u%r=!!w(MFT!I zE^RhO7Dg>5K}JSa2A0Ns({2g>jc<%vcJgrb1-r|7D?I$!4xF3tOFP8DXWFE>p0bG> zJ^nwPH}lj|&3!d1|FG(Um;rBnI$i`WUj*5 zl4U&){_`n0d;)u(HA2tSz!De~o(4@!t_Dqv4;C;pF)}f6gdXH(FPU?$%s_;VL#xf> zoGlA86O_fw$Zn8e$Zf#M#vIDRCd?EXY$#~J58|*3bNHo}D@5iOWgGGt@PLH4g;||J zVQVM`GzrM(66OsuR`AHrNpa3s@bPhmi8BlHg4HPm<>!|uI6E51iSrs-7#JEGKtPl@ zuMv<7#9$C*kW4ifNE(QPT*fRc0C9m!etuDIVjjp5kp{wS>|pORF(Q1=?8Lxg9VxzZ zs`2K{iVr24*w6CSh!{1h-fmuSq@<{#_f^`$*-ZCc(#~jyZ+KS{Vk6u7Fmd@g!5Mc| zxPq6P-AX(&J;iBpxk0IcEYMzAJ{B<+kqP{67MAt?QkNg!`q^~yOs4VE9w%^^$_g_w z{%2t|UY%@>>aa37k45ZjNfC-b8m7N*rMktGkjRh#lU?2!hw9Q>f zjEo?CjMyWT*^|M*jY*MV|GTE^D-UhC`ocZmXzuIzvl=>neq_#+-N*asLHolTpMIc{ zv$D_cO6bp9b??cZqjwfxuhV?~c+cGY9d^li43}Riww`2wrzvA#n$icRsZ-E2Rn(Fc z{rk926?P{Kf;2_@RE;}9bvbATFenPAC

bU-0mjm9M7tXYHCJV$LgElKH$M*hOaV zvCWEH`|9|eB^`pnT08aT8~xfQ^z!_5kw;6^CTOTHPBBPAN>DSO8%$X>H-f2BWJkqx zmG0|unOqIjNKjad16b<$W|!xdbkt^hoN~5eR_>o!-CPSlH9olMa$%8EXI@eMOJM5h zOny7()j_!rWhHSof&7N^4sl(6BA4@Mr62!+zg*?FB7vn6BLg?MI4%N~1n^?G5_d6t zocY08^>33L=6g>N$;1OkREzfBi+`fjC70Q~RdMy}K3q@?_V8$y!CeuI eW*GumM&|2wpZLT5+WJnMzb}~i#{bCiybS)G@-X?+mY|-Tee<)=9*YxsoTi9MSd-heQSZt1j}=LraUJzGWM}< zml8YFvL)v6C9vmNBlJuSEP+8GYS6?aXwbyCV*xW0BNG!xM%DL3%g6r547k`hwAwt* z*|IP*8AKRL8%VM-hq5s92swj7Ou;WdB~>9PKfgr5+0l^OfDR2nHZs7W@cn(c4A;*K7DV2+QYQ8&C``wqYMpM?`!X# zJfkFZZ~8UoC!z^|ZyS^>*ijaEM)v(1=a!FRnG$BpfAgBWt@s@)Yj-3+=vczy6oVuK zS)k)(`B=nQL|(tsGZ58uSSL9{?neIO+IsdyhlCBdLDKvzz(8hfGf)O`R9R#Uq}Vus zNsg73of+sPD2s`W1t`g2zz-L(wx^uY^EADqDSu>qPs3c|OnJe}qB9NF3dOdqsMnYhzMX4;F5?rS5B>z=ZA z)&5f3xi!;SYHj`5z^NZT)hl=@rEQ%2G{7rW{rUgtWuI zC-$8%F*10#z^_&Kvhm3|#mj15q~`zety|`P{7QqOL!U-nBIoYoKTJGC8T)ti{GH3S ztRtP-_(p-A!Rz=30T0gJY1wo6nf=1v`66p14<0`JviSG}+d{V56B`d4eDb&`IZ){J zfmI%BS2Xe7Z~1xQ(DbRf6)VfF_>LU&|0y0YCoUmwRlz$_rV?>%?zWN0ZV4s{D9%TYU+mcEv^Z-rpDJMS+w7K2>`?Qks$y8 diff --git a/node-api/src/main/resources/certificates/cordatruststore.jks b/node-api/src/main/resources/certificates/cordatruststore.jks index dd2c81122ca3749ff15c0ec74a19585db6f97f59..b91e35b8d502389f9529686b08103087b0d34fd0 100644 GIT binary patch literal 631 zcmezO_TO6u1_mY|W(3pR$@xVoiADMOCCQ0Eu|#)|sZ$wPBlJuSEP=|54Vswr4VoBF zEnsG1WMbkdYDtRzecY$YK!lA$tIgw_EekUfl*P=*ZjfNeZNSOK9LmBb%oG}IC}_YB z;;;*I_@$OBMCKP|8}b?OfP}b(S)D=l8j1l;0`j?pd4r4dUQv4j22#E9@Zvl9bLmxE>i zgQ9SXQb6MO1rKjo`D$8!)~-1s=Dflsna?YNU1ati+pNg7ua4hY(jgeEwNr1t(XVYn zFV9~Wd9*}rf`UtD9@#r^W|2T`nwg>dY(3e+g95nf!LntAlbM%1YvF v0{IQ+9pbwDL@wviNKcs*f_M>JkHs&Ff$oM7)l#RvN4CUF!KmGgG^QM z%TGyF2+GedQE+xNIX@9i_W1E38h@;9PV<5%Gq0Pp~%F50RbP|-s#Kr=YWH8_dDH3L6 z{LjK_zzn32BZ}Fb!N7${q3X~+>unEqUt6*3N7#+nhRb`_Pnf~6^E4-G@jZ{PpHF8p zDJYy@ZgR!)!X2*Hv5I#$Zhu>_ZA^!lCdQmUSPuE z1SXsf(1cSEzGdar0uyi(Pl|DSH=Gg;3i<;z>n6K(gq(k=eVbsVo(H9$)+HS1R1-|4}q zE!@dH>zQk-jHNhxF>MkI@1h@G++7>4Pxjw3FUT==fr10)!#z@qcNhmPtQ>nJ!>MKIl Hh`s;-J;OgJ 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 0b2ba8360b..1921632e90 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 @@ -8,8 +8,8 @@ import net.corda.core.identity.CordaX500Name import net.corda.core.internal.createDirectories import net.corda.core.internal.div import net.corda.core.internal.exists +import net.corda.nodeapi.internal.* import net.corda.nodeapi.internal.config.SSLConfiguration -import net.corda.nodeapi.internal.createDevKeyStores import net.corda.nodeapi.internal.crypto.X509KeyStore import net.corda.nodeapi.internal.crypto.loadKeyStore import net.corda.nodeapi.internal.crypto.save @@ -51,7 +51,7 @@ fun NodeConfiguration.configureWithDevSSLCertificate() = configureDevKeyAndTrust fun SSLConfiguration.configureDevKeyAndTrustStores(myLegalName: CordaX500Name) { certificatesDirectory.createDirectories() if (!trustStoreFile.exists()) { - loadKeyStore(javaClass.classLoader.getResourceAsStream("certificates/cordatruststore.jks"), "trustpass").save(trustStoreFile, trustStorePassword) + loadKeyStore(javaClass.classLoader.getResourceAsStream("certificates/$DEV_CA_TRUST_STORE_FILE"), DEV_CA_TRUST_STORE_PASS).save(trustStoreFile, trustStorePassword) } if (!sslKeystore.exists() || !nodeKeystore.exists()) { val (nodeKeyStore) = createDevKeyStores(myLegalName) @@ -59,11 +59,11 @@ fun SSLConfiguration.configureDevKeyAndTrustStores(myLegalName: CordaX500Name) { // Move distributed service composite key (generated by IdentityGenerator.generateToDisk) to keystore if exists. val distributedServiceKeystore = certificatesDirectory / "distributedService.jks" if (distributedServiceKeystore.exists()) { - val serviceKeystore = X509KeyStore.fromFile(distributedServiceKeystore, "cordacadevpass") + val serviceKeystore = X509KeyStore.fromFile(distributedServiceKeystore, DEV_CA_KEY_STORE_PASS) nodeKeyStore.update { serviceKeystore.aliases().forEach { if (serviceKeystore.internal.isKeyEntry(it)) { - setPrivateKey(it, serviceKeystore.getPrivateKey(it, "cordacadevkeypass"), serviceKeystore.getCertificateChain(it)) + setPrivateKey(it, serviceKeystore.getPrivateKey(it, DEV_CA_PRIVATE_KEY_PASS), serviceKeystore.getCertificateChain(it)) } else { setCertificate(it, serviceKeystore.getCertificate(it)) } From ad7b84b5a84031a6f0b8f1bccafe6d732134fa69 Mon Sep 17 00:00:00 2001 From: Joel Dudley Date: Mon, 12 Feb 2018 13:30:47 +0000 Subject: [PATCH 06/10] Removes IdentityServiceInternal as a public parameter to MockServices. --- .../docs/tutorial/testdsl/TutorialTestDSL.kt | 4 ++-- .../net/corda/node/internal/AbstractNode.kt | 4 ++-- .../identity/InMemoryIdentityService.kt | 4 ++-- .../keys/E2ETestKeyManagementService.kt | 4 ++-- .../net/corda/node/services/keys/KMSUtils.kt | 9 ++++++-- .../keys/PersistentKeyManagementService.kt | 4 ++-- .../services/vault/VaultQueryJavaTests.java | 3 ++- .../net/corda/testing/node/MockServices.kt | 21 +++++++++---------- .../node/internal/InternalMockNetwork.kt | 10 ++------- 9 files changed, 31 insertions(+), 32 deletions(-) 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 b0c910e45d..ef7e62920b 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 @@ -5,6 +5,7 @@ import com.nhaarman.mockito_kotlin.whenever import net.corda.core.contracts.TransactionVerificationException import net.corda.core.crypto.generateKeyPair import net.corda.core.identity.CordaX500Name +import net.corda.core.node.services.IdentityService import net.corda.core.utilities.days import net.corda.finance.DOLLARS import net.corda.finance.`issued by` @@ -13,7 +14,6 @@ import net.corda.finance.contracts.CommercialPaper 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.core.* import net.corda.testing.internal.rigorousMock import net.corda.testing.node.MockServices @@ -38,7 +38,7 @@ class CommercialPaperTest { @Rule @JvmField val testSerialization = SerializationEnvironmentRule() - private val ledgerServices = MockServices(emptyList(), rigorousMock().also { + private val ledgerServices = MockServices(emptyList(), rigorousMock().also { doReturn(MEGA_CORP).whenever(it).partyFromKey(MEGA_CORP_PUBKEY) doReturn(null).whenever(it).partyFromKey(BIG_CORP_PUBKEY) doReturn(null).whenever(it).partyFromKey(ALICE_PUBKEY) 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 142150d8d0..105bdc2676 100644 --- a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt +++ b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt @@ -525,7 +525,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(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: IdentityService, networkMapCache: NetworkMapCacheInternal): MutableList { checkpointStorage = DBCheckpointStorage() val metrics = MetricRegistry() attachments = NodeAttachmentService(metrics, configuration.attachmentContentCacheSizeBytes, configuration.attachmentCacheBound) @@ -625,7 +625,7 @@ abstract class AbstractNode(val configuration: NodeConfiguration, } } - protected open fun makeKeyManagementService(identityService: IdentityServiceInternal, keyPairs: Set): KeyManagementService { + protected open fun makeKeyManagementService(identityService: IdentityService, keyPairs: Set): KeyManagementService { return PersistentKeyManagementService(identityService, keyPairs) } 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 d016a56371..92e06f437d 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 @@ -4,11 +4,11 @@ import net.corda.core.contracts.PartyAndReference import net.corda.core.crypto.toStringShort import net.corda.core.identity.* import net.corda.core.internal.CertRole +import net.corda.core.node.services.IdentityService import net.corda.core.node.services.UnknownAnonymousPartyException 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.X509Utilities import net.corda.nodeapi.internal.crypto.x509Certificates import java.security.InvalidAlgorithmParameterException @@ -25,7 +25,7 @@ import javax.annotation.concurrent.ThreadSafe // TODO There is duplicated logic between this and PersistentIdentityService @ThreadSafe class InMemoryIdentityService(identities: Array, - override val trustRoot: X509Certificate) : SingletonSerializeAsToken(), IdentityServiceInternal { + override val trustRoot: X509Certificate) : SingletonSerializeAsToken(), IdentityService { companion object { private val log = contextLogger() } diff --git a/node/src/main/kotlin/net/corda/node/services/keys/E2ETestKeyManagementService.kt b/node/src/main/kotlin/net/corda/node/services/keys/E2ETestKeyManagementService.kt index 34b1c94e46..0c0eb61778 100644 --- a/node/src/main/kotlin/net/corda/node/services/keys/E2ETestKeyManagementService.kt +++ b/node/src/main/kotlin/net/corda/node/services/keys/E2ETestKeyManagementService.kt @@ -3,9 +3,9 @@ package net.corda.node.services.keys import net.corda.core.crypto.* import net.corda.core.identity.PartyAndCertificate import net.corda.core.internal.ThreadBox +import net.corda.core.node.services.IdentityService import net.corda.core.node.services.KeyManagementService import net.corda.core.serialization.SingletonSerializeAsToken -import net.corda.node.services.api.IdentityServiceInternal import org.bouncycastle.operator.ContentSigner import java.security.KeyPair import java.security.PrivateKey @@ -25,7 +25,7 @@ import javax.annotation.concurrent.ThreadSafe * etc. */ @ThreadSafe -class E2ETestKeyManagementService(val identityService: IdentityServiceInternal, +class E2ETestKeyManagementService(val identityService: IdentityService, initialKeys: Set) : SingletonSerializeAsToken(), KeyManagementService { private class InnerState { val keys = HashMap() 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 460845a652..1db8b49e02 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 @@ -3,6 +3,7 @@ package net.corda.node.services.keys import net.corda.core.crypto.Crypto import net.corda.core.identity.PartyAndCertificate import net.corda.core.internal.CertRole +import net.corda.core.node.services.IdentityService import net.corda.core.utilities.days import net.corda.node.services.api.IdentityServiceInternal import net.corda.nodeapi.internal.crypto.CertificateType @@ -27,7 +28,7 @@ import java.time.Duration * @param revocationEnabled whether to check revocation status of certificates in the certificate path. * @return X.509 certificate and path to the trust root. */ -fun freshCertificate(identityService: IdentityServiceInternal, +fun freshCertificate(identityService: IdentityService, subjectPublicKey: PublicKey, issuer: PartyAndCertificate, issuerSigner: ContentSigner, @@ -45,7 +46,11 @@ fun freshCertificate(identityService: IdentityServiceInternal, window) val ourCertPath = X509Utilities.buildCertPath(ourCertificate, issuer.certPath.x509Certificates) val anonymisedIdentity = PartyAndCertificate(ourCertPath) - identityService.justVerifyAndRegisterIdentity(anonymisedIdentity) + if (identityService is IdentityServiceInternal) { + identityService.justVerifyAndRegisterIdentity(anonymisedIdentity) + } else { + identityService.verifyAndRegisterIdentity(anonymisedIdentity) + } return anonymisedIdentity } diff --git a/node/src/main/kotlin/net/corda/node/services/keys/PersistentKeyManagementService.kt b/node/src/main/kotlin/net/corda/node/services/keys/PersistentKeyManagementService.kt index 02018515d7..f31af4f276 100644 --- a/node/src/main/kotlin/net/corda/node/services/keys/PersistentKeyManagementService.kt +++ b/node/src/main/kotlin/net/corda/node/services/keys/PersistentKeyManagementService.kt @@ -2,10 +2,10 @@ package net.corda.node.services.keys import net.corda.core.crypto.* import net.corda.core.identity.PartyAndCertificate +import net.corda.core.node.services.IdentityService import net.corda.core.node.services.KeyManagementService import net.corda.core.serialization.SingletonSerializeAsToken import net.corda.core.utilities.MAX_HASH_HEX_SIZE -import net.corda.node.services.api.IdentityServiceInternal import net.corda.node.utilities.AppendOnlyPersistentMap import net.corda.nodeapi.internal.persistence.NODE_DATABASE_PREFIX import org.apache.commons.lang.ArrayUtils.EMPTY_BYTE_ARRAY @@ -25,7 +25,7 @@ import javax.persistence.Lob * * This class needs database transactions to be in-flight during method calls and init. */ -class PersistentKeyManagementService(val identityService: IdentityServiceInternal, +class PersistentKeyManagementService(val identityService: IdentityService, initialKeys: Set) : SingletonSerializeAsToken(), KeyManagementService { @Entity 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 2729af12f7..6df1039035 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 @@ -9,6 +9,7 @@ import net.corda.core.identity.AbstractParty; import net.corda.core.identity.CordaX500Name; import net.corda.core.identity.Party; import net.corda.core.messaging.DataFeed; +import net.corda.core.node.services.IdentityService; import net.corda.core.node.services.Vault; import net.corda.core.node.services.VaultQueryException; import net.corda.core.node.services.VaultService; @@ -70,7 +71,7 @@ public class VaultQueryJavaTests { @Before public void setUp() throws CertificateException, InvalidAlgorithmParameterException { List cordappPackages = Arrays.asList("net.corda.testing.internal.vault", "net.corda.finance.contracts.asset", CashSchemaV1.class.getPackage().getName()); - IdentityServiceInternal identitySvc = makeTestIdentityService(MEGA_CORP.getIdentity(), DUMMY_CASH_ISSUER_INFO.getIdentity(), DUMMY_NOTARY.getIdentity()); + IdentityService identitySvc = makeTestIdentityService(MEGA_CORP.getIdentity(), DUMMY_CASH_ISSUER_INFO.getIdentity(), DUMMY_NOTARY.getIdentity()); Pair databaseAndServices = makeTestDatabaseAndMockServices( cordappPackages, identitySvc, 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 a33b51879e..51de7c6549 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 @@ -18,7 +18,6 @@ import net.corda.core.transactions.SignedTransaction import net.corda.node.VersionInfo import net.corda.node.internal.configureDatabase import net.corda.node.internal.cordapp.CordappLoader -import net.corda.node.services.api.IdentityServiceInternal import net.corda.node.services.api.SchemaService import net.corda.node.services.api.VaultServiceInternal import net.corda.node.services.api.WritableTransactionStorage @@ -61,7 +60,7 @@ fun makeTestIdentityService(vararg identities: PartyAndCertificate) = InMemoryId open class MockServices private constructor( cordappLoader: CordappLoader, override val validatedTransactions: WritableTransactionStorage, - override val identityService: IdentityServiceInternal, + override val identityService: IdentityService, private val initialIdentity: TestIdentity, private val moreKeys: Array ) : ServiceHub, StateLoader by validatedTransactions { @@ -90,13 +89,13 @@ 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 identityService an instance of [IdentityService], see [makeTestIdentityService]. * @param initialIdentity the first (typically sole) identity the services will represent. * @return a pair where the first element is the instance of [CordaPersistence] and the second is [MockServices]. */ @JvmStatic fun makeTestDatabaseAndMockServices(cordappPackages: List, - identityService: IdentityServiceInternal, + identityService: IdentityService, initialIdentity: TestIdentity, vararg moreKeys: KeyPair): Pair { val cordappLoader = CordappLoader.createWithTestPackages(cordappPackages) @@ -128,7 +127,7 @@ open class MockServices private constructor( } } - private constructor(cordappLoader: CordappLoader, identityService: IdentityServiceInternal, + private constructor(cordappLoader: CordappLoader, identityService: IdentityService, initialIdentity: TestIdentity, moreKeys: Array) : this(cordappLoader, MockTransactionStorage(), identityService, initialIdentity, moreKeys) @@ -137,28 +136,28 @@ open class MockServices private constructor( * (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) + constructor(cordappPackages: List, identityService: IdentityService = 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) + constructor(cordappPackages: List, identityService: IdentityService = 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)) + constructor(cordappPackages: List, identityService: IdentityService = 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(identityService: IdentityServiceInternal = makeTestIdentityService(), initialIdentityName: CordaX500Name, key: KeyPair, vararg moreKeys: KeyPair) + constructor(identityService: IdentityService = makeTestIdentityService(), initialIdentityName: CordaX500Name, key: KeyPair, vararg moreKeys: KeyPair) : this(listOf(getCallerPackage()), identityService, TestIdentity(initialIdentityName, key), *moreKeys) /** @@ -166,7 +165,7 @@ open class MockServices private constructor( * (you can get one from [makeTestIdentityService]) and which represents the given identity. It has no keys. */ @JvmOverloads - constructor(identityService: IdentityServiceInternal = makeTestIdentityService(), initialIdentityName: CordaX500Name) + constructor(identityService: IdentityService = makeTestIdentityService(), initialIdentityName: CordaX500Name) : this(listOf(getCallerPackage()), identityService, TestIdentity(initialIdentityName)) /** @@ -218,7 +217,7 @@ open class MockServices private constructor( override fun registerUnloadHandler(runOnStop: () -> Unit) = throw UnsupportedOperationException() } -class MockKeyManagementService(val identityService: IdentityServiceInternal, +class MockKeyManagementService(val identityService: IdentityService, vararg initialKeys: KeyPair) : SingletonSerializeAsToken(), KeyManagementService { private val keyStore: MutableMap = initialKeys.associateByTo(HashMap(), { it.public }, { it.private }) diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/InternalMockNetwork.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/InternalMockNetwork.kt index a60dc7d13d..83736fed8b 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/InternalMockNetwork.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/InternalMockNetwork.kt @@ -7,7 +7,6 @@ import com.nhaarman.mockito_kotlin.whenever import net.corda.core.DoNotImplement import net.corda.core.crypto.Crypto import net.corda.core.crypto.random63BitValue -import net.corda.core.flows.FlowLogic import net.corda.core.identity.CordaX500Name import net.corda.core.identity.Party import net.corda.core.identity.PartyAndCertificate @@ -29,10 +28,8 @@ import net.corda.core.utilities.contextLogger import net.corda.core.utilities.seconds import net.corda.node.VersionInfo import net.corda.node.internal.AbstractNode -import net.corda.node.internal.InitiatedFlowFactory import net.corda.node.internal.StartedNode import net.corda.node.internal.cordapp.CordappLoader -import net.corda.node.services.api.IdentityServiceInternal import net.corda.node.services.api.SchemaService import net.corda.node.services.config.* import net.corda.node.services.keys.E2ETestKeyManagementService @@ -48,17 +45,14 @@ import net.corda.nodeapi.internal.network.NetworkParametersCopier import net.corda.nodeapi.internal.persistence.CordaPersistence import net.corda.nodeapi.internal.persistence.DatabaseConfig import net.corda.testing.common.internal.testNetworkParameters -import net.corda.testing.core.DUMMY_NOTARY_NAME import net.corda.testing.core.setGlobalSerialization import net.corda.testing.internal.rigorousMock import net.corda.testing.internal.testThreadFactory +import net.corda.testing.node.* import net.corda.testing.node.MockServices.Companion.MOCK_VERSION_INFO import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties -import net.corda.testing.core.setGlobalSerialization -import net.corda.testing.node.* import org.apache.activemq.artemis.utils.ReusableLatch import org.apache.sshd.common.util.security.SecurityUtils -import rx.Observable import rx.internal.schedulers.CachedThreadScheduler import java.math.BigInteger import java.nio.file.Path @@ -280,7 +274,7 @@ open class InternalMockNetwork(private val cordappPackages: List, network = messagingServiceSpy } - override fun makeKeyManagementService(identityService: IdentityServiceInternal, keyPairs: Set): KeyManagementService { + override fun makeKeyManagementService(identityService: IdentityService, keyPairs: Set): KeyManagementService { return E2ETestKeyManagementService(identityService, keyPairs) } From 3f3e0e99730004695b5b428042306a768852832f Mon Sep 17 00:00:00 2001 From: Joel Dudley Date: Mon, 12 Feb 2018 14:00:25 +0000 Subject: [PATCH 07/10] Allows the webserver JAR used by each node in deployNodes to be configured. --- docs/source/changelog.rst | 3 +++ docs/source/generating-a-node.rst | 19 +++++++++++++++ .../src/main/kotlin/net/corda/plugins/Node.kt | 24 +++++++++++++++++-- 3 files changed, 44 insertions(+), 2 deletions(-) diff --git a/docs/source/changelog.rst b/docs/source/changelog.rst index d5fbbcc6cc..d24525247e 100644 --- a/docs/source/changelog.rst +++ b/docs/source/changelog.rst @@ -183,6 +183,9 @@ UNRELEASED * Marked ``stateMachine`` on ``FlowLogic`` as ``CordaInternal`` to make clear that is it not part of the public api and is only for internal use +* Provided experimental support for specifying your own webserver to be used instead of the default development + webserver in ``Cordform`` using the ``webserverJar`` argument + * Created new ``StartedMockNode`` and ``UnstartedMockNode`` classes which are wrappers around our MockNode implementation that expose relevant methods for testing without exposing internals, create these using a ``MockNetwork``. diff --git a/docs/source/generating-a-node.rst b/docs/source/generating-a-node.rst index 99c5022344..68d000f68d 100644 --- a/docs/source/generating-a-node.rst +++ b/docs/source/generating-a-node.rst @@ -137,6 +137,25 @@ a single node to run the network map service, by putting its name in the ``netwo .. warning:: When adding nodes, make sure that there are no port clashes! +Specifying a custom webserver +----------------------------- +By default, any node listing a webport will use the default development webserver, which is not production-ready. You +can use your own webserver JAR instead by using the ``webserverJar`` argument in a ``Cordform`` ``node`` configuration +block: + +.. sourcecode:: groovy + + node { + name "O=PartyA,L=New York,C=US" + webPort 10005 + webserverJar "lib/my_webserver.jar" + } + +The webserver JAR will be copied into the node's ``build`` folder with the name ``corda-webserver.jar``. + +.. warning:: This is an experimental feature. There is currently no support for reading the webserver's port from the + node's ``node.conf`` file. + Running deployNodes ------------------- To create the nodes defined in our ``deployNodes`` task, run the following command in a terminal window from the root 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 59f9a7f6f5..10c408cc71 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 @@ -38,9 +38,10 @@ class Node(private val project: Project) : CordformNode() { private set internal lateinit var containerName: String private set - internal var rpcSettings: RpcSettings = RpcSettings() private set + internal var webserverJar: String? = null + private set /** * Sets whether this node will use HTTPS communication. @@ -79,6 +80,17 @@ class Node(private val project: Project) : CordformNode() { config = config.withValue("sshd.port", ConfigValueFactory.fromAnyRef(sshdPort)) } + /** + * The webserver JAR to be used by this node. + * + * If not provided, the default development webserver is used. + * + * @param webserverJar The file path of the webserver JAR to use. + */ + fun webserverJar(webserverJar: String) { + this.webserverJar = webserverJar + } + internal fun build() { if (config.hasPath("webAddress")) { installWebserverJar() @@ -130,7 +142,15 @@ class Node(private val project: Project) : CordformNode() { * Installs the corda webserver JAR to the node directory */ private fun installWebserverJar() { - val webJar = Cordformation.verifyAndGetRuntimeJar(project, "corda-webserver") + // If no webserver JAR is provided, the default development webserver is used. + val webJar = if (webserverJar == null) { + project.logger.info("Using default development webserver.") + Cordformation.verifyAndGetRuntimeJar(project, "corda-webserver") + } else { + project.logger.info("Using custom webserver: $webserverJar.") + File(webserverJar) + } + project.copy { it.apply { from(webJar) From ddf0d34147393eb54003423c3b0ef038d5e303da Mon Sep 17 00:00:00 2001 From: Patrick Kuo Date: Mon, 12 Feb 2018 15:33:22 +0000 Subject: [PATCH 08/10] CORDA-1008 Samples fail to run due to wrongly configured max transaction size in the network bootstrapper (#2509) * [CORDA-1008] - Samples fail to run due to wrongly configured max transaction size in the network bootstrapper * address PR issues --- .../net/corda/nodeapi/internal/network/NetworkBootstrapper.kt | 2 +- .../net/corda/testing/node/internal/network/NetworkMapServer.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/network/NetworkBootstrapper.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/network/NetworkBootstrapper.kt index d97b86afd6..c8f90fcd74 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/network/NetworkBootstrapper.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/network/NetworkBootstrapper.kt @@ -165,7 +165,7 @@ class NetworkBootstrapper { notaries = notaryInfos, modifiedTime = Instant.now(), maxMessageSize = 10485760, - maxTransactionSize = 40000, + maxTransactionSize = Int.MAX_VALUE, epoch = 1 ), overwriteFile = true) diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/network/NetworkMapServer.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/network/NetworkMapServer.kt index 7b8d6a4e77..dbacebb931 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/network/NetworkMapServer.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/network/NetworkMapServer.kt @@ -39,7 +39,7 @@ class NetworkMapServer(private val cacheTimeout: Duration, private val myHostNameValue: String = "test.host.name", vararg additionalServices: Any) : Closeable { companion object { - private val stubNetworkParameters = NetworkParameters(1, emptyList(), 10485760, 40000, Instant.now(), 10) + private val stubNetworkParameters = NetworkParameters(1, emptyList(), 10485760, Int.MAX_VALUE, Instant.now(), 10) } private val server: Server From 1487c411b43286b4b01b2b7467b598167a0283ec Mon Sep 17 00:00:00 2001 From: Anthony Keenan Date: Mon, 12 Feb 2018 15:53:29 +0000 Subject: [PATCH 09/10] CORDA-716 - Update changelog for test package changes and provide upgrade script (#2434) * Update changelog for test package changes and provide upgrade script * Upgrade kotlin and java scripts --- docs/source/changelog.rst | 6 ++++++ tools/scripts/upgrade-test-packages.sh | 6 ++++++ 2 files changed, 12 insertions(+) create mode 100644 tools/scripts/upgrade-test-packages.sh diff --git a/docs/source/changelog.rst b/docs/source/changelog.rst index d24525247e..b75b26ac85 100644 --- a/docs/source/changelog.rst +++ b/docs/source/changelog.rst @@ -189,6 +189,12 @@ UNRELEASED * Created new ``StartedMockNode`` and ``UnstartedMockNode`` classes which are wrappers around our MockNode implementation that expose relevant methods for testing without exposing internals, create these using a ``MockNetwork``. +* The test utils in ``Expect.kt``, ``SerializationTestHelpers.kt``, ``TestConstants.kt`` and ``TestUtils.kt`` have moved + from the ``net.corda.testing`` package to the ``net.corda.testing.core`` package, and ``FlowStackSnapshot.kt`` has moved to the + ``net.corda.testing.services`` package. Moving items out of the ``net.corda.testing.*`` package will help make it clearer which + parts of the api are stable. The bash script ``tools\scripts\update-test-packages.sh`` can be used to smooth the upgrade + process for existing projects. + .. _changelog_v1: Release 1.0 diff --git a/tools/scripts/upgrade-test-packages.sh b/tools/scripts/upgrade-test-packages.sh new file mode 100644 index 0000000000..1631126a11 --- /dev/null +++ b/tools/scripts/upgrade-test-packages.sh @@ -0,0 +1,6 @@ +#!/bin/bash + +find $1 -type f \( -iname \*.kt -o -iname \*.java \) -exec sed -i " +s/net.corda.testing.\(\*\|generateStateRef\|freeLocalHostAndPort\|getFreeLocalPorts\|getTestPartyAndCertificate\|TestIdentity\|chooseIdentity\|singleIdentity\|TEST_TX_TIME\|DUMMY_NOTARY_NAME\|DUMMY_BANK_A_NAME\|DUMMY_BANK_B_NAME\|DUMMY_BANK_C_NAME\|BOC_NAME\|ALICE_NAME\|BOB_NAME\|CHARLIE_NAME\|DEV_INTERMEDIATE_CA\|DEV_ROOT_CA\|dummyCommand\|DummyCommandData\|MAX_MESSAGE_SIZE\|SerializationEnvironmentRule\|setGlobalSerialization\|expect\|sequence\|parallel\|replicate\|genericExpectEvents\)/net.corda.testing.core.\1/g +s/net.corda.testing.FlowStackSnapshotFactoryImpl/net.corda.testing.services.FlowStackSnapshotFactoryImpl/g +" '{}' \; \ No newline at end of file From fa4b5d16ba6249f85b50851c2f52dc697eebdee6 Mon Sep 17 00:00:00 2001 From: Konstantinos Chalkias Date: Mon, 12 Feb 2018 16:13:04 +0000 Subject: [PATCH 10/10] CORDA-1009 Remove X509EdDSAEngine dependency on X509Key (#2506) --- .../kotlin/net/corda/core/internal/X509EdDSAEngine.kt | 9 ++++----- .../net/corda/core/internal/X509EdDSAEngineTest.kt | 8 ++++++++ 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/core/src/main/kotlin/net/corda/core/internal/X509EdDSAEngine.kt b/core/src/main/kotlin/net/corda/core/internal/X509EdDSAEngine.kt index cd5fac1ee1..5ccee61be5 100644 --- a/core/src/main/kotlin/net/corda/core/internal/X509EdDSAEngine.kt +++ b/core/src/main/kotlin/net/corda/core/internal/X509EdDSAEngine.kt @@ -27,12 +27,11 @@ class X509EdDSAEngine : Signature { override fun engineInitSign(privateKey: PrivateKey, random: SecureRandom) = engine.initSign(privateKey, random) override fun engineInitVerify(publicKey: PublicKey) { - val parsedKey = if (publicKey is sun.security.x509.X509Key) { - EdDSAPublicKey(X509EncodedKeySpec(publicKey.encoded)) - } else { - publicKey + val parsedKey = try { + publicKey as? EdDSAPublicKey ?: EdDSAPublicKey(X509EncodedKeySpec(publicKey.encoded)) + } catch(e: Exception) { + throw (InvalidKeyException(e.message)) } - engine.initVerify(parsedKey) } diff --git a/core/src/test/kotlin/net/corda/core/internal/X509EdDSAEngineTest.kt b/core/src/test/kotlin/net/corda/core/internal/X509EdDSAEngineTest.kt index c4cca75b83..eb5586c483 100644 --- a/core/src/test/kotlin/net/corda/core/internal/X509EdDSAEngineTest.kt +++ b/core/src/test/kotlin/net/corda/core/internal/X509EdDSAEngineTest.kt @@ -114,4 +114,12 @@ class X509EdDSAEngineTest { engine.verify(signature) } } + + /** Verify will fail if the input public key cannot be converted to EdDSA public key. */ + @Test + fun `verify with non-supported key type fails`() { + val engine = EdDSAEngine() + val keyPair = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256K1_SHA256, BigInteger.valueOf(SEED)) + assertFailsWith { engine.initVerify(keyPair.public) } + } } \ No newline at end of file