From d6adb83cd00b6bbe7d8114c3ec9ae39bbf132dc9 Mon Sep 17 00:00:00 2001 From: Katelyn Baker Date: Tue, 31 Oct 2017 15:45:58 +0000 Subject: [PATCH 01/24] CORDA-756 - Refactor Kryo into sub module as per AMQP --- .../net/corda/client/rpc/CordaRPCClient.kt | 1 + .../internal/KryoClientSerializationScheme.kt | 5 + .../internal/serialization/ClientContexts.kt | 10 ++ .../serialization/CordaClassResolver.kt | 1 + .../serialization/SerializationScheme.kt | 129 ----------------- .../internal/serialization/ServerContexts.kt | 33 ++++- .../{ => amqp}/AMQPSerializationScheme.kt | 17 +-- .../{ => kryo}/CordaClosureSerializer.kt | 2 +- .../{ => kryo}/DefaultKryoCustomizer.kt | 6 +- .../internal/serialization/{ => kryo}/Kryo.kt | 6 +- .../kryo/KryoSerializationScheme.kt | 132 ++++++++++++++++++ .../{ => kryo}/SerializeAsTokenSerializer.kt | 2 +- .../ForbiddenLambdaSerializationTests.java | 6 +- .../LambdaCheckpointSerializationTest.java | 4 +- .../serialization/CordaClassResolverTests.kt | 2 + .../internal/serialization/KryoTests.kt | 1 + .../serialization/ListsSerializationTest.kt | 1 + .../serialization/MapsSerializationTest.kt | 3 +- .../serialization/SerializationTokenTest.kt | 4 + .../serialization/SetsSerializationTest.kt | 1 + .../amqp/SerializationOutputTests.kt | 1 - .../kotlin/net/corda/node/internal/Node.kt | 1 + .../KryoServerSerializationScheme.kt | 8 +- .../corda/node/utilities/X509UtilitiesTest.kt | 2 +- .../kotlin/net/corda/testing/CoreTestUtils.kt | 2 +- .../corda/testing/SerializationTestHelpers.kt | 2 + .../kotlin/net/corda/verifier/Verifier.kt | 3 + 27 files changed, 225 insertions(+), 160 deletions(-) rename node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/{ => amqp}/AMQPSerializationScheme.kt (92%) rename node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/{ => kryo}/CordaClosureSerializer.kt (94%) rename node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/{ => kryo}/DefaultKryoCustomizer.kt (97%) rename node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/{ => kryo}/Kryo.kt (98%) create mode 100644 node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/kryo/KryoSerializationScheme.kt rename node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/{ => kryo}/SerializeAsTokenSerializer.kt (96%) diff --git a/client/rpc/src/main/kotlin/net/corda/client/rpc/CordaRPCClient.kt b/client/rpc/src/main/kotlin/net/corda/client/rpc/CordaRPCClient.kt index 2def3fbf20..1ef330d3e1 100644 --- a/client/rpc/src/main/kotlin/net/corda/client/rpc/CordaRPCClient.kt +++ b/client/rpc/src/main/kotlin/net/corda/client/rpc/CordaRPCClient.kt @@ -7,6 +7,7 @@ import net.corda.core.messaging.CordaRPCOps import net.corda.core.utilities.NetworkHostAndPort import net.corda.nodeapi.ArtemisTcpTransport.Companion.tcpTransport import net.corda.nodeapi.ConnectionDirection +import net.corda.nodeapi.internal.serialization.AMQP_RPC_CLIENT_CONTEXT import net.corda.nodeapi.internal.serialization.KRYO_RPC_CLIENT_CONTEXT import java.time.Duration diff --git a/client/rpc/src/main/kotlin/net/corda/client/rpc/internal/KryoClientSerializationScheme.kt b/client/rpc/src/main/kotlin/net/corda/client/rpc/internal/KryoClientSerializationScheme.kt index 2275cd0d3f..e044117699 100644 --- a/client/rpc/src/main/kotlin/net/corda/client/rpc/internal/KryoClientSerializationScheme.kt +++ b/client/rpc/src/main/kotlin/net/corda/client/rpc/internal/KryoClientSerializationScheme.kt @@ -5,6 +5,11 @@ import net.corda.core.serialization.SerializationContext import net.corda.core.serialization.SerializationDefaults import net.corda.core.utilities.ByteSequence import net.corda.nodeapi.internal.serialization.* +import net.corda.nodeapi.internal.serialization.amqp.AMQPClientSerializationScheme +import net.corda.nodeapi.internal.serialization.kryo.AbstractKryoSerializationScheme +import net.corda.nodeapi.internal.serialization.kryo.DefaultKryoCustomizer +import net.corda.nodeapi.internal.serialization.kryo.KryoHeaderV0_1 +import net.corda.nodeapi.internal.serialization.kryo.RPCKryo import java.util.concurrent.atomic.AtomicBoolean class KryoClientSerializationScheme : AbstractKryoSerializationScheme() { diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/ClientContexts.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/ClientContexts.kt index bd6135522a..dc3712d56e 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/ClientContexts.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/ClientContexts.kt @@ -4,15 +4,25 @@ package net.corda.nodeapi.internal.serialization import net.corda.core.serialization.SerializationContext import net.corda.core.serialization.SerializationDefaults +import net.corda.nodeapi.internal.serialization.amqp.AmqpHeaderV1_0 +import net.corda.nodeapi.internal.serialization.kryo.KryoHeaderV0_1 /* * Serialisation contexts for the client. * These have been refactored into a separate file to prevent * servers from trying to instantiate any of them. */ + val KRYO_RPC_CLIENT_CONTEXT = SerializationContextImpl(KryoHeaderV0_1, SerializationDefaults.javaClass.classLoader, GlobalTransientClassWhiteList(BuiltInExceptionsWhitelist()), emptyMap(), true, SerializationContext.UseCase.RPCClient) + +val AMQP_RPC_CLIENT_CONTEXT = SerializationContextImpl(AmqpHeaderV1_0, + SerializationDefaults.javaClass.classLoader, + GlobalTransientClassWhiteList(BuiltInExceptionsWhitelist()), + emptyMap(), + true, + SerializationContext.UseCase.RPCClient) diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/CordaClassResolver.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/CordaClassResolver.kt index 97c50dcd67..52690a1780 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/CordaClassResolver.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/CordaClassResolver.kt @@ -12,6 +12,7 @@ import net.corda.core.serialization.CordaSerializable import net.corda.core.serialization.SerializationContext import net.corda.core.utilities.loggerFor import net.corda.nodeapi.internal.serialization.amqp.hasAnnotationInHierarchy +import net.corda.nodeapi.internal.serialization.kryo.ThrowableSerializer import java.io.PrintWriter import java.lang.reflect.Modifier.isAbstract import java.nio.charset.StandardCharsets diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/SerializationScheme.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/SerializationScheme.kt index a6be235f88..304340f342 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/SerializationScheme.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/SerializationScheme.kt @@ -1,26 +1,13 @@ package net.corda.nodeapi.internal.serialization -import co.paralleluniverse.fibers.Fiber -import co.paralleluniverse.io.serialization.kryo.KryoSerializer -import com.esotericsoftware.kryo.Kryo -import com.esotericsoftware.kryo.KryoException -import com.esotericsoftware.kryo.Serializer -import com.esotericsoftware.kryo.io.Input -import com.esotericsoftware.kryo.io.Output -import com.esotericsoftware.kryo.pool.KryoPool -import com.esotericsoftware.kryo.serializers.ClosureSerializer import com.google.common.cache.Cache import com.google.common.cache.CacheBuilder import net.corda.core.contracts.Attachment import net.corda.core.crypto.SecureHash -import net.corda.core.internal.LazyPool -import net.corda.core.internal.uncheckedCast import net.corda.core.serialization.* import net.corda.core.utilities.ByteSequence -import net.corda.core.utilities.OpaqueBytes import net.corda.nodeapi.internal.AttachmentsClassLoader import org.slf4j.LoggerFactory -import java.io.ByteArrayOutputStream import java.io.NotSerializableException import java.util.* import java.util.concurrent.ConcurrentHashMap @@ -158,124 +145,8 @@ open class SerializationFactoryImpl : SerializationFactory() { override fun hashCode(): Int = registeredSchemes.hashCode() } -private object AutoCloseableSerialisationDetector : Serializer() { - override fun write(kryo: Kryo, output: Output, closeable: AutoCloseable) { - val message = "${closeable.javaClass.name}, which is a closeable resource, has been detected during flow checkpointing. " + - "Restoring such resources across node restarts is not supported. Make sure code accessing it is " + - "confined to a private method or the reference is nulled out." - throw UnsupportedOperationException(message) - } - - override fun read(kryo: Kryo, input: Input, type: Class) = throw IllegalStateException("Should not reach here!") -} - -abstract class AbstractKryoSerializationScheme : SerializationScheme { - private val kryoPoolsForContexts = ConcurrentHashMap, KryoPool>() - - protected abstract fun rpcClientKryoPool(context: SerializationContext): KryoPool - protected abstract fun rpcServerKryoPool(context: SerializationContext): KryoPool - - private fun getPool(context: SerializationContext): KryoPool { - return kryoPoolsForContexts.computeIfAbsent(Pair(context.whitelist, context.deserializationClassLoader)) { - when (context.useCase) { - SerializationContext.UseCase.Checkpoint -> - KryoPool.Builder { - val serializer = Fiber.getFiberSerializer(false) as KryoSerializer - val classResolver = CordaClassResolver(context).apply { setKryo(serializer.kryo) } - // TODO The ClassResolver can only be set in the Kryo constructor and Quasar doesn't provide us with a way of doing that - val field = Kryo::class.java.getDeclaredField("classResolver").apply { isAccessible = true } - serializer.kryo.apply { - field.set(this, classResolver) - DefaultKryoCustomizer.customize(this) - addDefaultSerializer(AutoCloseable::class.java, AutoCloseableSerialisationDetector) - register(ClosureSerializer.Closure::class.java, CordaClosureSerializer) - classLoader = it.second - } - }.build() - SerializationContext.UseCase.RPCClient -> - rpcClientKryoPool(context) - SerializationContext.UseCase.RPCServer -> - rpcServerKryoPool(context) - else -> - KryoPool.Builder { - DefaultKryoCustomizer.customize(CordaKryo(CordaClassResolver(context))).apply { classLoader = it.second } - }.build() - } - } - } - - private fun withContext(kryo: Kryo, context: SerializationContext, block: (Kryo) -> T): T { - kryo.context.ensureCapacity(context.properties.size) - context.properties.forEach { kryo.context.put(it.key, it.value) } - try { - return block(kryo) - } finally { - kryo.context.clear() - } - } - - override fun deserialize(byteSequence: ByteSequence, clazz: Class, context: SerializationContext): T { - val pool = getPool(context) - val headerSize = KryoHeaderV0_1.size - val header = byteSequence.take(headerSize) - if (header != KryoHeaderV0_1) { - throw KryoException("Serialized bytes header does not match expected format.") - } - Input(byteSequence.bytes, byteSequence.offset + headerSize, byteSequence.size - headerSize).use { input -> - return pool.run { kryo -> - withContext(kryo, context) { - if (context.objectReferencesEnabled) { - uncheckedCast(kryo.readClassAndObject(input)) - } else { - kryo.withoutReferences { uncheckedCast(kryo.readClassAndObject(input)) } - } - } - } - } - } - - override fun serialize(obj: T, context: SerializationContext): SerializedBytes { - val pool = getPool(context) - return pool.run { kryo -> - withContext(kryo, context) { - serializeOutputStreamPool.run { stream -> - serializeBufferPool.run { buffer -> - Output(buffer).use { - it.outputStream = stream - it.writeBytes(KryoHeaderV0_1.bytes) - if (context.objectReferencesEnabled) { - kryo.writeClassAndObject(it, obj) - } else { - kryo.withoutReferences { kryo.writeClassAndObject(it, obj) } - } - } - SerializedBytes(stream.toByteArray()) - } - } - } - } - } -} - -private val serializeBufferPool = LazyPool( - newInstance = { ByteArray(64 * 1024) } -) -private val serializeOutputStreamPool = LazyPool( - clear = ByteArrayOutputStream::reset, - shouldReturnToPool = { it.size() < 256 * 1024 }, // Discard if it grew too large - newInstance = { ByteArrayOutputStream(64 * 1024) } -) - -// "corda" + majorVersionByte + minorVersionMSB + minorVersionLSB -val KryoHeaderV0_1: OpaqueBytes = OpaqueBytes("corda\u0000\u0000\u0001".toByteArray(Charsets.UTF_8)) -val KRYO_P2P_CONTEXT = SerializationContextImpl(KryoHeaderV0_1, - SerializationDefaults.javaClass.classLoader, - GlobalTransientClassWhiteList(BuiltInExceptionsWhitelist()), - emptyMap(), - true, - SerializationContext.UseCase.P2P) interface SerializationScheme { // byteSequence expected to just be the 8 bytes necessary for versioning diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/ServerContexts.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/ServerContexts.kt index baab649669..7380cfdd0e 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/ServerContexts.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/ServerContexts.kt @@ -6,6 +6,11 @@ import net.corda.core.serialization.ClassWhitelist import net.corda.core.serialization.SerializationContext import net.corda.core.serialization.SerializationDefaults import net.corda.nodeapi.internal.serialization.amqp.AmqpHeaderV1_0 +import net.corda.nodeapi.internal.serialization.kryo.KryoHeaderV0_1 + +object QuasarWhitelist : ClassWhitelist { + override fun hasListed(type: Class<*>): Boolean = true +} /* * Serialisation contexts for the server. @@ -16,18 +21,28 @@ import net.corda.nodeapi.internal.serialization.amqp.AmqpHeaderV1_0 * CANNOT always be instantiated outside of the server and so * MUST be kept separate! */ + val KRYO_RPC_SERVER_CONTEXT = SerializationContextImpl(KryoHeaderV0_1, SerializationDefaults.javaClass.classLoader, GlobalTransientClassWhiteList(BuiltInExceptionsWhitelist()), emptyMap(), true, SerializationContext.UseCase.RPCServer) + val KRYO_STORAGE_CONTEXT = SerializationContextImpl(KryoHeaderV0_1, SerializationDefaults.javaClass.classLoader, AllButBlacklisted, emptyMap(), true, SerializationContext.UseCase.Storage) + +val KRYO_P2P_CONTEXT = SerializationContextImpl(KryoHeaderV0_1, + SerializationDefaults.javaClass.classLoader, + GlobalTransientClassWhiteList(BuiltInExceptionsWhitelist()), + emptyMap(), + true, + SerializationContext.UseCase.P2P) + val KRYO_CHECKPOINT_CONTEXT = SerializationContextImpl(KryoHeaderV0_1, SerializationDefaults.javaClass.classLoader, QuasarWhitelist, @@ -35,9 +50,6 @@ val KRYO_CHECKPOINT_CONTEXT = SerializationContextImpl(KryoHeaderV0_1, true, SerializationContext.UseCase.Checkpoint) -object QuasarWhitelist : ClassWhitelist { - override fun hasListed(type: Class<*>): Boolean = true -} val AMQP_STORAGE_CONTEXT = SerializationContextImpl(AmqpHeaderV1_0, SerializationDefaults.javaClass.classLoader, @@ -45,3 +57,18 @@ val AMQP_STORAGE_CONTEXT = SerializationContextImpl(AmqpHeaderV1_0, emptyMap(), true, SerializationContext.UseCase.Storage) + +val AMQP_P2P_CONTEXT = SerializationContextImpl(AmqpHeaderV1_0, + SerializationDefaults.javaClass.classLoader, + GlobalTransientClassWhiteList(BuiltInExceptionsWhitelist()), + emptyMap(), + true, + SerializationContext.UseCase.P2P) + +val AMQP_RPC_SERVER_CONTEXT = SerializationContextImpl(AmqpHeaderV1_0, + SerializationDefaults.javaClass.classLoader, + GlobalTransientClassWhiteList(BuiltInExceptionsWhitelist()), + emptyMap(), + true, + SerializationContext.UseCase.RPCServer) + diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/AMQPSerializationScheme.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/AMQPSerializationScheme.kt similarity index 92% rename from node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/AMQPSerializationScheme.kt rename to node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/AMQPSerializationScheme.kt index 501840e3df..2f888c70fe 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/AMQPSerializationScheme.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/AMQPSerializationScheme.kt @@ -1,13 +1,12 @@ @file:JvmName("AMQPSerializationScheme") -package net.corda.nodeapi.internal.serialization +package net.corda.nodeapi.internal.serialization.amqp import net.corda.core.serialization.* import net.corda.core.utilities.ByteSequence -import net.corda.nodeapi.internal.serialization.amqp.AmqpHeaderV1_0 -import net.corda.nodeapi.internal.serialization.amqp.DeserializationInput -import net.corda.nodeapi.internal.serialization.amqp.SerializationOutput -import net.corda.nodeapi.internal.serialization.amqp.SerializerFactory +import net.corda.nodeapi.internal.serialization.DefaultWhitelist +import net.corda.nodeapi.internal.serialization.MutableClassWhitelist +import net.corda.nodeapi.internal.serialization.SerializationScheme import java.util.* import java.util.concurrent.ConcurrentHashMap @@ -25,7 +24,7 @@ fun SerializerFactory.addToWhitelist(vararg types: Class<*>) { } abstract class AbstractAMQPSerializationScheme : SerializationScheme { - internal companion object { + companion object { private val serializationWhitelists: List by lazy { ServiceLoader.load(SerializationWhitelist::class.java, this::class.java.classLoader).toList() + DefaultWhitelist } @@ -132,9 +131,3 @@ class AMQPClientSerializationScheme : AbstractAMQPSerializationScheme() { } -val AMQP_P2P_CONTEXT = SerializationContextImpl(AmqpHeaderV1_0, - SerializationDefaults.javaClass.classLoader, - GlobalTransientClassWhiteList(BuiltInExceptionsWhitelist()), - emptyMap(), - true, - SerializationContext.UseCase.P2P) diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/CordaClosureSerializer.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/kryo/CordaClosureSerializer.kt similarity index 94% rename from node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/CordaClosureSerializer.kt rename to node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/kryo/CordaClosureSerializer.kt index ec032a8d63..c821509b84 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/CordaClosureSerializer.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/kryo/CordaClosureSerializer.kt @@ -1,4 +1,4 @@ -package net.corda.nodeapi.internal.serialization +package net.corda.nodeapi.internal.serialization.kryo import com.esotericsoftware.kryo.Kryo import com.esotericsoftware.kryo.io.Output diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/DefaultKryoCustomizer.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/kryo/DefaultKryoCustomizer.kt similarity index 97% rename from node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/DefaultKryoCustomizer.kt rename to node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/kryo/DefaultKryoCustomizer.kt index 7532ecffe9..e19fd49604 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/DefaultKryoCustomizer.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/kryo/DefaultKryoCustomizer.kt @@ -1,4 +1,4 @@ -package net.corda.nodeapi.internal.serialization +package net.corda.nodeapi.internal.serialization.kryo import com.esotericsoftware.kryo.Kryo import com.esotericsoftware.kryo.Serializer @@ -23,6 +23,10 @@ import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.WireTransaction import net.corda.core.utilities.NonEmptySet import net.corda.core.utilities.toNonEmptySet +import net.corda.nodeapi.internal.serialization.CordaClassResolver +import net.corda.nodeapi.internal.serialization.DefaultWhitelist +import net.corda.nodeapi.internal.serialization.GeneratedAttachment +import net.corda.nodeapi.internal.serialization.MutableClassWhitelist import net.i2p.crypto.eddsa.EdDSAPrivateKey import net.i2p.crypto.eddsa.EdDSAPublicKey import org.bouncycastle.asn1.x500.X500Name diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/Kryo.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/kryo/Kryo.kt similarity index 98% rename from node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/Kryo.kt rename to node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/kryo/Kryo.kt index b4d0dc9c8c..8cdc24f941 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/Kryo.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/kryo/Kryo.kt @@ -1,4 +1,4 @@ -package net.corda.nodeapi.internal.serialization +package net.corda.nodeapi.internal.serialization.kryo import com.esotericsoftware.kryo.* import com.esotericsoftware.kryo.factories.ReflectionSerializerFactory @@ -21,6 +21,8 @@ import net.corda.core.serialization.SerializedBytes import net.corda.core.toFuture import net.corda.core.toObservable import net.corda.core.transactions.* +import net.corda.nodeapi.internal.serialization.CordaClassResolver +import net.corda.nodeapi.internal.serialization.serializationContextKey import org.bouncycastle.asn1.ASN1InputStream import org.bouncycastle.asn1.x500.X500Name import org.bouncycastle.cert.X509CertificateHolder @@ -280,7 +282,7 @@ object SignedTransactionSerializer : Serializer() { sealed class UseCaseSerializer(private val allowedUseCases: EnumSet) : Serializer() { protected fun checkUseCase() { - checkUseCase(allowedUseCases) + net.corda.nodeapi.internal.serialization.checkUseCase(allowedUseCases) } } diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/kryo/KryoSerializationScheme.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/kryo/KryoSerializationScheme.kt new file mode 100644 index 0000000000..cff437dd59 --- /dev/null +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/kryo/KryoSerializationScheme.kt @@ -0,0 +1,132 @@ +package net.corda.nodeapi.internal.serialization.kryo + +import java.util.concurrent.ConcurrentHashMap +import java.io.ByteArrayOutputStream +import co.paralleluniverse.fibers.Fiber +import co.paralleluniverse.io.serialization.kryo.KryoSerializer +import com.esotericsoftware.kryo.Kryo +import com.esotericsoftware.kryo.KryoException +import com.esotericsoftware.kryo.Serializer +import com.esotericsoftware.kryo.io.Input +import com.esotericsoftware.kryo.io.Output +import com.esotericsoftware.kryo.pool.KryoPool +import com.esotericsoftware.kryo.serializers.ClosureSerializer +import net.corda.core.internal.uncheckedCast +import net.corda.core.utilities.OpaqueBytes +import net.corda.core.utilities.ByteSequence +import net.corda.core.serialization.* +import net.corda.core.internal.LazyPool +import net.corda.nodeapi.internal.serialization.CordaClassResolver +import net.corda.nodeapi.internal.serialization.SerializationScheme + +// "corda" + majorVersionByte + minorVersionMSB + minorVersionLSB +val KryoHeaderV0_1: OpaqueBytes = OpaqueBytes("corda\u0000\u0000\u0001".toByteArray(Charsets.UTF_8)) + +private object AutoCloseableSerialisationDetector : Serializer() { + override fun write(kryo: Kryo, output: Output, closeable: AutoCloseable) { + val message = "${closeable.javaClass.name}, which is a closeable resource, has been detected during flow checkpointing. " + + "Restoring such resources across node restarts is not supported. Make sure code accessing it is " + + "confined to a private method or the reference is nulled out." + throw UnsupportedOperationException(message) + } + + override fun read(kryo: Kryo, input: Input, type: Class) = throw IllegalStateException("Should not reach here!") +} + +abstract class AbstractKryoSerializationScheme : SerializationScheme { + private val kryoPoolsForContexts = ConcurrentHashMap, KryoPool>() + + protected abstract fun rpcClientKryoPool(context: SerializationContext): KryoPool + protected abstract fun rpcServerKryoPool(context: SerializationContext): KryoPool + + private fun getPool(context: SerializationContext): KryoPool { + return kryoPoolsForContexts.computeIfAbsent(Pair(context.whitelist, context.deserializationClassLoader)) { + when (context.useCase) { + SerializationContext.UseCase.Checkpoint -> + KryoPool.Builder { + val serializer = Fiber.getFiberSerializer(false) as KryoSerializer + val classResolver = CordaClassResolver(context).apply { setKryo(serializer.kryo) } + // TODO The ClassResolver can only be set in the Kryo constructor and Quasar doesn't provide us with a way of doing that + val field = Kryo::class.java.getDeclaredField("classResolver").apply { isAccessible = true } + serializer.kryo.apply { + field.set(this, classResolver) + DefaultKryoCustomizer.customize(this) + addDefaultSerializer(AutoCloseable::class.java, AutoCloseableSerialisationDetector) + register(ClosureSerializer.Closure::class.java, CordaClosureSerializer) + classLoader = it.second + } + }.build() + SerializationContext.UseCase.RPCClient -> + rpcClientKryoPool(context) + SerializationContext.UseCase.RPCServer -> + rpcServerKryoPool(context) + else -> + KryoPool.Builder { + DefaultKryoCustomizer.customize(CordaKryo(CordaClassResolver(context))).apply { classLoader = it.second } + }.build() + } + } + } + + private fun withContext(kryo: Kryo, context: SerializationContext, block: (Kryo) -> T): T { + kryo.context.ensureCapacity(context.properties.size) + context.properties.forEach { kryo.context.put(it.key, it.value) } + try { + return block(kryo) + } finally { + kryo.context.clear() + } + } + + override fun deserialize(byteSequence: ByteSequence, clazz: Class, context: SerializationContext): T { + val pool = getPool(context) + val headerSize = KryoHeaderV0_1.size + val header = byteSequence.take(headerSize) + if (header != KryoHeaderV0_1) { + throw KryoException("Serialized bytes header does not match expected format.") + } + Input(byteSequence.bytes, byteSequence.offset + headerSize, byteSequence.size - headerSize).use { input -> + return pool.run { kryo -> + withContext(kryo, context) { + if (context.objectReferencesEnabled) { + uncheckedCast(kryo.readClassAndObject(input)) + } else { + kryo.withoutReferences { uncheckedCast(kryo.readClassAndObject(input)) } + } + } + } + } + } + + override fun serialize(obj: T, context: SerializationContext): SerializedBytes { + val pool = getPool(context) + return pool.run { kryo -> + withContext(kryo, context) { + serializeOutputStreamPool.run { stream -> + serializeBufferPool.run { buffer -> + Output(buffer).use { + it.outputStream = stream + it.writeBytes(KryoHeaderV0_1.bytes) + if (context.objectReferencesEnabled) { + kryo.writeClassAndObject(it, obj) + } else { + kryo.withoutReferences { kryo.writeClassAndObject(it, obj) } + } + } + SerializedBytes(stream.toByteArray()) + } + } + } + } + } +} + +private val serializeBufferPool = LazyPool( + newInstance = { ByteArray(64 * 1024) } +) + +private val serializeOutputStreamPool = LazyPool( + clear = ByteArrayOutputStream::reset, + shouldReturnToPool = { it.size() < 256 * 1024 }, // Discard if it grew too large + newInstance = { ByteArrayOutputStream(64 * 1024) } +) diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/SerializeAsTokenSerializer.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/kryo/SerializeAsTokenSerializer.kt similarity index 96% rename from node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/SerializeAsTokenSerializer.kt rename to node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/kryo/SerializeAsTokenSerializer.kt index 118eb299fb..eebe87a099 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/SerializeAsTokenSerializer.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/kryo/SerializeAsTokenSerializer.kt @@ -1,4 +1,4 @@ -package net.corda.nodeapi.internal.serialization +package net.corda.nodeapi.internal.serialization.kryo import com.esotericsoftware.kryo.Kryo import com.esotericsoftware.kryo.KryoException diff --git a/node-api/src/test/java/net/corda/nodeapi/internal/serialization/ForbiddenLambdaSerializationTests.java b/node-api/src/test/java/net/corda/nodeapi/internal/serialization/ForbiddenLambdaSerializationTests.java index 26ef012df3..ba452d138c 100644 --- a/node-api/src/test/java/net/corda/nodeapi/internal/serialization/ForbiddenLambdaSerializationTests.java +++ b/node-api/src/test/java/net/corda/nodeapi/internal/serialization/ForbiddenLambdaSerializationTests.java @@ -5,6 +5,8 @@ import net.corda.core.serialization.SerializationContext; import net.corda.core.serialization.SerializationFactory; import net.corda.core.serialization.SerializedBytes; import net.corda.testing.SerializationEnvironmentRule; +import net.corda.nodeapi.internal.serialization.kryo.CordaClosureBlacklistSerializer; +import net.corda.nodeapi.internal.serialization.kryo.KryoSerializationSchemeKt; import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -31,7 +33,7 @@ public final class ForbiddenLambdaSerializationTests { EnumSet contexts = EnumSet.complementOf(EnumSet.of(SerializationContext.UseCase.Checkpoint)); contexts.forEach(ctx -> { - SerializationContext context = new SerializationContextImpl(SerializationSchemeKt.getKryoHeaderV0_1(), this.getClass().getClassLoader(), AllWhitelist.INSTANCE, Maps.newHashMap(), true, ctx); + SerializationContext context = new SerializationContextImpl(KryoSerializationSchemeKt.getKryoHeaderV0_1(), this.getClass().getClassLoader(), AllWhitelist.INSTANCE, Maps.newHashMap(), true, ctx); String value = "Hey"; Callable target = (Callable & Serializable) () -> value; @@ -54,7 +56,7 @@ public final class ForbiddenLambdaSerializationTests { EnumSet contexts = EnumSet.complementOf(EnumSet.of(SerializationContext.UseCase.Checkpoint)); contexts.forEach(ctx -> { - SerializationContext context = new SerializationContextImpl(SerializationSchemeKt.getKryoHeaderV0_1(), this.getClass().getClassLoader(), AllWhitelist.INSTANCE, Maps.newHashMap(), true, ctx); + SerializationContext context = new SerializationContextImpl(KryoSerializationSchemeKt.getKryoHeaderV0_1(), this.getClass().getClassLoader(), AllWhitelist.INSTANCE, Maps.newHashMap(), true, ctx); String value = "Hey"; Callable target = () -> value; diff --git a/node-api/src/test/java/net/corda/nodeapi/internal/serialization/LambdaCheckpointSerializationTest.java b/node-api/src/test/java/net/corda/nodeapi/internal/serialization/LambdaCheckpointSerializationTest.java index c3230be211..93156d1073 100644 --- a/node-api/src/test/java/net/corda/nodeapi/internal/serialization/LambdaCheckpointSerializationTest.java +++ b/node-api/src/test/java/net/corda/nodeapi/internal/serialization/LambdaCheckpointSerializationTest.java @@ -5,6 +5,8 @@ import net.corda.core.serialization.SerializationContext; import net.corda.core.serialization.SerializationFactory; import net.corda.core.serialization.SerializedBytes; import net.corda.testing.SerializationEnvironmentRule; +import net.corda.nodeapi.internal.serialization.kryo.CordaClosureSerializer; +import net.corda.nodeapi.internal.serialization.kryo.KryoSerializationSchemeKt; import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -24,7 +26,7 @@ public final class LambdaCheckpointSerializationTest { @Before public void setup() { factory = testSerialization.env.getSERIALIZATION_FACTORY(); - context = new SerializationContextImpl(SerializationSchemeKt.getKryoHeaderV0_1(), this.getClass().getClassLoader(), AllWhitelist.INSTANCE, Maps.newHashMap(), true, SerializationContext.UseCase.Checkpoint); + context = new SerializationContextImpl(KryoSerializationSchemeKt.getKryoHeaderV0_1(), this.getClass().getClassLoader(), AllWhitelist.INSTANCE, Maps.newHashMap(), true, SerializationContext.UseCase.Checkpoint); } @Test diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/CordaClassResolverTests.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/CordaClassResolverTests.kt index 04f4c69122..155229a5bc 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/CordaClassResolverTests.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/CordaClassResolverTests.kt @@ -10,6 +10,8 @@ import net.corda.core.node.services.AttachmentStorage import net.corda.core.serialization.* import net.corda.nodeapi.internal.AttachmentsClassLoader import net.corda.nodeapi.internal.AttachmentsClassLoaderTests +import net.corda.nodeapi.internal.serialization.kryo.CordaKryo +import net.corda.nodeapi.internal.serialization.kryo.KryoHeaderV0_1 import net.corda.testing.node.MockAttachmentStorage import net.corda.testing.rigorousMock import org.junit.Rule diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/KryoTests.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/KryoTests.kt index 07593368a7..cafeb14dd0 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/KryoTests.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/KryoTests.kt @@ -13,6 +13,7 @@ import net.corda.core.utilities.ProgressTracker import net.corda.core.utilities.sequence import net.corda.node.serialization.KryoServerSerializationScheme import net.corda.node.services.persistence.NodeAttachmentService +import net.corda.nodeapi.internal.serialization.kryo.KryoHeaderV0_1 import net.corda.testing.ALICE_PUBKEY import net.corda.testing.SerializationEnvironmentRule import org.assertj.core.api.Assertions.assertThat diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/ListsSerializationTest.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/ListsSerializationTest.kt index c59d26b72d..5663a78beb 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/ListsSerializationTest.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/ListsSerializationTest.kt @@ -7,6 +7,7 @@ import net.corda.node.services.statemachine.SessionData import net.corda.nodeapi.internal.serialization.amqp.DeserializationInput import net.corda.nodeapi.internal.serialization.amqp.Envelope import net.corda.nodeapi.internal.serialization.amqp.SerializerFactory +import net.corda.nodeapi.internal.serialization.kryo.KryoHeaderV0_1 import net.corda.testing.amqpSpecific import net.corda.testing.kryoSpecific import net.corda.testing.SerializationEnvironmentRule diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/MapsSerializationTest.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/MapsSerializationTest.kt index 5cc64a5b64..ed45e63b68 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/MapsSerializationTest.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/MapsSerializationTest.kt @@ -6,6 +6,7 @@ import net.corda.core.serialization.CordaSerializable import net.corda.core.serialization.deserialize import net.corda.core.serialization.serialize import net.corda.node.services.statemachine.SessionData +import net.corda.nodeapi.internal.serialization.kryo.KryoHeaderV0_1 import net.corda.testing.amqpSpecific import net.corda.testing.kryoSpecific import net.corda.testing.SerializationEnvironmentRule @@ -84,4 +85,4 @@ class MapsSerializationTest { } assertArrayEquals(output.toByteArray(), serializedForm.bytes) } -} \ No newline at end of file +} diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/SerializationTokenTest.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/SerializationTokenTest.kt index 6fc74fba55..bcca06f6de 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/SerializationTokenTest.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/SerializationTokenTest.kt @@ -5,6 +5,10 @@ import com.esotericsoftware.kryo.KryoException import com.esotericsoftware.kryo.io.Output import net.corda.core.serialization.* import net.corda.core.utilities.OpaqueBytes +import net.corda.nodeapi.internal.serialization.kryo.CordaKryo +import net.corda.nodeapi.internal.serialization.kryo.DefaultKryoCustomizer +import net.corda.nodeapi.internal.serialization.kryo.KryoHeaderV0_1 +import net.corda.testing.TestDependencyInjectionBase import net.corda.testing.rigorousMock import net.corda.testing.SerializationEnvironmentRule import org.assertj.core.api.Assertions.assertThat diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/SetsSerializationTest.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/SetsSerializationTest.kt index 57cee0e7e5..243b73a803 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/SetsSerializationTest.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/SetsSerializationTest.kt @@ -5,6 +5,7 @@ import com.esotericsoftware.kryo.util.DefaultClassResolver import net.corda.core.serialization.deserialize import net.corda.core.serialization.serialize import net.corda.node.services.statemachine.SessionData +import net.corda.nodeapi.internal.serialization.kryo.KryoHeaderV0_1 import net.corda.testing.kryoSpecific import net.corda.testing.SerializationEnvironmentRule import org.junit.Assert.assertArrayEquals diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializationOutputTests.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializationOutputTests.kt index f3496a9f74..d6527fd816 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializationOutputTests.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializationOutputTests.kt @@ -13,7 +13,6 @@ import net.corda.core.serialization.CordaSerializable import net.corda.core.serialization.SerializationFactory import net.corda.core.transactions.LedgerTransaction import net.corda.client.rpc.RPCException -import net.corda.nodeapi.internal.serialization.AbstractAMQPSerializationScheme import net.corda.nodeapi.internal.serialization.AllWhitelist import net.corda.nodeapi.internal.serialization.EmptyWhitelist import net.corda.nodeapi.internal.serialization.amqp.SerializerFactory.Companion.isPrimitive diff --git a/node/src/main/kotlin/net/corda/node/internal/Node.kt b/node/src/main/kotlin/net/corda/node/internal/Node.kt index 992b433081..20eb06247a 100644 --- a/node/src/main/kotlin/net/corda/node/internal/Node.kt +++ b/node/src/main/kotlin/net/corda/node/internal/Node.kt @@ -39,6 +39,7 @@ import net.corda.nodeapi.ConnectionDirection import net.corda.nodeapi.internal.ShutdownHook import net.corda.nodeapi.internal.addShutdownHook import net.corda.nodeapi.internal.serialization.* +import net.corda.nodeapi.internal.serialization.amqp.AMQPServerSerializationScheme import org.apache.activemq.artemis.api.core.ActiveMQNotConnectedException import org.apache.activemq.artemis.api.core.RoutingType import org.apache.activemq.artemis.api.core.client.ActiveMQClient diff --git a/node/src/main/kotlin/net/corda/node/serialization/KryoServerSerializationScheme.kt b/node/src/main/kotlin/net/corda/node/serialization/KryoServerSerializationScheme.kt index 319312689b..09f876d937 100644 --- a/node/src/main/kotlin/net/corda/node/serialization/KryoServerSerializationScheme.kt +++ b/node/src/main/kotlin/net/corda/node/serialization/KryoServerSerializationScheme.kt @@ -4,10 +4,10 @@ import com.esotericsoftware.kryo.pool.KryoPool import net.corda.core.serialization.SerializationContext import net.corda.core.utilities.ByteSequence import net.corda.node.services.messaging.RpcServerObservableSerializer -import net.corda.nodeapi.internal.serialization.AbstractKryoSerializationScheme -import net.corda.nodeapi.internal.serialization.DefaultKryoCustomizer -import net.corda.nodeapi.internal.serialization.KryoHeaderV0_1 -import net.corda.nodeapi.internal.serialization.RPCKryo +import net.corda.nodeapi.internal.serialization.kryo.AbstractKryoSerializationScheme +import net.corda.nodeapi.internal.serialization.kryo.DefaultKryoCustomizer +import net.corda.nodeapi.internal.serialization.kryo.KryoHeaderV0_1 +import net.corda.nodeapi.internal.serialization.kryo.RPCKryo class KryoServerSerializationScheme : AbstractKryoSerializationScheme() { override fun canDeserializeVersion(byteSequence: ByteSequence, target: SerializationContext.UseCase): Boolean { diff --git a/node/src/test/kotlin/net/corda/node/utilities/X509UtilitiesTest.kt b/node/src/test/kotlin/net/corda/node/utilities/X509UtilitiesTest.kt index a94b2d7d88..df81bef948 100644 --- a/node/src/test/kotlin/net/corda/node/utilities/X509UtilitiesTest.kt +++ b/node/src/test/kotlin/net/corda/node/utilities/X509UtilitiesTest.kt @@ -14,7 +14,7 @@ import net.corda.core.serialization.serialize import net.corda.node.serialization.KryoServerSerializationScheme import net.corda.node.services.config.createKeystoreForCordaNode import net.corda.nodeapi.internal.serialization.AllWhitelist -import net.corda.nodeapi.internal.serialization.KryoHeaderV0_1 +import net.corda.nodeapi.internal.serialization.kryo.KryoHeaderV0_1 import net.corda.nodeapi.internal.serialization.SerializationContextImpl import net.corda.nodeapi.internal.serialization.SerializationFactoryImpl import net.corda.testing.ALICE diff --git a/testing/test-utils/src/main/kotlin/net/corda/testing/CoreTestUtils.kt b/testing/test-utils/src/main/kotlin/net/corda/testing/CoreTestUtils.kt index a1cda49a22..3155e38f27 100644 --- a/testing/test-utils/src/main/kotlin/net/corda/testing/CoreTestUtils.kt +++ b/testing/test-utils/src/main/kotlin/net/corda/testing/CoreTestUtils.kt @@ -24,7 +24,7 @@ import net.corda.node.utilities.CertificateAndKeyPair import net.corda.node.utilities.CertificateType import net.corda.node.utilities.X509Utilities import net.corda.nodeapi.config.SSLConfiguration -import net.corda.nodeapi.internal.serialization.AMQP_ENABLED +import net.corda.nodeapi.internal.serialization.amqp.AMQP_ENABLED import org.mockito.internal.stubbing.answers.ThrowsException import org.mockito.stubbing.Answer import java.nio.file.Files diff --git a/testing/test-utils/src/main/kotlin/net/corda/testing/SerializationTestHelpers.kt b/testing/test-utils/src/main/kotlin/net/corda/testing/SerializationTestHelpers.kt index 857f9cb0fb..4738175ae4 100644 --- a/testing/test-utils/src/main/kotlin/net/corda/testing/SerializationTestHelpers.kt +++ b/testing/test-utils/src/main/kotlin/net/corda/testing/SerializationTestHelpers.kt @@ -9,6 +9,8 @@ import net.corda.core.serialization.internal.SerializationEnvironment import net.corda.core.utilities.ByteSequence import net.corda.node.serialization.KryoServerSerializationScheme import net.corda.nodeapi.internal.serialization.* +import net.corda.nodeapi.internal.serialization.amqp.AMQPClientSerializationScheme +import net.corda.nodeapi.internal.serialization.amqp.AMQPServerSerializationScheme import org.junit.rules.TestRule import org.junit.runner.Description import org.junit.runners.model.Statement diff --git a/verifier/src/main/kotlin/net/corda/verifier/Verifier.kt b/verifier/src/main/kotlin/net/corda/verifier/Verifier.kt index 22ff4e9f60..ccc9a967db 100644 --- a/verifier/src/main/kotlin/net/corda/verifier/Verifier.kt +++ b/verifier/src/main/kotlin/net/corda/verifier/Verifier.kt @@ -18,8 +18,11 @@ import net.corda.nodeapi.config.NodeSSLConfiguration import net.corda.nodeapi.config.getValue import net.corda.nodeapi.internal.addShutdownHook import net.corda.nodeapi.internal.serialization.* +import net.corda.nodeapi.internal.serialization.amqp.AbstractAMQPSerializationScheme import net.corda.nodeapi.internal.serialization.amqp.AmqpHeaderV1_0 import net.corda.nodeapi.internal.serialization.amqp.SerializerFactory +import net.corda.nodeapi.internal.serialization.kryo.AbstractKryoSerializationScheme +import net.corda.nodeapi.internal.serialization.kryo.KryoHeaderV0_1 import org.apache.activemq.artemis.api.core.client.ActiveMQClient import java.nio.file.Path import java.nio.file.Paths From 6a5f8a236260fc264a556465c4be67b5f536eadc Mon Sep 17 00:00:00 2001 From: Clinton Date: Tue, 31 Oct 2017 16:44:13 +0000 Subject: [PATCH 02/24] Upgraded to gradle 4.3 (#1960) * Upgraded to gradle 4.3 * Replaced a null check with an elvis expression --- constants.properties | 2 +- .../kotlin/net/corda/plugins/CordappPlugin.kt | 3 ++- gradle/wrapper/gradle-wrapper.jar | Bin 54708 -> 54727 bytes gradle/wrapper/gradle-wrapper.properties | 3 +-- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/constants.properties b/constants.properties index c85d51a756..c31f20735c 100644 --- a/constants.properties +++ b/constants.properties @@ -1,4 +1,4 @@ -gradlePluginsVersion=2.0.6 +gradlePluginsVersion=2.0.7 kotlinVersion=1.1.50 guavaVersion=21.0 bouncycastleVersion=1.57 diff --git a/gradle-plugins/cordapp/src/main/kotlin/net/corda/plugins/CordappPlugin.kt b/gradle-plugins/cordapp/src/main/kotlin/net/corda/plugins/CordappPlugin.kt index e307a7b162..624194fd32 100644 --- a/gradle-plugins/cordapp/src/main/kotlin/net/corda/plugins/CordappPlugin.kt +++ b/gradle-plugins/cordapp/src/main/kotlin/net/corda/plugins/CordappPlugin.kt @@ -61,7 +61,8 @@ class CordappPlugin : Plugin { } filteredDeps.forEach { // net.corda or com.r3.corda.enterprise may be a core dependency which shouldn't be included in this cordapp so give a warning - if ((it.group.startsWith("net.corda.") || it.group.startsWith("com.r3.corda.enterprise."))) { + val group = it.group?.toString() ?: "" + if (group.startsWith("net.corda.") || group.startsWith("com.r3.corda.enterprise.")) { project.logger.warn("You appear to have included a Corda platform component ($it) using a 'compile' or 'runtime' dependency." + "This can cause node stability problems. Please use 'corda' instead." + "See http://docs.corda.net/cordapp-build-systems.html") diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 7a3265ee94c0ab25cf079ac8ccdf87f41d455d42..27768f1bbac3ce2d055b20d521f12da78d331e8e 100644 GIT binary patch delta 1862 zcmYL}X*ApU7sd%es1cMBJ1wE5C~B>eseK4Wi`3Ru>;|>fGOwt)+?vCH7sL zps~ijmN9g(r5(Fb{4;sc|2^k>&T~Ka-shh4eQ_HIEd2zQ^qZ{q?_`xTbR5`rgZ>F( zW=eHf9)BrCEtxIHRXh-$ z`!s%Xy2Bs7jv6$V@GMU-&0F6~!j9}&?O_{EaaHe$?l~56{Dpc1z4m3=?~!ZzXJ`92 znXa!cz?vVn)9^$i+7=$o^vOx&4_m*%P7bx6&PsQek= zPHkJ2f1;YrUsmO(>3WeIPpceCDp4f1spXAfwn2@YB}P)HymCwb%uUw9=4!LRKKOOG z<}AlNaW}7{`m;p_1#g5VJKmqzflr4B~$+xm<^cG%>OgH)0H6=T-dNlt*xT~Xq zCwTzkpjssQ=Gq8Xq*)ixQ(fh6x!ELjSflFO>eVQG>dY*P#7z+0Z+KU9xYlyDJEvgk zy8aMX0_*L+odaFEU9amHtla61a=>~zXUg5>Xi(CEKBpELIX$bb19t`we8k1Qcod__ zLZ}=Z>(Mf;u@l?2su{4@U3oN6ICP#;+#+&!r@GvKRW# zV}=N)MwA`ml9fh)@%U!I4(2E*T+7RDt4hL-&oumc3KNpL03+0peP_d*>4 zJi{64GkZlszOt3DB2FWOKVQ*H@eY@efPeA#%}C}^k4i|JqvAJJO35@iM~s>aNqlSS z8|2nht(95v%H!74xLcpR_0~nUR>i_>5qvDyzTe;8D5aUFcph=O_#7Ga(ITzOHqxzz zmIddCq>Oce#gS)TgOUPF6~!la1`%y(&rVWc3YzAp zTfa*E^KD+66IxmXY+11RUBPsdBI@9rYC^u+y4B^^eUlBHRddya>}u7uc3h@2h<0|A zr>&xL$@SYFn@`>GpU__4s069?HU^km&ZDy_0K5~Dpfk@Z%aJytRE?91+g4Ds(-d_pD5l11a!*y7Q|YUV z5jGAxYbf>lTa>TvK9^zqAa!1O_e*j2?esNRMNpjD%AF+m& z^_n-Bj6D!iIqR4k1Wdc0fZ3DMDgrnVF*1JrZqoXO4y%q+qWe1$S>q}ZmFTP$%lWDd zs<;lDaQRc4)_#a%2!`0ljSLAYXqyRm7ZQ|3V14mwoXD5f*-?Uvh{4+82t}{+uy!Fj zwj>4hQ}JoQGI!=U(+!7|@zb{1BSasYoxRC7Jsgm#Z>-vxu)i#Yr_oTDcYovsN0mm&rT&eBehu{F z=NVh@T-eOL=4Q+Nm&zHH6(#J(-`=w6E)iOLJ8~|`03H7HNtaQs$59x>BQSL>uNS;1 z4fg(c$={IoY(o!m)ima@=GRxDRH8XD&|LODbt z(xBewb+MipperIQa=&YKk>}(9!KHuWm(%;+MfY{n7e#^gG3nN5{~*&eQ-l&+T_7Uv z=bkx5?TP5{UwKAz`(@7edPT~u)begQUv;+rj^$6yQpSsBfAG;b^nSS3oBddy`3b}+ zu#ls<0J5IYyo6rzv9VBm$kjW4I!zI1A>|LEz0A8{d~&+Jki+Sk4`3VMfEGu{tkD`@ z>qeThumVNHu=?hZP)klWCZst#2YTQv9=J)iVqEz8Wbyz94nx}K zTwv^A#X~PJ{Rjx2QPjZ;(zhvqRtp%mJcYb;KLwC>F(Vb7Fad>^0I?--05_okl4%5f znv@1C8`&6!2OUtZAAU?H0QaN=h`9&g?9l~qLr|b$62?3Qj839K6(pdJBn8+{K}G)i aJ%0KSFZ~Jqf0(Y0f%vJbY&PWo;Qs(ksB^sl delta 1775 zcmVbznQ*=)eqSh4iL) z%q4IG^9kIZyW|G$FlzBc!5uEXJV6a7QTLjlmGAC2`gi z4@*W##(NTEtK{gGS(eZ>en-8l<|>YEt?>PK%Lk~Hwe0DTb0tR?kq|;+z8sTi@Bn$77Q(yH}u@7wX~#~g>l`~ zCf&$x644Re)SZ(OQt1GUF-No21w$Jin2`_}wF;Uj8Bb1BmKHR7TD%j$t-NZ?sJ1Tl zo-%5AX|?J7egRxiu2aXd88zZ8>g>Xv7h|k{uc41iLQj2gw!j0>ERPl)%&ys1)FEEf zoXJ%JZAcG!};HwQ|K^W?MpU&s21ypa}+a(s!M+qv^1(HK9xr)@j4A?gg>7rw2G5Tm4sm zR|rTi8Ct+3zB*xALBeJ?xA4R%)L*F0*ig8`YnrlTx`8Z$Pe;@L!t(h()uP)9_F=z&f=}?Nf&)VO44;eb7xXb-YeL^(A1}3A3ep&0 z;pc029d%z~RKZvHTERCWcHfICRfl|X;f|JfTvE)e;&xxmSUP(jnV!3Pjy(!r1rlPq zcWJTEb-fQd{J0e^!Wu8LH zJVV>uQ?x%pB=a-YJwddlk+Um*h)K9|a0ML_9%37XxWw~`iOesE46Zk>Kx{{XCiGn^$m{_=pNeFa)^L zP8=ojW0V!HRn2ifuU9FZpqFY^IqrnbIsxsmYmjq`D3tr$pj9X9yZ}{yCnMCohLfI? zYg`2gIg=HYcve%g!R-x(lv7U0nyw0ZxbBr2c(~;ST#A=|n zK%hfBo(o{^@B&FV-PFnYW=;;f_?kI6gHg}PpI*Y+Gjy`)UE{1?_j4#&u`Xl7EZ5|7 zq_fM|_#NWe?!IMgn$0GEH;Z?|DCr8x9?v%Q5WS7Iy{^eJw$uQgASJ<8#@NIz?t0mW zT`bUkc4Ckj9$}uZ5$0L`UB)?FCwb;@k-tJO1LOZ!Y3@<7R%$P=VO&TiA08o(p_c*cDm1*b3BIDJ}^=>x7b z9bLKcu-53IqU9O9u!_tMF};O2khDIFaT`vz^4s z2niy3$y$;H005bjaLr``nRSz4&m)ui%`pOnc9UVxBa=|hL;@RnlVQ&zlTgkDlfKS3 z0uOzYVb3Fz8qY-mQ$lVQ&zllad@0uzjrVb3FzWsDn>e$Xlc6_1la(kYW| zj~0{Q(h!sR&>8{=l9NHwDU)E55R-t?5R+cf8UhxTlR?rclWvs}li<=2lf=;)0uz^$ zLDDai57ICK_L-AG(rE(FoReYCBa_e4I|3)4lVQ&zlP}Xq0%f9;Vb3Cyo6|4?5Veyb z)FPAb(=Y+(lOWVt0`9+)9l$)39@P+&$J81E!oidA!8?;4)hYp%lS$Pw1WK#$ZIgf1 R76Imymenf;Kg0k4003uUWfcGb diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 383fa73d26..92165eede8 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,5 @@ -#Thu Aug 24 12:32:31 BST 2017 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-4.3-bin.zip From 6b2b663ab3b47792941422389aa348206136d8cc Mon Sep 17 00:00:00 2001 From: Andrzej Cichocki Date: Tue, 31 Oct 2017 17:10:37 +0000 Subject: [PATCH 03/24] CORDA-716 Retire MockNetwork.Factory (#1937) --- .../net/corda/core/flows/AttachmentTests.kt | 11 +++----- .../AttachmentSerializationTest.kt | 9 +++---- .../node/messaging/TwoPartyTradeFlowTests.kt | 12 ++++----- .../vault/VaultSoftLockManagerTest.kt | 17 +++++------- .../corda/netmap/simulation/IRSSimulation.kt | 2 +- .../net/corda/netmap/simulation/Simulation.kt | 26 +++++++++--------- .../kotlin/net/corda/testing/node/MockNode.kt | 27 +++++++------------ .../node/MockNodeFactoryInJavaTest.java | 25 +++++++++++++++++ 8 files changed, 66 insertions(+), 63 deletions(-) create mode 100644 testing/node-driver/src/test/java/net/corda/testing/node/MockNodeFactoryInJavaTest.java 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 81e67b3b6e..1824be00a9 100644 --- a/core/src/test/kotlin/net/corda/core/flows/AttachmentTests.kt +++ b/core/src/test/kotlin/net/corda/core/flows/AttachmentTests.kt @@ -14,7 +14,6 @@ import net.corda.testing.ALICE import net.corda.testing.ALICE_NAME import net.corda.testing.BOB import net.corda.testing.node.MockNetwork -import net.corda.testing.node.MockNodeArgs import net.corda.testing.node.MockNodeParameters import net.corda.testing.singleIdentity import org.junit.After @@ -109,13 +108,11 @@ class AttachmentTests { } @Test - fun `malicious response`() { + fun maliciousResponse() { // Make a node that doesn't do sanity checking at load time. - val aliceNode = mockNet.createNotaryNode(MockNodeParameters(legalName = ALICE.name), nodeFactory = object : MockNetwork.Factory { - override fun create(args: MockNodeArgs): MockNetwork.MockNode { - return object : MockNetwork.MockNode(args) { - override fun start() = super.start().apply { attachments.checkAttachmentsOnLoad = false } - } + val aliceNode = mockNet.createNotaryNode(MockNodeParameters(legalName = ALICE.name), nodeFactory = { args -> + object : MockNetwork.MockNode(args) { + override fun start() = super.start().apply { attachments.checkAttachmentsOnLoad = false } } }, validating = false) val bobNode = mockNet.createNode(MockNodeParameters(legalName = BOB.name)) 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 8057164bcf..2828bb31b9 100644 --- a/core/src/test/kotlin/net/corda/core/serialization/AttachmentSerializationTest.kt +++ b/core/src/test/kotlin/net/corda/core/serialization/AttachmentSerializationTest.kt @@ -17,7 +17,6 @@ import net.corda.node.services.persistence.NodeAttachmentService import net.corda.node.utilities.currentDBSession import net.corda.testing.chooseIdentity import net.corda.testing.node.MockNetwork -import net.corda.testing.node.MockNodeArgs import net.corda.testing.node.MockNodeParameters import org.junit.After import org.junit.Before @@ -156,11 +155,9 @@ class AttachmentSerializationTest { private fun rebootClientAndGetAttachmentContent(checkAttachmentsOnLoad: Boolean = true): String { client.dispose() - client = mockNet.createNode(MockNodeParameters(client.internals.id), object : MockNetwork.Factory { - override fun create(args: MockNodeArgs): MockNetwork.MockNode { - return object : MockNetwork.MockNode(args) { - override fun start() = super.start().apply { attachments.checkAttachmentsOnLoad = checkAttachmentsOnLoad } - } + client = mockNet.createNode(MockNodeParameters(client.internals.id), { args -> + object : MockNetwork.MockNode(args) { + override fun start() = super.start().apply { attachments.checkAttachmentsOnLoad = checkAttachmentsOnLoad } } }) return (client.smm.allStateMachines[0].stateMachine.resultFuture.apply { mockNet.runNetwork() }.getOrThrow() as ClientResult).attachmentContent 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 c5f893965e..451b1c2d9c 100644 --- a/node/src/test/kotlin/net/corda/node/messaging/TwoPartyTradeFlowTests.kt +++ b/node/src/test/kotlin/net/corda/node/messaging/TwoPartyTradeFlowTests.kt @@ -294,13 +294,11 @@ class TwoPartyTradeFlowTests(val anonymous: Boolean) { // of gets and puts. private fun makeNodeWithTracking(name: CordaX500Name): StartedNode { // Create a node in the mock network ... - return mockNet.createNode(MockNodeParameters(legalName = name), nodeFactory = object : MockNetwork.Factory { - override fun create(args: MockNodeArgs): MockNetwork.MockNode { - return object : MockNetwork.MockNode(args) { - // That constructs a recording tx storage - override fun makeTransactionStorage(): WritableTransactionStorage { - return RecordingTransactionStorage(database, super.makeTransactionStorage()) - } + return mockNet.createNode(MockNodeParameters(legalName = name), nodeFactory = { args -> + object : MockNetwork.MockNode(args) { + // That constructs a recording tx storage + override fun makeTransactionStorage(): WritableTransactionStorage { + return RecordingTransactionStorage(database, super.makeTransactionStorage()) } } }) 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 ae1ac4da8f..16aa63254d 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 @@ -28,7 +28,6 @@ import net.corda.node.services.api.VaultServiceInternal import net.corda.testing.chooseIdentity import net.corda.testing.node.MockNetwork import net.corda.testing.rigorousMock -import net.corda.testing.node.MockNodeArgs import net.corda.testing.node.MockNodeParameters import org.junit.After import org.junit.Test @@ -80,15 +79,13 @@ class VaultSoftLockManagerTest { private val mockVault = rigorousMock().also { doNothing().whenever(it).softLockRelease(any(), anyOrNull()) } - private val mockNet = MockNetwork(cordappPackages = listOf(ContractImpl::class.packageName), defaultFactory = object : MockNetwork.Factory { - override fun create(args: MockNodeArgs): MockNetwork.MockNode { - return object : MockNetwork.MockNode(args) { - override fun makeVaultService(keyManagementService: KeyManagementService, stateLoader: StateLoader): VaultServiceInternal { - val realVault = super.makeVaultService(keyManagementService, stateLoader) - return object : VaultServiceInternal by realVault { - override fun softLockRelease(lockId: UUID, stateRefs: NonEmptySet?) { - mockVault.softLockRelease(lockId, stateRefs) // No need to also call the real one for these tests. - } + private val mockNet = MockNetwork(cordappPackages = listOf(ContractImpl::class.packageName), defaultFactory = { args -> + object : MockNetwork.MockNode(args) { + override fun makeVaultService(keyManagementService: KeyManagementService, stateLoader: StateLoader): VaultServiceInternal { + val realVault = super.makeVaultService(keyManagementService, stateLoader) + return object : VaultServiceInternal by realVault { + override fun softLockRelease(lockId: UUID, stateRefs: NonEmptySet?) { + mockVault.softLockRelease(lockId, stateRefs) // No need to also call the real one for these tests. } } } diff --git a/samples/network-visualiser/src/main/kotlin/net/corda/netmap/simulation/IRSSimulation.kt b/samples/network-visualiser/src/main/kotlin/net/corda/netmap/simulation/IRSSimulation.kt index 5b73073155..ae34b399da 100644 --- a/samples/network-visualiser/src/main/kotlin/net/corda/netmap/simulation/IRSSimulation.kt +++ b/samples/network-visualiser/src/main/kotlin/net/corda/netmap/simulation/IRSSimulation.kt @@ -133,7 +133,7 @@ class IRSSimulation(networkSendManuallyPumped: Boolean, runAsync: Boolean, laten val irs = om.readValue(javaClass.classLoader.getResourceAsStream("net/corda/irs/web/simulation/trade.json") .reader() .readText() - .replace("oracleXXX", RatesOracleFactory.RATES_SERVICE_NAME.toString())) + .replace("oracleXXX", RatesOracleNode.RATES_SERVICE_NAME.toString())) irs.fixedLeg.fixedRatePayer = node1.info.chooseIdentity() irs.floatingLeg.floatingRatePayer = node2.info.chooseIdentity() 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 d186a74484..4e774cc191 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 @@ -50,20 +50,18 @@ abstract class Simulation(val networkSendManuallyPumped: Boolean, val bankLocations = listOf(Pair("London", "GB"), Pair("Frankfurt", "DE"), Pair("Rome", "IT")) - object RatesOracleFactory : MockNetwork.Factory { - // TODO: Make a more realistic legal name - val RATES_SERVICE_NAME = CordaX500Name(organisation = "Rates Service Provider", locality = "Madrid", country = "ES") + class RatesOracleNode(args: MockNodeArgs) : MockNode(args) { + companion object { + // TODO: Make a more realistic legal name + val RATES_SERVICE_NAME = CordaX500Name(organisation = "Rates Service Provider", locality = "Madrid", country = "ES") + } - override fun create(args: MockNodeArgs): MockNode { - return object : MockNode(args) { - override fun start() = super.start().apply { - registerInitiatedFlow(NodeInterestRates.FixQueryHandler::class.java) - registerInitiatedFlow(NodeInterestRates.FixSignHandler::class.java) - javaClass.classLoader.getResourceAsStream("net/corda/irs/simulation/example.rates.txt").use { - database.transaction { - findTokenizableService(NodeInterestRates.Oracle::class.java)!!.uploadFixes(it.reader().readText()) - } - } + override fun start() = super.start().apply { + registerInitiatedFlow(NodeInterestRates.FixQueryHandler::class.java) + registerInitiatedFlow(NodeInterestRates.FixSignHandler::class.java) + javaClass.classLoader.getResourceAsStream("net/corda/irs/simulation/example.rates.txt").use { + database.transaction { + findTokenizableService(NodeInterestRates.Oracle::class.java)!!.uploadFixes(it.reader().readText()) } } } @@ -78,7 +76,7 @@ abstract class Simulation(val networkSendManuallyPumped: Boolean, // So we just fire a message at a node that doesn't know how to handle it, and it'll ignore it. // But that's fine for visualisation purposes. val regulators = listOf(mockNet.createUnstartedNode(defaultParams.copy(legalName = DUMMY_REGULATOR.name))) - val ratesOracle = mockNet.createUnstartedNode(defaultParams.copy(legalName = RatesOracleFactory.RATES_SERVICE_NAME), RatesOracleFactory) + 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(notary.internals, ratesOracle) val banks: List = bankLocations.mapIndexed { i, (city, country) -> diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNode.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNode.kt index 5f008e40d0..0dd7089795 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNode.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNode.kt @@ -63,13 +63,13 @@ data class MockNetworkParameters( val networkSendManuallyPumped: Boolean = false, val threadPerNode: Boolean = false, val servicePeerAllocationStrategy: InMemoryMessagingNetwork.ServicePeerAllocationStrategy = InMemoryMessagingNetwork.ServicePeerAllocationStrategy.Random(), - val defaultFactory: MockNetwork.Factory<*> = MockNetwork.DefaultFactory, + val defaultFactory: (MockNodeArgs) -> MockNetwork.MockNode = MockNetwork::MockNode, val initialiseSerialization: Boolean = true, val cordappPackages: List = emptyList()) { 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: MockNetwork.Factory<*>) = copy(defaultFactory = defaultFactory) + fun setDefaultFactory(defaultFactory: (MockNodeArgs) -> MockNetwork.MockNode) = copy(defaultFactory = defaultFactory) fun setInitialiseSerialization(initialiseSerialization: Boolean) = copy(initialiseSerialization = initialiseSerialization) fun setCordappPackages(cordappPackages: List) = copy(cordappPackages = cordappPackages) } @@ -119,7 +119,7 @@ class MockNetwork(defaultParameters: MockNetworkParameters = MockNetworkParamete private val networkSendManuallyPumped: Boolean = defaultParameters.networkSendManuallyPumped, private val threadPerNode: Boolean = defaultParameters.threadPerNode, servicePeerAllocationStrategy: InMemoryMessagingNetwork.ServicePeerAllocationStrategy = defaultParameters.servicePeerAllocationStrategy, - private val defaultFactory: Factory<*> = defaultParameters.defaultFactory, + private val defaultFactory: (MockNodeArgs) -> MockNode = defaultParameters.defaultFactory, initialiseSerialization: Boolean = defaultParameters.initialiseSerialization, private val cordappPackages: List = defaultParameters.cordappPackages) : Closeable { /** Helper constructor for creating a [MockNetwork] with custom parameters from Java. */ @@ -141,15 +141,6 @@ class MockNetwork(defaultParameters: MockNetworkParameters = MockNetworkParamete filesystem.getPath("/nodes").createDirectory() } - /** Allows customisation of how nodes are created. */ - interface Factory { - fun create(args: MockNodeArgs): N - } - - object DefaultFactory : Factory { - override fun create(args: MockNodeArgs) = MockNode(args) - } - /** * Because this executor is shared, we need to be careful about nodes shutting it down. */ @@ -286,19 +277,19 @@ class MockNetwork(defaultParameters: MockNetworkParameters = MockNetworkParamete } fun createUnstartedNode(parameters: MockNodeParameters = MockNodeParameters()) = createUnstartedNode(parameters, defaultFactory) - fun createUnstartedNode(parameters: MockNodeParameters = MockNodeParameters(), nodeFactory: Factory): N { + fun createUnstartedNode(parameters: MockNodeParameters = MockNodeParameters(), nodeFactory: (MockNodeArgs) -> N): N { return createNodeImpl(parameters, nodeFactory, false) } fun createNode(parameters: MockNodeParameters = MockNodeParameters()): StartedNode = createNode(parameters, defaultFactory) - /** Like the other [createNode] but takes a [Factory] and propagates its [MockNode] subtype. */ - fun createNode(parameters: MockNodeParameters = MockNodeParameters(), nodeFactory: Factory): StartedNode { + /** Like the other [createNode] but takes a [nodeFactory] and propagates its [MockNode] subtype. */ + fun createNode(parameters: MockNodeParameters = MockNodeParameters(), nodeFactory: (MockNodeArgs) -> N): StartedNode { val node: StartedNode = uncheckedCast(createNodeImpl(parameters, nodeFactory, true).started)!! ensureAllNetworkMapCachesHaveAllNodeInfos() return node } - private fun createNodeImpl(parameters: MockNodeParameters, nodeFactory: Factory, start: Boolean): N { + private fun createNodeImpl(parameters: MockNodeParameters, nodeFactory: (MockNodeArgs) -> N, start: Boolean): N { val id = parameters.forcedID ?: nextNodeId++ val config = testNodeConfiguration( baseDirectory = baseDirectory(id).createDirectories(), @@ -306,7 +297,7 @@ class MockNetwork(defaultParameters: MockNetworkParameters = MockNetworkParamete doReturn(makeTestDataSourceProperties("node_${id}_net_$networkId")).whenever(it).dataSourceProperties parameters.configOverrides(it) } - return nodeFactory.create(MockNodeArgs(config, this, id, parameters.notaryIdentity, parameters.entropyRoot)).apply { + return nodeFactory(MockNodeArgs(config, this, id, parameters.notaryIdentity, parameters.entropyRoot)).apply { if (start) { start() if (threadPerNode) nodeReadyFuture.getOrThrow() // XXX: What about manually-started nodes? @@ -347,7 +338,7 @@ class MockNetwork(defaultParameters: MockNetworkParameters = MockNetworkParamete fun createNotaryNode(parameters: MockNodeParameters = MockNodeParameters(legalName = DUMMY_NOTARY.name), validating: Boolean = true, - nodeFactory: Factory): StartedNode { + nodeFactory: (MockNodeArgs) -> N): StartedNode { return createNode(parameters.copy(configOverrides = { doReturn(NotaryConfig(validating)).whenever(it).notary parameters.configOverrides(it) 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 new file mode 100644 index 0000000000..4e6b9215ca --- /dev/null +++ b/testing/node-driver/src/test/java/net/corda/testing/node/MockNodeFactoryInJavaTest.java @@ -0,0 +1,25 @@ +package net.corda.testing.node; + +import org.jetbrains.annotations.NotNull; + +@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(new MockNetworkParameters().setDefaultFactory(args -> new CustomNode(args))); + new MockNetwork(new MockNetworkParameters().setDefaultFactory(CustomNode::new)); + //noinspection Convert2MethodRef + new MockNetwork().createNode(new MockNodeParameters(), args -> new CustomNode(args)); + new MockNetwork().createNode(new MockNodeParameters(), CustomNode::new); + } +} From f614557ae3770bc5a12b54e2198128a54dfcb7b5 Mon Sep 17 00:00:00 2001 From: Alberto Arri <30873160+al-r3@users.noreply.github.com> Date: Tue, 31 Oct 2017 17:21:17 +0000 Subject: [PATCH 04/24] Remove SimpleNode it's unused (#1971) --- .../net/corda/testing/node/SimpleNode.kt | 77 ------------------- 1 file changed, 77 deletions(-) delete mode 100644 testing/node-driver/src/main/kotlin/net/corda/testing/node/SimpleNode.kt diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/SimpleNode.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/SimpleNode.kt deleted file mode 100644 index 6dad146916..0000000000 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/SimpleNode.kt +++ /dev/null @@ -1,77 +0,0 @@ -package net.corda.testing.node - -import com.codahale.metrics.MetricRegistry -import net.corda.core.crypto.generateKeyPair -import net.corda.core.internal.concurrent.openFuture -import net.corda.core.messaging.RPCOps -import net.corda.core.node.services.IdentityService -import net.corda.core.node.services.KeyManagementService -import net.corda.core.utilities.NetworkHostAndPort -import net.corda.node.services.RPCUserServiceImpl -import net.corda.node.services.api.MonitoringService -import net.corda.node.services.config.NodeConfiguration -import net.corda.node.services.identity.InMemoryIdentityService -import net.corda.node.services.keys.E2ETestKeyManagementService -import net.corda.node.services.messaging.ArtemisMessagingServer -import net.corda.node.services.messaging.NodeMessagingClient -import net.corda.node.services.network.NetworkMapCacheImpl -import net.corda.node.services.schema.NodeSchemaService -import net.corda.node.utilities.AffinityExecutor.ServiceAffinityExecutor -import net.corda.node.utilities.CordaPersistence -import net.corda.node.utilities.configureDatabase -import net.corda.testing.freeLocalHostAndPort -import net.corda.testing.node.MockServices.Companion.MOCK_VERSION_INFO -import java.security.KeyPair -import java.security.cert.X509Certificate -import kotlin.concurrent.thread - -/** - * This is a bare-bones node which can only send and receive messages. It doesn't register with a network map service or - * any other such task that would make it functional in a network and thus left to the user to do so manually. - */ -class SimpleNode(val config: NodeConfiguration, val address: NetworkHostAndPort = freeLocalHostAndPort(), - rpcAddress: NetworkHostAndPort = freeLocalHostAndPort(), - trustRoot: X509Certificate) : AutoCloseable { - - val userService = RPCUserServiceImpl(config.rpcUsers) - val monitoringService = MonitoringService(MetricRegistry()) - val identity: KeyPair = generateKeyPair() - val identityService: IdentityService = InMemoryIdentityService(trustRoot = trustRoot) - val database: CordaPersistence = configureDatabase(config.dataSourceProperties, config.database, { InMemoryIdentityService(trustRoot = trustRoot) }) - val keyService: KeyManagementService = E2ETestKeyManagementService(identityService, setOf(identity)) - val executor = ServiceAffinityExecutor(config.myLegalName.organisation, 1) - // TODO: We should have a dummy service hub rather than change behaviour in tests - val broker = ArtemisMessagingServer(config, address.port, rpcAddress.port, - NetworkMapCacheImpl(MockNetworkMapCache(database, config), identityService), userService) - val networkMapRegistrationFuture = openFuture() - val network = database.transaction { - NodeMessagingClient( - config, - MOCK_VERSION_INFO, - address, - identity.public, - executor, - database, - networkMapRegistrationFuture, - monitoringService) - } - - fun start() { - broker.start() - network.start( - object : RPCOps { - override val protocolVersion = 0 - }, - userService) - thread(name = config.myLegalName.organisation) { - network.run(broker.serverControl) - } - } - - override fun close() { - network.stop() - broker.stop() - database.close() - executor.shutdownNow() - } -} From dcaac916910c93d5de461fa2dde726486f91338a Mon Sep 17 00:00:00 2001 From: Clinton Date: Tue, 31 Oct 2017 20:48:57 +0000 Subject: [PATCH 05/24] Cordformation now allows addresses to be used for non-database addresses. (#1917) Cordformation now allows addresses to be used for non-database addresses. --- build.gradle | 6 ++-- docs/source/deploying-a-node.rst | 7 +++-- .../java/net/corda/cordform/CordformNode.java | 31 +++++++++++++++++-- .../src/main/kotlin/net/corda/plugins/Node.kt | 21 +++++++------ 4 files changed, 47 insertions(+), 18 deletions(-) diff --git a/build.gradle b/build.gradle index 1963375ca9..224e4ba215 100644 --- a/build.gradle +++ b/build.gradle @@ -254,9 +254,9 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) { } node { name "O=Bank B,OU=corda,L=London,C=GB" - p2pPort 10007 - rpcPort 10008 - webPort 10009 + p2pAddress "localhost:10007" + rpcAddress "localhost:10008" + webAddress "localhost:10009" cordapps = [] } } diff --git a/docs/source/deploying-a-node.rst b/docs/source/deploying-a-node.rst index 74f8bdac65..a9187ed6ed 100644 --- a/docs/source/deploying-a-node.rst +++ b/docs/source/deploying-a-node.rst @@ -41,11 +41,12 @@ notary node: cordapps = [] rpcUsers = [[ user: "user1", "password": "test", "permissions": []]] } + // Example of explicit addresses being used. node { name "CN=NodeC,O=NodeC,L=Paris,C=FR" - p2pPort 10011 - rpcPort 10012 - webPort 10013 + p2pAddress "localhost:10011" + rpcAddress "localhost:10012" + webAddress "localhost:10013" cordapps = [] rpcUsers = [[ user: "user1", "password": "test", "permissions": []]] } diff --git a/gradle-plugins/cordform-common/src/main/java/net/corda/cordform/CordformNode.java b/gradle-plugins/cordform-common/src/main/java/net/corda/cordform/CordformNode.java index 7421fee251..f8c4248f99 100644 --- a/gradle-plugins/cordform-common/src/main/java/net/corda/cordform/CordformNode.java +++ b/gradle-plugins/cordform-common/src/main/java/net/corda/cordform/CordformNode.java @@ -58,7 +58,16 @@ public class CordformNode implements NodeDefinition { } /** - * Set the Artemis P2P port for this node. + * Get the artemis address for this node. + * + * @return This node's P2P address. + */ + public String getP2pAddress() { + return config.getString("p2pAddress"); + } + + /** + * Set the Artemis P2P port for this node on localhost. * * @param p2pPort The Artemis messaging queue port. */ @@ -67,7 +76,16 @@ public class CordformNode implements NodeDefinition { } /** - * Set the Artemis RPC port for this node. + * Set the Artemis P2P address for this node. + * + * @param p2pAddress The Artemis messaging queue host and port. + */ + public void p2pAddress(String p2pAddress) { + config = config.withValue("p2pAddress", ConfigValueFactory.fromAnyRef(p2pAddress)); + } + + /** + * Set the Artemis RPC port for this node on localhost. * * @param rpcPort The Artemis RPC queue port. */ @@ -75,6 +93,15 @@ public class CordformNode implements NodeDefinition { config = config.withValue("rpcAddress", ConfigValueFactory.fromAnyRef(DEFAULT_HOST + ':' + rpcPort)); } + /** + * Set the Artemis RPC address for this node. + * + * @param rpcAddress The Artemis RPC queue host and port. + */ + public void rpcAddress(String rpcAddress) { + config = config.withValue("rpcAddress", ConfigValueFactory.fromAnyRef(rpcAddress)); + } + /** * Set the path to a file with optional properties, which are appended to the generated node.conf file. * 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 ef98848e18..f03e650772 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 @@ -61,7 +61,7 @@ class Node(private val project: Project) : CordformNode() { } /** - * Set the HTTP web server port for this node. + * Set the HTTP web server port for this node. Will use localhost as the address. * * @param webPort The web port number for this node. */ @@ -70,6 +70,16 @@ class Node(private val project: Project) : CordformNode() { ConfigValueFactory.fromAnyRef("$DEFAULT_HOST:$webPort")) } + /** + * Set the HTTP web server address and port for this node. + * + * @param webAddress The web address for this node. + */ + fun webAddress(webAddress: String) { + config = config.withValue("webAddress", + ConfigValueFactory.fromAnyRef(webAddress)) + } + /** * Set the network map address for this node. * @@ -107,15 +117,6 @@ class Node(private val project: Project) : CordformNode() { appendOptionalConfig() } - /** - * Get the artemis address for this node. - * - * @return This node's P2P address. - */ - fun getP2PAddress(): String { - return config.getString("p2pAddress") - } - internal fun rootDir(rootDir: Path) { if(name == null) { project.logger.error("Node has a null name - cannot create node") From 09c836f5e6af848ad0c34f9cdb62af2ec5568e3e Mon Sep 17 00:00:00 2001 From: Clinton Alexander Date: Tue, 31 Oct 2017 20:50:26 +0000 Subject: [PATCH 06/24] Bumped gradle plugins to 2.0.8 for publishing. --- constants.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/constants.properties b/constants.properties index c31f20735c..fed26dac6b 100644 --- a/constants.properties +++ b/constants.properties @@ -1,4 +1,4 @@ -gradlePluginsVersion=2.0.7 +gradlePluginsVersion=2.0.8 kotlinVersion=1.1.50 guavaVersion=21.0 bouncycastleVersion=1.57 From e564edaa572a011869d692b05518113632363216 Mon Sep 17 00:00:00 2001 From: Clinton Alexander Date: Tue, 31 Oct 2017 20:57:32 +0000 Subject: [PATCH 07/24] Line endings fix. --- constants.properties | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/constants.properties b/constants.properties index fed26dac6b..a780da9214 100644 --- a/constants.properties +++ b/constants.properties @@ -1,5 +1,5 @@ -gradlePluginsVersion=2.0.8 +gradlePluginsVersion=2.0.8 kotlinVersion=1.1.50 guavaVersion=21.0 bouncycastleVersion=1.57 -typesafeConfigVersion=1.3.1 +typesafeConfigVersion=1.3.1 \ No newline at end of file From 00e682a54476aded7a329521ff53d8d185ec8b95 Mon Sep 17 00:00:00 2001 From: Alberto Arri <30873160+al-r3@users.noreply.github.com> Date: Wed, 1 Nov 2017 14:25:48 +0000 Subject: [PATCH 08/24] [CORDA-446] Clean up other mentions of network map node and logic (#1974) * [CORDA-446] Clean up other mentions of network map node and logic * Rename AbstractNetworkMapService to NetworkMapService and remove the empty NetworkMapService * fix build * fix artemismessaging tests * pr comments --- config/dev/generalnodeb.conf | 4 - docs/source/azure-vm.rst | 2 +- docs/source/demobench.rst | 2 +- .../example-out-of-process-verifier-node.conf | 4 - docs/source/messaging.rst | 5 - docs/source/node-services.rst | 21 +- docs/source/setting-up-a-corda-network.rst | 3 +- .../net/corda/node/internal/AbstractNode.kt | 13 - .../kotlin/net/corda/node/internal/Node.kt | 61 +--- .../node/services/api/ServiceHubInternal.kt | 8 - .../messaging/ArtemisMessagingServer.kt | 2 +- .../services/messaging/NodeMessagingClient.kt | 85 +---- .../services/network/NetworkMapService.kt | 310 +----------------- .../network/PersistentNetworkMapService.kt | 15 +- .../messaging/ArtemisMessagingTests.kt | 31 +- .../net/corda/netmap/NetworkMapVisualiser.kt | 2 - samples/trader-demo/README.md | 2 +- .../kotlin/net/corda/testing/driver/Driver.kt | 10 +- .../kotlin/net/corda/testing/node/MockNode.kt | 4 +- .../kotlin/net/corda/testing/TestConstants.kt | 5 - 20 files changed, 53 insertions(+), 536 deletions(-) diff --git a/config/dev/generalnodeb.conf b/config/dev/generalnodeb.conf index af4e26cc27..55ebb1e690 100644 --- a/config/dev/generalnodeb.conf +++ b/config/dev/generalnodeb.conf @@ -4,8 +4,4 @@ trustStorePassword : "trustpass" p2pAddress : "localhost:10005" rpcAddress : "localhost:10006" webAddress : "localhost:10007" -networkMapService : { - address : "localhost:10000" - legalName : "O=Network Map Service,OU=corda,L=London,C=GB" -} useHTTPS : false diff --git a/docs/source/azure-vm.rst b/docs/source/azure-vm.rst index 7f94f0fdcb..ac8923bed1 100644 --- a/docs/source/azure-vm.rst +++ b/docs/source/azure-vm.rst @@ -3,7 +3,7 @@ Building a Corda Network on Azure Marketplace To help you design, build and test applications on Corda, called CorDapps, a Corda network can be deployed on the `Microsoft Azure Marketplace `_ -This Corda network offering builds a pre-configured network of Corda nodes as Ubuntu virtual machines (VM). The network comprises of a Network Map Service node, a Notary node and up to nine Corda nodes using a version of Corda of your choosing. The following guide will also show you how to load a simple Yo! CorDapp which demonstrates the basic principles of Corda. When you are ready to go further with developing on Corda and start making contributions to the project head over to the `Corda.net `_. +This Corda network offering builds a pre-configured network of Corda nodes as Ubuntu virtual machines (VM). The network comprises of a Notary node and up to nine Corda nodes using a version of Corda of your choosing. The following guide will also show you how to load a simple Yo! CorDapp which demonstrates the basic principles of Corda. When you are ready to go further with developing on Corda and start making contributions to the project head over to the `Corda.net `_. Pre-requisites -------------- diff --git a/docs/source/demobench.rst b/docs/source/demobench.rst index 374f12234a..24b2948806 100644 --- a/docs/source/demobench.rst +++ b/docs/source/demobench.rst @@ -16,7 +16,7 @@ Running DemoBench Configuring a Node Each node must have a unique name to identify it to the network map service. DemoBench will suggest node names, nearest cities and local port numbers to use. - The first node will host the network map service, and we are forcing that node also to be a notary. Hence only notary services will be available to be selected in the ``Services`` list. For subsequent nodes you may also select any of Corda's other built-in services. + The first node will be a notary. Hence only notary services will be available to be selected in the ``Services`` list. For subsequent nodes you may also select any of Corda's other built-in services. Press the ``Start node`` button to launch the Corda node with your configuration. diff --git a/docs/source/example-code/src/main/resources/example-out-of-process-verifier-node.conf b/docs/source/example-code/src/main/resources/example-out-of-process-verifier-node.conf index 8d43f51a9f..27bf43a1ab 100644 --- a/docs/source/example-code/src/main/resources/example-out-of-process-verifier-node.conf +++ b/docs/source/example-code/src/main/resources/example-out-of-process-verifier-node.conf @@ -1,8 +1,4 @@ myLegalName : "O=Bank A,L=London,C=GB" p2pAddress : "my-corda-node:10002" webAddress : "localhost:10003" -networkMapService : { - address : "my-network-map:10000" - legalName : "O=Network Map Service,OU=corda,L=London,C=GB" -} verifierType: "OutOfProcess" diff --git a/docs/source/messaging.rst b/docs/source/messaging.rst index 2bd301d3bc..55f48418d3 100644 --- a/docs/source/messaging.rst +++ b/docs/source/messaging.rst @@ -65,11 +65,6 @@ for maintenance and other minor purposes. corresponding bridge is used to forward the message to an advertising peer's p2p queue. Once a peer is picked the session continues on as normal. -:``internal.networkmap``: - This is another private queue just for the node which functions in a similar manner to the ``internal.peers.*`` queues - except this is used to form a connection to the network map node. The node running the network map service is treated - differently as it provides information about the rest of the network. - :``rpc.requests``: RPC clients send their requests here, and it's only open for sending by clients authenticated as RPC users. diff --git a/docs/source/node-services.rst b/docs/source/node-services.rst index b63dbfc942..edcf20b391 100644 --- a/docs/source/node-services.rst +++ b/docs/source/node-services.rst @@ -139,28 +139,17 @@ physical host and port information required for the physical ``ArtemisMQ`` messaging layer. -PersistentNetworkMapService and NetworkMapService -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +PersistentNetworkMapService +~~~~~~~~~~~~~~~~~~~~~~~~~~~ -The ``NetworkMapService`` is a node internal component responsible for -managing and communicating the directory of authenticated registered -nodes and advertised services in the Corda network. Only a single node -in the network (in future this will be a clustered service) should host -the NetworkMapService implementation. All other Corda nodes initiate -their remote connection to the ``NetworkMapService`` early in the -start-up sequence and wait to synchronise their local -``NetworkMapCache`` before activating any flows. For the -``PersistentNetworkMapService`` registered ``NodeInfo`` data is -persisted and will include nodes that are not currently active. The -networking layer will persist any messages directed at such inactive +The ``PersistentNetworkMapService`` keeps track of ``NodeInfo`` and +persists it to the database. It and will include nodes that are not currently active. +The networking layer will persist any messages directed at such inactive nodes with the expectation that they will be delivered eventually, or else that the source flow will be terminated by admin intervention. An ``InMemoryNetworkMapService`` is also available for unit tests without a database. -The ``NetworkMapService`` should not be used by any flows, or -contracts. Instead they should access the NetworkMapCache service to -access this data. Storage and persistence related services ---------------------------------------- diff --git a/docs/source/setting-up-a-corda-network.rst b/docs/source/setting-up-a-corda-network.rst index 18a2172058..41a44d300f 100644 --- a/docs/source/setting-up-a-corda-network.rst +++ b/docs/source/setting-up-a-corda-network.rst @@ -3,8 +3,7 @@ Creating a Corda network ======================== -A Corda network consists of a number of machines running nodes, including a single node operating as the network map -service. These nodes communicate using persistent protocols in order to create and validate transactions. +A Corda network consists of a number of machines running nodes. These nodes communicate using persistent protocols in order to create and validate transactions. There are four broader categories of functionality one such node may have. These pieces of functionality are provided as services, and one node may run several of them. 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 1b49934765..e61471a8e1 100644 --- a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt +++ b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt @@ -42,10 +42,7 @@ import net.corda.node.services.events.ScheduledActivityObserver import net.corda.node.services.identity.PersistentIdentityService import net.corda.node.services.keys.PersistentKeyManagementService import net.corda.node.services.messaging.MessagingService -import net.corda.node.services.messaging.sendRequest import net.corda.node.services.network.* -import net.corda.node.services.network.NetworkMapService.RegistrationRequest -import net.corda.node.services.network.NetworkMapService.RegistrationResponse import net.corda.node.services.persistence.DBCheckpointStorage import net.corda.node.services.persistence.DBTransactionMappingStorage import net.corda.node.services.persistence.DBTransactionStorage @@ -58,7 +55,6 @@ import net.corda.node.services.upgrade.ContractUpgradeServiceImpl import net.corda.node.services.vault.NodeVaultService import net.corda.node.services.vault.VaultSoftLockManager import net.corda.node.utilities.* -import net.corda.node.utilities.AddOrRemove.ADD import org.apache.activemq.artemis.utils.ReusableLatch import org.slf4j.Logger import rx.Observable @@ -553,15 +549,6 @@ abstract class AbstractNode(config: NodeConfiguration, } } - private fun sendNetworkMapRegistration(networkMapAddress: SingleMessageRecipient): CordaFuture { - // Register this node against the network - val instant = platformClock.instant() - val expires = instant + NetworkMapService.DEFAULT_EXPIRATION_PERIOD - val reg = NodeRegistration(info, info.serial, ADD, expires) - val request = RegistrationRequest(reg.toWire(services.keyManagementService, info.legalIdentitiesAndCerts.first().owningKey), network.myAddress) - return network.sendRequest(NetworkMapService.REGISTER_TOPIC, request, networkMapAddress) - } - /** Return list of node's addresses. It's overridden in MockNetwork as we don't have real addresses for MockNodes. */ protected abstract fun myAddresses(): List diff --git a/node/src/main/kotlin/net/corda/node/internal/Node.kt b/node/src/main/kotlin/net/corda/node/internal/Node.kt index 20eb06247a..8a4e18c290 100644 --- a/node/src/main/kotlin/net/corda/node/internal/Node.kt +++ b/node/src/main/kotlin/net/corda/node/internal/Node.kt @@ -11,7 +11,8 @@ import net.corda.core.internal.uncheckedCast import net.corda.core.messaging.RPCOps import net.corda.core.node.ServiceHub import net.corda.core.serialization.SerializationDefaults -import net.corda.core.utilities.* +import net.corda.core.utilities.NetworkHostAndPort +import net.corda.core.utilities.loggerFor import net.corda.node.VersionInfo import net.corda.node.internal.cordapp.CordappLoader import net.corda.node.serialization.KryoServerSerializationScheme @@ -22,8 +23,6 @@ import net.corda.node.services.api.NetworkMapCacheInternal import net.corda.node.services.api.SchemaService import net.corda.node.services.config.NodeConfiguration import net.corda.node.services.messaging.ArtemisMessagingServer -import net.corda.node.services.messaging.ArtemisMessagingServer.Companion.ipDetectRequestProperty -import net.corda.node.services.messaging.ArtemisMessagingServer.Companion.ipDetectResponseProperty import net.corda.node.services.messaging.MessagingService import net.corda.node.services.messaging.NodeMessagingClient import net.corda.node.services.network.NetworkMapService @@ -32,10 +31,6 @@ import net.corda.node.utilities.AddressUtils import net.corda.node.utilities.AffinityExecutor import net.corda.node.utilities.TestClock import net.corda.nodeapi.ArtemisMessagingComponent -import net.corda.nodeapi.ArtemisMessagingComponent.Companion.IP_REQUEST_PREFIX -import net.corda.nodeapi.ArtemisMessagingComponent.Companion.PEER_USER -import net.corda.nodeapi.ArtemisTcpTransport -import net.corda.nodeapi.ConnectionDirection import net.corda.nodeapi.internal.ShutdownHook import net.corda.nodeapi.internal.addShutdownHook import net.corda.nodeapi.internal.serialization.* @@ -46,9 +41,7 @@ import org.apache.activemq.artemis.api.core.client.ActiveMQClient import org.apache.activemq.artemis.api.core.client.ClientMessage import org.slf4j.Logger import org.slf4j.LoggerFactory -import java.io.IOException import java.time.Clock -import java.util.* import java.util.concurrent.atomic.AtomicInteger import javax.management.ObjectName import kotlin.system.exitProcess @@ -160,7 +153,6 @@ open class Node(override val configuration: NodeConfiguration, legalIdentity.owningKey, serverThread, database, - nodeReadyFuture, services.monitoringService, advertisedAddress) } @@ -200,53 +192,6 @@ open class Node(override val configuration: NodeConfiguration, return null } - /** - * Asks the network map service to provide this node's public IP address: - * - Connects to the network map service's message broker and creates a special IP request queue with a custom - * request id. Marks the established session with the same request id. - * - On the server side a special post-queue-creation callback is fired. It finds the session matching the request id - * encoded in the queue name. It then extracts the remote IP from the session details and posts a message containing - * it back to the queue. - * - Once the message is received the session is closed and the queue deleted. - */ - private fun discoverPublicHost(serverAddress: NetworkHostAndPort): String? { - log.trace { "Trying to detect public hostname through the Network Map Service at $serverAddress" } - val tcpTransport = ArtemisTcpTransport.tcpTransport(ConnectionDirection.Outbound(), serverAddress, configuration) - val locator = ActiveMQClient.createServerLocatorWithoutHA(tcpTransport).apply { - initialConnectAttempts = 2 // TODO Public host discovery needs rewriting, as we may start nodes without network map, and we don't want to wait that long on startup. - retryInterval = 2.seconds.toMillis() - retryIntervalMultiplier = 1.5 - maxRetryInterval = 3.minutes.toMillis() - } - val clientFactory = try { - locator.createSessionFactory() - } catch (e: ActiveMQNotConnectedException) { - log.warn("Unable to connect to the Network Map Service at $serverAddress for IP address discovery. " + - "Using the provided \"${configuration.p2pAddress.host}\" as the advertised address.") - return null - } - - val session = clientFactory.createSession(PEER_USER, PEER_USER, false, true, true, locator.isPreAcknowledge, ActiveMQClient.DEFAULT_ACK_BATCH_SIZE) - val requestId = UUID.randomUUID().toString() - session.addMetaData(ipDetectRequestProperty, requestId) - session.start() - - val queueName = "$IP_REQUEST_PREFIX$requestId" - session.createQueue(queueName, RoutingType.MULTICAST, queueName, false) - - val consumer = session.createConsumer(queueName) - val artemisMessage: ClientMessage = consumer.receive(10.seconds.toMillis()) ?: - throw IOException("Did not receive a response from the Network Map Service at $serverAddress") - val publicHostAndPort = artemisMessage.getStringProperty(ipDetectResponseProperty) - log.info("Detected public address: $publicHostAndPort") - - consumer.close() - session.deleteQueue(queueName) - clientFactory.close() - - return NetworkHostAndPort.parse(publicHostAndPort.removePrefix("/")).host - } - override fun startMessagingService(rpcOps: RPCOps) { // Start up the embedded MQ server messageBroker?.apply { @@ -264,7 +209,7 @@ open class Node(override val configuration: NodeConfiguration, } override fun makeNetworkMapService(network: MessagingService, networkMapCache: NetworkMapCacheInternal): NetworkMapService { - return PersistentNetworkMapService(network, networkMapCache, configuration.minimumPlatformVersion) + return PersistentNetworkMapService() } /** diff --git a/node/src/main/kotlin/net/corda/node/services/api/ServiceHubInternal.kt b/node/src/main/kotlin/net/corda/node/services/api/ServiceHubInternal.kt index f2d888d54d..d54deb153a 100644 --- a/node/src/main/kotlin/net/corda/node/services/api/ServiceHubInternal.kt +++ b/node/src/main/kotlin/net/corda/node/services/api/ServiceHubInternal.kt @@ -1,6 +1,5 @@ package net.corda.node.services.api -import net.corda.core.CordaException import net.corda.core.concurrent.CordaFuture import net.corda.core.crypto.SecureHash import net.corda.core.flows.FlowInitiator @@ -18,7 +17,6 @@ import net.corda.core.node.StatesToRecord import net.corda.core.node.services.NetworkMapCache import net.corda.core.node.services.NetworkMapCacheBase import net.corda.core.node.services.TransactionStorage -import net.corda.core.serialization.CordaSerializable import net.corda.core.transactions.SignedTransaction import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.loggerFor @@ -42,12 +40,6 @@ interface NetworkMapCacheBaseInternal : NetworkMapCacheBase { val loadDBSuccess: Boolean } -@CordaSerializable -sealed class NetworkCacheException : CordaException("Network Cache Error") { - /** Indicates a failure to deregister, because of a rejected request from the remote node */ - class DeregistrationFailed : NetworkCacheException() -} - interface ServiceHubInternal : ServiceHub { companion object { private val log = loggerFor() diff --git a/node/src/main/kotlin/net/corda/node/services/messaging/ArtemisMessagingServer.kt b/node/src/main/kotlin/net/corda/node/services/messaging/ArtemisMessagingServer.kt index 12951716c9..449b232aec 100644 --- a/node/src/main/kotlin/net/corda/node/services/messaging/ArtemisMessagingServer.kt +++ b/node/src/main/kotlin/net/corda/node/services/messaging/ArtemisMessagingServer.kt @@ -116,7 +116,7 @@ class ArtemisMessagingServer(override val config: NodeConfiguration, /** * The server will make sure the bridge exists on network map changes, see method [updateBridgesOnNetworkChange] - * We assume network map will be updated accordingly when the client node register with the network map server. + * We assume network map will be updated accordingly when the client node register with the network map. */ @Throws(IOException::class, KeyStoreException::class) fun start() = mutex.locked { diff --git a/node/src/main/kotlin/net/corda/node/services/messaging/NodeMessagingClient.kt b/node/src/main/kotlin/net/corda/node/services/messaging/NodeMessagingClient.kt index d9b0aa0f13..39becee90a 100644 --- a/node/src/main/kotlin/net/corda/node/services/messaging/NodeMessagingClient.kt +++ b/node/src/main/kotlin/net/corda/node/services/messaging/NodeMessagingClient.kt @@ -1,11 +1,8 @@ package net.corda.node.services.messaging -import net.corda.core.concurrent.CordaFuture import net.corda.core.crypto.random63BitValue import net.corda.core.identity.CordaX500Name import net.corda.core.internal.ThreadBox -import net.corda.core.internal.concurrent.andForget -import net.corda.core.internal.concurrent.thenMatch import net.corda.core.messaging.CordaRPCOps import net.corda.core.messaging.MessageRecipients import net.corda.core.messaging.RPCOps @@ -16,7 +13,10 @@ import net.corda.core.serialization.SerializationDefaults import net.corda.core.serialization.deserialize import net.corda.core.serialization.serialize import net.corda.core.transactions.LedgerTransaction -import net.corda.core.utilities.* +import net.corda.core.utilities.NetworkHostAndPort +import net.corda.core.utilities.loggerFor +import net.corda.core.utilities.sequence +import net.corda.core.utilities.trace import net.corda.node.VersionInfo import net.corda.node.services.RPCUserService import net.corda.node.services.api.MonitoringService @@ -65,8 +65,7 @@ import javax.persistence.Lob * CordaRPCClient class. * * @param serverAddress The address of the broker instance to connect to (might be running in the same process). - * @param myIdentity Either the public key to be used as the ArtemisMQ address and queue name for the node globally, or null to indicate - * that this is a NetworkMapService node which will be bound globally to the name "networkmap". + * @param myIdentity The public key to be used as the ArtemisMQ address and queue name for the node. * @param nodeExecutor An executor to run received message tasks upon. * @param advertisedAddress The node address for inbound connections, advertised to the network map service and peers. * If not provided, will default to [serverAddress]. @@ -78,7 +77,6 @@ class NodeMessagingClient(override val config: NodeConfiguration, private val myIdentity: PublicKey, private val nodeExecutor: AffinityExecutor.ServiceAffinityExecutor, val database: CordaPersistence, - private val networkMapRegistrationFuture: CordaFuture, val monitoringService: MonitoringService, advertisedAddress: NetworkHostAndPort = serverAddress ) : ArtemisMessagingComponent(), MessagingService { @@ -234,18 +232,7 @@ class NodeMessagingClient(override val config: NodeConfiguration, this.producer = producer // Create a queue, consumer and producer for handling P2P network messages. - p2pConsumer = makeP2PConsumer(session, true) - networkMapRegistrationFuture.thenMatch({ - state.locked { - log.info("Network map is complete, so removing filter from P2P consumer.") - try { - p2pConsumer!!.close() - } catch (e: ActiveMQObjectClosedException) { - // Ignore it: this can happen if the server has gone away before we do. - } - p2pConsumer = makeP2PConsumer(session, false) - } - }, {}) + p2pConsumer = session.createConsumer(P2P_QUEUE) val myCert = loadKeyStore(config.sslKeystore, config.keyStorePassword).getX509Certificate(X509Utilities.CORDA_CLIENT_TLS) rpcServer = RPCServer(rpcOps, NODE_USER, NODE_USER, locator, userService, CordaX500Name.build(myCert.subjectX500Principal)) @@ -267,20 +254,6 @@ class NodeMessagingClient(override val config: NodeConfiguration, resumeMessageRedelivery() } - /** - * We make the consumer twice, once to filter for just network map messages, and then once that is complete, we close - * the original and make another without a filter. We do this so that there is a network map in place for all other - * message handlers. - */ - private fun makeP2PConsumer(session: ClientSession, networkMapOnly: Boolean): ClientConsumer { - return if (networkMapOnly) { - // Filter for just the network map messages. - val messageFilter = "hyphenated_props:$topicProperty like 'platform.network_map.%'" - session.createConsumer(P2P_QUEUE, messageFilter) - } else - session.createConsumer(P2P_QUEUE) - } - private fun resumeMessageRedelivery() { messagesToRedeliver.forEach { retryId, (message, target) -> send(message, target, retryId) @@ -325,46 +298,22 @@ class NodeMessagingClient(override val config: NodeConfiguration, return true } - private fun runPreNetworkMap(serverControl: ActiveMQServerControl) { - val consumer = state.locked { - check(started) { "start must be called first" } - check(!running) { "run can't be called twice" } - running = true - rpcServer!!.start(serverControl) - (verifierService as? OutOfProcessTransactionVerifierService)?.start(verificationResponseConsumer!!) - p2pConsumer!! - } - - while (!networkMapRegistrationFuture.isDone && processMessage(consumer)) { - } - with(networkMapRegistrationFuture) { - if (isDone) getOrThrow() else andForget(log) // Trigger node shutdown here to avoid deadlock in shutdown hooks. - } - } - - private fun runPostNetworkMap() { - val consumer = state.locked { - // If it's null, it means we already called stop, so return immediately. - p2pConsumer ?: return - } - - while (processMessage(consumer)) { - } - } - /** * Starts the p2p event loop: this method only returns once [stop] has been called. - * - * This actually runs as two sequential loops. The first subscribes for and receives only network map messages until - * we get our network map fetch response. At that point the filtering consumer is closed and we proceed to the second loop and - * consume all messages via a new consumer without a filter applied. */ fun run(serverControl: ActiveMQServerControl) { try { - // Build the network map. - runPreNetworkMap(serverControl) - // Process everything else once we have the network map. - runPostNetworkMap() + val consumer = state.locked { + check(started) { "start must be called first" } + check(!running) { "run can't be called twice" } + running = true + rpcServer!!.start(serverControl) + (verifierService as? OutOfProcessTransactionVerifierService)?.start(verificationResponseConsumer!!) + // If it's null, it means we already called stop, so return immediately. + p2pConsumer ?: return + } + + while (processMessage(consumer)) { } } finally { shutdownLatch.countDown() } diff --git a/node/src/main/kotlin/net/corda/node/services/network/NetworkMapService.kt b/node/src/main/kotlin/net/corda/node/services/network/NetworkMapService.kt index 06b04e55a7..0c6499ec96 100644 --- a/node/src/main/kotlin/net/corda/node/services/network/NetworkMapService.kt +++ b/node/src/main/kotlin/net/corda/node/services/network/NetworkMapService.kt @@ -1,48 +1,14 @@ package net.corda.node.services.network -import net.corda.core.CordaException -import net.corda.core.crypto.DigitalSignature -import net.corda.core.crypto.SignedData -import net.corda.core.crypto.isFulfilledBy -import net.corda.core.crypto.random63BitValue import net.corda.core.identity.PartyAndCertificate import net.corda.core.internal.ThreadBox -import net.corda.core.internal.VisibleForTesting -import net.corda.core.messaging.MessageRecipients import net.corda.core.messaging.SingleMessageRecipient import net.corda.core.node.NodeInfo -import net.corda.core.node.services.KeyManagementService import net.corda.core.node.services.NetworkMapCache import net.corda.core.serialization.CordaSerializable -import net.corda.core.serialization.SerializedBytes -import net.corda.core.serialization.deserialize -import net.corda.core.serialization.serialize -import net.corda.core.utilities.debug -import net.corda.core.utilities.loggerFor -import net.corda.node.services.api.AbstractNodeService -import net.corda.node.services.api.NetworkMapCacheInternal -import net.corda.node.services.messaging.MessageHandlerRegistration -import net.corda.node.services.messaging.MessagingService -import net.corda.node.services.messaging.ServiceRequestMessage -import net.corda.node.services.messaging.createMessage -import net.corda.node.services.network.NetworkMapService.* -import net.corda.node.services.network.NetworkMapService.Companion.FETCH_TOPIC -import net.corda.node.services.network.NetworkMapService.Companion.PUSH_ACK_TOPIC -import net.corda.node.services.network.NetworkMapService.Companion.PUSH_TOPIC -import net.corda.node.services.network.NetworkMapService.Companion.QUERY_TOPIC -import net.corda.node.services.network.NetworkMapService.Companion.REGISTER_TOPIC -import net.corda.node.services.network.NetworkMapService.Companion.SUBSCRIPTION_TOPIC import net.corda.node.utilities.AddOrRemove -import net.corda.node.utilities.AddOrRemove.ADD -import net.corda.node.utilities.AddOrRemove.REMOVE -import java.io.IOException -import java.security.PublicKey -import java.security.SignatureException import java.time.Instant -import java.time.Period -import java.util.* import java.util.concurrent.ConcurrentHashMap -import java.util.concurrent.atomic.AtomicInteger import javax.annotation.concurrent.ThreadSafe /** @@ -59,256 +25,23 @@ import javax.annotation.concurrent.ThreadSafe // It may also be that this is replaced or merged with the identity management service; for example if the network has // a concept of identity changes over time, should that include the node for an identity? If so, that is likely to // replace this service. +@ThreadSafe interface NetworkMapService { - companion object { - val DEFAULT_EXPIRATION_PERIOD: Period = Period.ofWeeks(4) - const val FETCH_TOPIC = "platform.network_map.fetch" - const val QUERY_TOPIC = "platform.network_map.query" - const val REGISTER_TOPIC = "platform.network_map.register" - const val SUBSCRIPTION_TOPIC = "platform.network_map.subscribe" - // Base topic used when pushing out updates to the network map. Consumed, for example, by the map cache. - // When subscribing to these updates, remember they must be acknowledged - const val PUSH_TOPIC = "platform.network_map.push" - // Base topic for messages acknowledging pushed updates - const val PUSH_ACK_TOPIC = "platform.network_map.push_ack" - } + val nodeRegistrations: Map - data class FetchMapRequest(val subscribe: Boolean, - val ifChangedSinceVersion: Int?, - override val replyTo: SingleMessageRecipient, - override val sessionID: Long = random63BitValue()) : ServiceRequestMessage - - @CordaSerializable - data class FetchMapResponse(val nodes: List?, val version: Int) - - data class QueryIdentityRequest(val identity: PartyAndCertificate, - override val replyTo: SingleMessageRecipient, - override val sessionID: Long = random63BitValue()) : ServiceRequestMessage - - @CordaSerializable - data class QueryIdentityResponse(val node: NodeInfo?) - - // TODO Rename this RegistractionChangeRequest or similar (and related classes) - data class RegistrationRequest(val wireReg: WireNodeRegistration, - override val replyTo: SingleMessageRecipient, - override val sessionID: Long = random63BitValue()) : ServiceRequestMessage - - /** If [error] is null then the registration was successful. If not null then it wasn't and it explains why */ - @CordaSerializable - data class RegistrationResponse(val error: String?) - - data class SubscribeRequest(val subscribe: Boolean, - override val replyTo: SingleMessageRecipient, - override val sessionID: Long = random63BitValue()) : ServiceRequestMessage - - @CordaSerializable - data class SubscribeResponse(val confirmed: Boolean) - - @CordaSerializable - data class Update(val wireReg: WireNodeRegistration, val mapVersion: Int, val replyTo: MessageRecipients) - - @CordaSerializable - data class UpdateAcknowledge(val mapVersion: Int, val replyTo: MessageRecipients) + // Map from subscriber address, to most recently acknowledged update map version. + val subscribers: ThreadBox> } -object NullNetworkMapService : NetworkMapService @ThreadSafe -class InMemoryNetworkMapService(network: MessagingService, networkMapCache: NetworkMapCacheInternal, minimumPlatformVersion: Int) - : AbstractNetworkMapService(network, networkMapCache, minimumPlatformVersion) { +class InMemoryNetworkMapService: NetworkMapService { override val nodeRegistrations: MutableMap = ConcurrentHashMap() override val subscribers = ThreadBox(mutableMapOf()) - - init { - setup() - } } -/** - * Abstracted out core functionality as the basis for a persistent implementation, as well as existing in-memory implementation. - * - * Design is slightly refactored to track time and map version of last acknowledge per subscriber to facilitate - * subscriber clean up and is simpler to persist than the previous implementation based on a set of missing messages acks. - */ -@ThreadSafe -abstract class AbstractNetworkMapService(network: MessagingService, - private val networkMapCache: NetworkMapCacheInternal, - private val minimumPlatformVersion: Int) : NetworkMapService, AbstractNodeService(network) { - companion object { - /** - * Maximum credible size for a registration request. Generally requests are around 2000-6000 bytes, so this gives a - * 10 times overhead. - */ - private const val MAX_SIZE_REGISTRATION_REQUEST_BYTES = 40000 - private val logger = loggerFor() - } - - protected abstract val nodeRegistrations: MutableMap - - // Map from subscriber address, to most recently acknowledged update map version. - protected abstract val subscribers: ThreadBox> - - protected val _mapVersion = AtomicInteger(0) - - @VisibleForTesting - val mapVersion: Int - get() = _mapVersion.get() - - /** Maximum number of unacknowledged updates to send to a node before automatically unregistering them for updates */ - val maxUnacknowledgedUpdates = 10 - - private val handlers = ArrayList() - protected fun setup() { - // Register message handlers - handlers += addMessageHandler(FETCH_TOPIC) { req: FetchMapRequest -> processFetchAllRequest(req) } - handlers += addMessageHandler(QUERY_TOPIC) { req: QueryIdentityRequest -> processQueryRequest(req) } - handlers += addMessageHandler(REGISTER_TOPIC) { req: RegistrationRequest -> processRegistrationRequest(req) } - handlers += addMessageHandler(SUBSCRIPTION_TOPIC) { req: SubscribeRequest -> processSubscriptionRequest(req) } - handlers += network.addMessageHandler(PUSH_ACK_TOPIC) { message, _ -> - val req = message.data.deserialize() - processAcknowledge(req) - } - } - - @VisibleForTesting - fun unregisterNetworkHandlers() { - for (handler in handlers) { - network.removeMessageHandler(handler) - } - handlers.clear() - } - - private fun addSubscriber(subscriber: MessageRecipients) { - if (subscriber !is SingleMessageRecipient) throw NodeMapException.InvalidSubscriber() - subscribers.locked { - if (!containsKey(subscriber)) { - put(subscriber, LastAcknowledgeInfo(mapVersion)) - } - } - } - - private fun removeSubscriber(subscriber: MessageRecipients) { - if (subscriber !is SingleMessageRecipient) throw NodeMapException.InvalidSubscriber() - subscribers.locked { remove(subscriber) } - } - - private fun processAcknowledge(request: UpdateAcknowledge) { - if (request.replyTo !is SingleMessageRecipient) throw NodeMapException.InvalidSubscriber() - subscribers.locked { - val lastVersionAcked = this[request.replyTo]?.mapVersion - if ((lastVersionAcked ?: 0) < request.mapVersion) { - this[request.replyTo] = LastAcknowledgeInfo(request.mapVersion) - } - } - } - - private fun processFetchAllRequest(request: FetchMapRequest): FetchMapResponse { - if (request.subscribe) { - addSubscriber(request.replyTo) - } - val currentVersion = mapVersion - val nodeRegistrations = if (request.ifChangedSinceVersion == null || request.ifChangedSinceVersion < currentVersion) { - // We return back the current state of the entire map including nodes that have been removed - ArrayList(nodeRegistrations.values.map { it.reg }) // Snapshot to avoid attempting to serialise Map internals - } else { - null - } - return FetchMapResponse(nodeRegistrations, currentVersion) - } - - private fun processQueryRequest(request: QueryIdentityRequest): QueryIdentityResponse { - val candidate = nodeRegistrations[request.identity]?.reg - // If the most recent record we have is of the node being removed from the map, then it's considered - // as no match. - val node = if (candidate == null || candidate.type == REMOVE) null else candidate.node - return QueryIdentityResponse(node) - } - - private fun processRegistrationRequest(request: RegistrationRequest): RegistrationResponse { - val requestSize = request.wireReg.raw.size - logger.debug { "Received registration request of size: $requestSize" } - if (requestSize > MAX_SIZE_REGISTRATION_REQUEST_BYTES) { - return RegistrationResponse("Request is too big") - } - - val change = try { - request.wireReg.verified() - } catch (e: SignatureException) { - return RegistrationResponse("Invalid signature on request") - } catch (e: IOException) { - val msg = "Unexpected IO exception: ${e.message}" - logger.error(msg, e) - return RegistrationResponse(msg) - } - val node = change.node - // Get identity from signature on node's registration and use it as an index. - val identity = node.legalIdentitiesAndCerts.singleOrNull { request.wireReg.sig.by == it.owningKey } - identity ?: return RegistrationResponse("Key from signature on the node registration wasn't found in NodeInfo") - - if (node.platformVersion < minimumPlatformVersion) { - return RegistrationResponse("Minimum platform version requirement not met: $minimumPlatformVersion") - } - - // Update the current value atomically, so that if multiple updates come - // in on different threads, there is no risk of a race condition while checking - // sequence numbers. - val registrationInfo = try { - nodeRegistrations.compute(identity) { _, existing: NodeRegistrationInfo? -> - require(!((existing == null || existing.reg.type == REMOVE) && change.type == REMOVE)) { - "Attempting to de-register unknown node" - } - require(existing == null || existing.reg.serial < change.serial) { "Serial value is too small" } - NodeRegistrationInfo(change, _mapVersion.incrementAndGet()) - } - } catch (e: IllegalArgumentException) { - return RegistrationResponse(e.message) - } - - notifySubscribers(request.wireReg, registrationInfo!!.mapVersion) - - // Update the local cache - // TODO: Once local messaging is fixed, this should go over the network layer as it does to other - // subscribers - when (change.type) { - ADD -> { - logger.info("Added node ${node.addresses} to network map") - networkMapCache.addNode(change.node) - } - REMOVE -> { - logger.info("Removed node ${node.addresses} from network map") - networkMapCache.removeNode(change.node) - } - } - - return RegistrationResponse(null) - } - - private fun notifySubscribers(wireReg: WireNodeRegistration, newMapVersion: Int) { - // TODO: Once we have a better established messaging system, we can probably send - // to a MessageRecipientGroup that nodes join/leave, rather than the network map - // service itself managing the group - val update = NetworkMapService.Update(wireReg, newMapVersion, network.myAddress).serialize().bytes - val message = network.createMessage(PUSH_TOPIC, data = update) - - subscribers.locked { - // Remove any stale subscribers - values.removeIf { (mapVersion) -> newMapVersion - mapVersion > maxUnacknowledgedUpdates } - // TODO: introduce some concept of time in the condition to avoid unsubscribes when there's a message burst. - keys.forEach { recipient -> network.send(message, recipient) } - } - } - - private fun processSubscriptionRequest(request: SubscribeRequest): SubscribeResponse { - if (request.subscribe) { - addSubscriber(request.replyTo) - } else { - removeSubscriber(request.replyTo) - } - return SubscribeResponse(true) - } -} /** * A node registration state in the network map. @@ -325,42 +58,9 @@ abstract class AbstractNetworkMapService(network: MessagingService, // involves providing both node and paerty, and deregistering a node involves a request with party but no node. @CordaSerializable data class NodeRegistration(val node: NodeInfo, val serial: Long, val type: AddOrRemove, var expires: Instant) { - /** - * Build a node registration in wire format. - */ - fun toWire(keyManager: KeyManagementService, publicKey: PublicKey): WireNodeRegistration { - val regSerialized = this.serialize() - val regSig = keyManager.sign(regSerialized.bytes, publicKey) - - return WireNodeRegistration(regSerialized, regSig) - } - override fun toString(): String = "$node #$serial ($type)" } -/** - * A node registration and its signature as a pair. - */ -@CordaSerializable -class WireNodeRegistration(raw: SerializedBytes, sig: DigitalSignature.WithKey) : SignedData(raw, sig) { - @Throws(IllegalArgumentException::class) - override fun verifyData(data: NodeRegistration) { - // Check that the registration is fulfilled by any of node's identities. - // TODO It may cause some problems with distributed services? We loose node's main identity. Should be all signatures instead of isFulfilledBy? - require(data.node.legalIdentitiesAndCerts.any { it.owningKey.isFulfilledBy(sig.by) }) - } -} - -@CordaSerializable -sealed class NodeMapException : CordaException("Network Map Protocol Error") { - - /** Thrown if the signature on the node info does not match the public key for the identity */ - class InvalidSignature : NodeMapException() - - /** Thrown if the replyTo of a subscription change message is not a single message recipient */ - class InvalidSubscriber : NodeMapException() -} - @CordaSerializable data class LastAcknowledgeInfo(val mapVersion: Int) diff --git a/node/src/main/kotlin/net/corda/node/services/network/PersistentNetworkMapService.kt b/node/src/main/kotlin/net/corda/node/services/network/PersistentNetworkMapService.kt index e12e1703f4..02c04ac968 100644 --- a/node/src/main/kotlin/net/corda/node/services/network/PersistentNetworkMapService.kt +++ b/node/src/main/kotlin/net/corda/node/services/network/PersistentNetworkMapService.kt @@ -8,26 +8,23 @@ import net.corda.core.serialization.SerializationDefaults import net.corda.core.serialization.deserialize import net.corda.core.serialization.serialize import net.corda.core.utilities.MAX_HASH_HEX_SIZE -import net.corda.node.services.api.NetworkMapCacheInternal -import net.corda.node.services.messaging.MessagingService import net.corda.node.utilities.NODE_DATABASE_PREFIX import net.corda.node.utilities.PersistentMap import net.corda.nodeapi.ArtemisMessagingComponent import java.io.ByteArrayInputStream import java.security.cert.CertificateFactory -import javax.persistence.* import java.util.* +import javax.persistence.* /** * A network map service backed by a database to survive restarts of the node hosting it. * - * Majority of the logic is inherited from [AbstractNetworkMapService]. + * Majority of the logic is inherited from [NetworkMapService]. * * This class needs database transactions to be in-flight during method calls and init, otherwise it will throw * exceptions. */ -class PersistentNetworkMapService(network: MessagingService, networkMapCache: NetworkMapCacheInternal, minimumPlatformVersion: Int) - : AbstractNetworkMapService(network, networkMapCache, minimumPlatformVersion) { +class PersistentNetworkMapService: NetworkMapService { // Only the node_party_path column is needed to reconstruct a PartyAndCertificate but we have the others for human readability @Entity @@ -124,10 +121,4 @@ class PersistentNetworkMapService(network: MessagingService, networkMapCache: Ne ) override val subscribers = ThreadBox(createNetworkSubscribersMap()) - - init { - // Initialise the network map version with the current highest persisted version, or zero if there are no entries. - _mapVersion.set(nodeRegistrations.values.map { it.mapVersion }.max() ?: 0) - setup() - } } diff --git a/node/src/test/kotlin/net/corda/node/services/messaging/ArtemisMessagingTests.kt b/node/src/test/kotlin/net/corda/node/services/messaging/ArtemisMessagingTests.kt index b18b7a1766..d300c932dd 100644 --- a/node/src/test/kotlin/net/corda/node/services/messaging/ArtemisMessagingTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/messaging/ArtemisMessagingTests.kt @@ -14,7 +14,6 @@ import net.corda.node.services.config.NodeConfiguration import net.corda.node.services.config.configureWithDevSSLCertificate import net.corda.node.services.network.NetworkMapCacheImpl import net.corda.node.services.network.PersistentNetworkMapCache -import net.corda.node.services.network.NetworkMapService import net.corda.node.services.transactions.PersistentUniquenessProvider import net.corda.node.utilities.AffinityExecutor.ServiceAffinityExecutor import net.corda.node.utilities.CordaPersistence @@ -40,16 +39,19 @@ import kotlin.test.assertNull //TODO This needs to be merged into P2PMessagingTest as that creates a more realistic environment class ArtemisMessagingTests { + companion object { + const val TOPIC = "platform.self" + } @Rule @JvmField val testSerialization = SerializationEnvironmentRule() + @Rule @JvmField val temporaryFolder = TemporaryFolder() val serverPort = freePort() val rpcPort = freePort() - val topic = "platform.self" val identity = generateKeyPair() lateinit var config: NodeConfiguration @@ -130,7 +132,7 @@ class ArtemisMessagingTests { val receivedMessages = LinkedBlockingQueue() val messagingClient = createAndStartClientAndServer(receivedMessages) - val message = messagingClient.createMessage(topic, data = "first msg".toByteArray()) + val message = messagingClient.createMessage(TOPIC, data = "first msg".toByteArray()) messagingClient.send(message, messagingClient.myAddress) val actual: Message = receivedMessages.take() @@ -146,15 +148,9 @@ class ArtemisMessagingTests { val receivedMessages = LinkedBlockingQueue() val messagingClient = createAndStartClientAndServer(receivedMessages) - val message = messagingClient.createMessage(topic, data = "first msg".toByteArray()) + val message = messagingClient.createMessage(TOPIC, data = "first msg".toByteArray()) messagingClient.send(message, messagingClient.myAddress) - val networkMapMessage = messagingClient.createMessage(NetworkMapService.FETCH_TOPIC, data = "second msg".toByteArray()) - messagingClient.send(networkMapMessage, messagingClient.myAddress) - - val actual: Message = receivedMessages.take() - assertEquals("second msg", String(actual.data)) - assertNull(receivedMessages.poll(200, MILLISECONDS)) settableFuture.set(Unit) val firstActual: Message = receivedMessages.take() assertEquals("first msg", String(firstActual.data)) @@ -171,17 +167,10 @@ class ArtemisMessagingTests { val messagingClient = createAndStartClientAndServer(receivedMessages) for (iter in 1..iterations) { - val message = messagingClient.createMessage(topic, data = "first msg $iter".toByteArray()) + val message = messagingClient.createMessage(TOPIC, data = "first msg $iter".toByteArray()) messagingClient.send(message, messagingClient.myAddress) } - val networkMapMessage = messagingClient.createMessage(NetworkMapService.FETCH_TOPIC, data = "second msg".toByteArray()) - messagingClient.send(networkMapMessage, messagingClient.myAddress) - - val actual: Message = receivedMessages.take() - assertEquals("second msg", String(actual.data)) - assertNull(receivedMessages.poll(200, MILLISECONDS)) - // Stop client and server and create afresh. messagingClient.stop() messagingServer?.stop() @@ -205,10 +194,7 @@ class ArtemisMessagingTests { val messagingClient = createMessagingClient() startNodeMessagingClient() - messagingClient.addMessageHandler(topic) { message, _ -> - receivedMessages.add(message) - } - messagingClient.addMessageHandler(NetworkMapService.FETCH_TOPIC) { message, _ -> + messagingClient.addMessageHandler(TOPIC) { message, _ -> receivedMessages.add(message) } // Run after the handlers are added, otherwise (some of) the messages get delivered and discarded / dead-lettered. @@ -225,7 +211,6 @@ class ArtemisMessagingTests { identity.public, ServiceAffinityExecutor("ArtemisMessagingTests", 1), database, - networkMapRegistrationFuture, MonitoringService(MetricRegistry())).apply { config.configureWithDevSSLCertificate() messagingClient = this 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 7034e4b4e8..b531d4e49b 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 @@ -343,8 +343,6 @@ class NetworkMapVisualiser : Application() { private fun transferIsInteresting(transfer: InMemoryMessagingNetwork.MessageTransfer): Boolean { // Loopback messages are boring. if (transfer.sender == transfer.recipients) return false - // Network map push acknowledgements are boring. - if (NetworkMapService.PUSH_ACK_TOPIC in transfer.message.topicSession.topic) return false val message = transfer.message.data.deserialize() return when (message) { is SessionEnd -> false diff --git a/samples/trader-demo/README.md b/samples/trader-demo/README.md index 4d8e4693bf..be47476272 100644 --- a/samples/trader-demo/README.md +++ b/samples/trader-demo/README.md @@ -1,7 +1,7 @@ Trader demo ----------- -This demo brings up four nodes: Bank A, Bank B, Bank Of Corda, and a notary/network map node that they all use. Bank A +This demo brings up four nodes: Bank A, Bank B, Bank Of Corda, and a notary node that they all use. Bank A will be the buyer, and requests some cash from the Bank of Corda in order to acquire commercial paper from Bank B, the seller. 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 c9d1eb8a67..e955cc51cd 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 @@ -33,7 +33,10 @@ import net.corda.nodeapi.NodeInfoFilesCopier import net.corda.nodeapi.User import net.corda.nodeapi.config.toConfig import net.corda.nodeapi.internal.addShutdownHook -import net.corda.testing.* +import net.corda.testing.ALICE +import net.corda.testing.BOB +import net.corda.testing.DUMMY_BANK_A +import net.corda.testing.initialiseTestSerialization import net.corda.testing.node.MockServices.Companion.MOCK_VERSION_INFO import okhttp3.OkHttpClient import okhttp3.Request @@ -62,8 +65,7 @@ import kotlin.concurrent.thread /** * This file defines a small "Driver" DSL for starting up nodes that is only intended for development, demos and tests. * - * The process the driver is run in behaves as an Artemis client and starts up other processes. Namely it first - * bootstraps a network map service to allow the specified nodes to connect to, then starts up the actual nodes. + * The process the driver is run in behaves as an Artemis client and starts up other processes. * * TODO this file is getting way too big, it should be split into several files. */ @@ -302,8 +304,6 @@ data class NodeParameters( * of the [NodeInfo] that may be waited on, which completes when the new node registered with the network map service or * loaded node data from database. * - * The driver implicitly bootstraps a [NetworkMapService]. - * * @param defaultParameters The default parameters for the driver. Allows the driver to be configured in builder style * when called from Java code. * @param isDebug Indicates whether the spawned nodes should start in jdwt debug mode and have debug level logging. diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNode.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNode.kt index 0dd7089795..81d4fa92f8 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNode.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNode.kt @@ -29,7 +29,7 @@ import net.corda.node.services.config.BFTSMaRtConfiguration import net.corda.node.services.config.NodeConfiguration import net.corda.node.services.config.NotaryConfig import net.corda.node.services.keys.E2ETestKeyManagementService -import net.corda.node.services.messaging.* +import net.corda.node.services.messaging.MessagingService import net.corda.node.services.network.InMemoryNetworkMapService import net.corda.node.services.network.NetworkMapService import net.corda.node.services.transactions.BFTNonValidatingNotaryService @@ -214,7 +214,7 @@ class MockNetwork(defaultParameters: MockNetworkParameters = MockNetworkParamete } override fun makeNetworkMapService(network: MessagingService, networkMapCache: NetworkMapCacheInternal): NetworkMapService { - return InMemoryNetworkMapService(network, networkMapCache, 1) + return InMemoryNetworkMapService() } // This is not thread safe, but node construction is done on a single thread, so that should always be fine diff --git a/testing/test-utils/src/main/kotlin/net/corda/testing/TestConstants.kt b/testing/test-utils/src/main/kotlin/net/corda/testing/TestConstants.kt index 9e73764f06..5aa2750e4b 100644 --- a/testing/test-utils/src/main/kotlin/net/corda/testing/TestConstants.kt +++ b/testing/test-utils/src/main/kotlin/net/corda/testing/TestConstants.kt @@ -18,7 +18,6 @@ import org.bouncycastle.cert.X509CertificateHolder import java.math.BigInteger import java.security.KeyPair import java.security.PublicKey -import java.security.cert.Certificate import java.time.Instant // A dummy time at which we will be pretending test transactions are created. @@ -33,10 +32,6 @@ val DUMMY_NOTARY_IDENTITY: PartyAndCertificate get() = getTestPartyAndCertificat val DUMMY_NOTARY: Party get() = Party(CordaX500Name(organisation = "Notary Service", locality = "Zurich", country = "CH"), DUMMY_NOTARY_KEY.public) val DUMMY_NOTARY_SERVICE_NAME: CordaX500Name = DUMMY_NOTARY.name.copy(commonName = "corda.notary.validating") -val DUMMY_MAP_KEY: KeyPair by lazy { entropyToKeyPair(BigInteger.valueOf(30)) } -/** Dummy network map service identity for tests and simulations */ -val DUMMY_MAP: Party get() = Party(CordaX500Name(organisation = "Network Map Service", locality = "Amsterdam", country = "NL"), DUMMY_MAP_KEY.public) - val DUMMY_BANK_A_KEY: KeyPair by lazy { entropyToKeyPair(BigInteger.valueOf(40)) } /** Dummy bank identity for tests and simulations */ val DUMMY_BANK_A: Party get() = Party(CordaX500Name(organisation = "Bank A", locality = "London", country = "GB"), DUMMY_BANK_A_KEY.public) From b925e6540ded47319f508fbc618fe5a59f57db94 Mon Sep 17 00:00:00 2001 From: Alberto Arri <30873160+al-r3@users.noreply.github.com> Date: Wed, 1 Nov 2017 14:28:45 +0000 Subject: [PATCH 09/24] [CORDA-446] Remove mentions of networkmap node in DemoBench (#1969) * [CORDA-446] Remove mentions of networkmap node in DemoBench --- .../corda/demobench/model/InstallFactory.kt | 4 -- .../net/corda/demobench/model/NodeConfig.kt | 8 --- .../corda/demobench/model/NodeController.kt | 19 +------ .../net/corda/demobench/views/NodeTabView.kt | 4 +- .../corda/demobench/model/NodeConfigTest.kt | 5 -- .../demobench/model/NodeControllerTest.kt | 52 ++++++------------- 6 files changed, 19 insertions(+), 73 deletions(-) diff --git a/tools/demobench/src/main/kotlin/net/corda/demobench/model/InstallFactory.kt b/tools/demobench/src/main/kotlin/net/corda/demobench/model/InstallFactory.kt index d2dba4a687..f7709a7ada 100644 --- a/tools/demobench/src/main/kotlin/net/corda/demobench/model/InstallFactory.kt +++ b/tools/demobench/src/main/kotlin/net/corda/demobench/model/InstallFactory.kt @@ -25,10 +25,6 @@ class InstallFactory : Controller() { val tempDir = Files.createTempDirectory(baseDir, ".node") - if (nodeConfig.isNetworkMap) { - log.info("Node '${nodeConfig.myLegalName}' is the network map") - } - return InstallConfig(tempDir, NodeConfigWrapper(tempDir, nodeConfig)) } } diff --git a/tools/demobench/src/main/kotlin/net/corda/demobench/model/NodeConfig.kt b/tools/demobench/src/main/kotlin/net/corda/demobench/model/NodeConfig.kt index 504fdfab79..8ac6aeefdf 100644 --- a/tools/demobench/src/main/kotlin/net/corda/demobench/model/NodeConfig.kt +++ b/tools/demobench/src/main/kotlin/net/corda/demobench/model/NodeConfig.kt @@ -22,7 +22,6 @@ data class NodeConfig( /** This is not used by the node but by the webserver which looks at node.conf. */ val webAddress: NetworkHostAndPort, val notary: NotaryService?, - val networkMapService: NetworkMapConfig?, val h2port: Int, val rpcUsers: List = listOf(defaultUser), /** This is an extra config used by the Cash app. */ @@ -39,16 +38,9 @@ data class NodeConfig( @Suppress("unused") private val useTestClock = true - val isNetworkMap: Boolean get() = networkMapService == null - fun toText(): String = toConfig().root().render(renderOptions) } -/** - * This is a mirror of NetworkMapInfo. - */ -data class NetworkMapConfig(val legalName: CordaX500Name, val address: NetworkHostAndPort) - /** * This is a subset of NotaryConfig. It implements [ExtraService] to avoid unnecessary copying. */ diff --git a/tools/demobench/src/main/kotlin/net/corda/demobench/model/NodeController.kt b/tools/demobench/src/main/kotlin/net/corda/demobench/model/NodeController.kt index 2830c969b8..59237e97a8 100644 --- a/tools/demobench/src/main/kotlin/net/corda/demobench/model/NodeController.kt +++ b/tools/demobench/src/main/kotlin/net/corda/demobench/model/NodeController.kt @@ -37,8 +37,6 @@ class NodeController(check: atRuntime = ::checkExists) : Controller() { private val nodes = LinkedHashMap() private val port = AtomicInteger(firstPort) - private var networkMapConfig: NetworkMapConfig? = null - val activeNodes: List get() = nodes.values.filter { (it.state == NodeState.RUNNING) || (it.state == NodeState.STARTING) @@ -70,7 +68,6 @@ class NodeController(check: atRuntime = ::checkExists) : Controller() { rpcAddress = nodeData.rpcPort.toLocalAddress(), webAddress = nodeData.webPort.toLocalAddress(), notary = nodeData.extraServices.filterIsInstance().noneOrSingle(), - networkMapService = networkMapConfig, // The first node becomes the network map h2port = nodeData.h2Port.value, issuableCurrencies = nodeData.extraServices.filterIsInstance().map { it.currency.toString() } ) @@ -82,11 +79,6 @@ class NodeController(check: atRuntime = ::checkExists) : Controller() { return null } - if (nodeConfig.isNetworkMap) { - networkMapConfig = nodeConfig.let { NetworkMapConfig(it.myLegalName, it.p2pAddress) } - log.info("Network map provided by: ${nodeConfig.myLegalName}") - } - nodeInfoFilesCopier.addConfig(wrapper) return wrapper @@ -96,10 +88,6 @@ class NodeController(check: atRuntime = ::checkExists) : Controller() { config.state = NodeState.DEAD nodeInfoFilesCopier.removeConfig(config) - - if (config.nodeConfig.isNetworkMap) { - log.warning("Network map service (Node '${config.nodeConfig.myLegalName}') has exited.") - } } val nextPort: Int get() = port.andIncrement @@ -110,7 +98,7 @@ class NodeController(check: atRuntime = ::checkExists) : Controller() { fun nameExists(name: String) = keyExists(name.toKey()) - fun hasNetworkMap(): Boolean = networkMapConfig != null + fun hasNotary(): Boolean = activeNodes.any { it.nodeConfig.notary != null } fun runCorda(pty: R3Pty, config: NodeConfigWrapper): Boolean { try { @@ -141,7 +129,6 @@ class NodeController(check: atRuntime = ::checkExists) : Controller() { log.info("Changed base directory: $baseDir") // Wipe out any knowledge of previous nodes. - networkMapConfig = null nodes.clear() nodeInfoFilesCopier.reset() } @@ -157,10 +144,6 @@ class NodeController(check: atRuntime = ::checkExists) : Controller() { updatePort(config.nodeConfig) - if (networkMapConfig == null && config.nodeConfig.isNetworkMap) { - networkMapConfig = config.nodeConfig.let { NetworkMapConfig(it.myLegalName, it.p2pAddress) } - } - return true } diff --git a/tools/demobench/src/main/kotlin/net/corda/demobench/views/NodeTabView.kt b/tools/demobench/src/main/kotlin/net/corda/demobench/views/NodeTabView.kt index c9f993a5c2..cc728b4708 100644 --- a/tools/demobench/src/main/kotlin/net/corda/demobench/views/NodeTabView.kt +++ b/tools/demobench/src/main/kotlin/net/corda/demobench/views/NodeTabView.kt @@ -114,7 +114,7 @@ class NodeTabView : Fragment() { fieldset("Additional configuration") { styleClass.addAll("services-panel") - val extraServices = if (nodeController.hasNetworkMap()) { + val extraServices = if (nodeController.hasNotary()) { listOf(USD, GBP, CHF, EUR).map { CurrencyIssuer(it) } } else { listOf(NotaryService(true), NotaryService(false)) @@ -123,7 +123,7 @@ class NodeTabView : Fragment() { val servicesList = CheckListView(extraServices.observable()).apply { vboxConstraints { vGrow = Priority.ALWAYS } model.item.extraServices.set(checkModel.checkedItems) - if (!nodeController.hasNetworkMap()) { + if (!nodeController.hasNotary()) { checkModel.check(0) checkModel.checkedItems.addListener(ListChangeListener { change -> while (change.next()) { diff --git a/tools/demobench/src/test/kotlin/net/corda/demobench/model/NodeConfigTest.kt b/tools/demobench/src/test/kotlin/net/corda/demobench/model/NodeConfigTest.kt index 92ed477be8..8ebcbaf2cc 100644 --- a/tools/demobench/src/test/kotlin/net/corda/demobench/model/NodeConfigTest.kt +++ b/tools/demobench/src/test/kotlin/net/corda/demobench/model/NodeConfigTest.kt @@ -7,7 +7,6 @@ import net.corda.core.utilities.NetworkHostAndPort import net.corda.node.services.config.parseAsNodeConfiguration import net.corda.nodeapi.User import net.corda.nodeapi.config.toConfig -import net.corda.testing.DUMMY_NOTARY import net.corda.webserver.WebServerConfig import org.assertj.core.api.Assertions.assertThat import org.junit.Test @@ -32,7 +31,6 @@ class NodeConfigTest { webPort = 20001, h2port = 30001, notary = NotaryService(validating = false), - networkMap = NetworkMapConfig(DUMMY_NOTARY.name, localPort(12345)), users = listOf(user("jenny")) ) @@ -60,7 +58,6 @@ class NodeConfigTest { webPort = 20001, h2port = 30001, notary = NotaryService(validating = false), - networkMap = NetworkMapConfig(DUMMY_NOTARY.name, localPort(12345)), users = listOf(user("jenny")) ) @@ -83,7 +80,6 @@ class NodeConfigTest { webPort: Int = -1, h2port: Int = -1, notary: NotaryService?, - networkMap: NetworkMapConfig?, users: List = listOf(user("guest")) ): NodeConfig { return NodeConfig( @@ -93,7 +89,6 @@ class NodeConfigTest { webAddress = localPort(webPort), h2port = h2port, notary = notary, - networkMapService = networkMap, rpcUsers = users ) } diff --git a/tools/demobench/src/test/kotlin/net/corda/demobench/model/NodeControllerTest.kt b/tools/demobench/src/test/kotlin/net/corda/demobench/model/NodeControllerTest.kt index bae103a317..6ed56f94e0 100644 --- a/tools/demobench/src/test/kotlin/net/corda/demobench/model/NodeControllerTest.kt +++ b/tools/demobench/src/test/kotlin/net/corda/demobench/model/NodeControllerTest.kt @@ -3,7 +3,6 @@ package net.corda.demobench.model import net.corda.core.identity.CordaX500Name import net.corda.core.utilities.NetworkHostAndPort import net.corda.nodeapi.User -import net.corda.testing.DUMMY_NOTARY import org.junit.Test import java.nio.file.Path import java.nio.file.Paths @@ -24,6 +23,22 @@ class NodeControllerTest { assertNull(controller.validate(data)) } + @Test + fun `register notary`() { + assertFalse(controller.hasNotary()) + val config = createConfig(commonName = "Name", notary = NotaryService(false)) + controller.register(config) + assertTrue(controller.hasNotary()) + } + + @Test + fun `register non notary`() { + assertFalse(controller.hasNotary()) + val config = createConfig(commonName = "Name") + controller.register(config) + assertFalse(controller.hasNotary()) + } + @Test fun `test unique key after validate`() { val data = NodeData() @@ -48,17 +63,6 @@ class NodeControllerTest { assertTrue(controller.nameExists("organisation 1")) } - @Test - fun `test first validated node becomes network map`() { - val data = NodeData() - data.legalName.value = node1Name - data.p2pPort.value = 10000 - - assertFalse(controller.hasNetworkMap()) - controller.validate(data) - assertTrue(controller.hasNetworkMap()) - } - @Test fun `test register unique nodes`() { val config = createConfig(commonName = organisation2Name) @@ -88,28 +92,6 @@ class NodeControllerTest { assertTrue(controller.nameExists("organisation 2")) } - @Test - fun `test register network map node`() { - val config = createConfig(commonName = "Organisation is Network Map") - assertTrue(config.nodeConfig.isNetworkMap) - - assertFalse(controller.hasNetworkMap()) - controller.register(config) - assertTrue(controller.hasNetworkMap()) - } - - @Test - fun `test register non-network-map node`() { - val config = createConfig( - commonName = "Organisation is not Network Map", - networkMap = NetworkMapConfig(DUMMY_NOTARY.name, localPort(10000))) - assertFalse(config.nodeConfig.isNetworkMap) - - assertFalse(controller.hasNetworkMap()) - controller.register(config) - assertFalse(controller.hasNetworkMap()) - } - @Test fun `test valid ports`() { assertFalse(controller.isPortValid(NodeController.minPort - 1)) @@ -173,7 +155,6 @@ class NodeControllerTest { webPort: Int = 0, h2port: Int = 0, notary: NotaryService? = null, - networkMap: NetworkMapConfig? = null, users: List = listOf(user("guest")) ): NodeConfigWrapper { val nodeConfig = NodeConfig( @@ -187,7 +168,6 @@ class NodeControllerTest { webAddress = localPort(webPort), h2port = h2port, notary = notary, - networkMapService = networkMap, rpcUsers = users ) return NodeConfigWrapper(baseDir, nodeConfig) From 20a6fda357947ff388f6ea31b2e742690d17460d Mon Sep 17 00:00:00 2001 From: Viktor Kolomeyko Date: Wed, 1 Nov 2017 15:38:10 +0000 Subject: [PATCH 10/24] Correct unchecked cast call (#1973) Or else NodeExplorer will not start due to exception: java.lang.ClassCastException: net.corda.core.contracts.StateAndRef cannot be cast to java.lang.Void at net.corda.client.jfx.model.ContractStateModel$Companion.filterCashStateAndRefs(ContractStateModel.kt:43) at net.corda.client.jfx.model.ContractStateModel$Companion.access$filterCashStateAndRefs(ContractStateModel.kt:38) at net.corda.client.jfx.model.ContractStateModel$cashStatesDiff$1.call(ContractStateModel.kt:29) at net.corda.client.jfx.model.ContractStateModel$cashStatesDiff$1.call(ContractStateModel.kt:22) at rx.internal.operators.OnSubscribeMap$MapSubscriber.onNext(OnSubscribeMap.java:69) at rx.internal.operators.OnSubscribeMap$MapSubscriber.onNext(OnSubscribeMap.java:77) at rx.subjects.PublishSubject$PublishSubjectProducer.onNext(PublishSubject.java:304) at rx.subjects.PublishSubject$PublishSubjectState.onNext(PublishSubject.java:219) at rx.subjects.PublishSubject.onNext(PublishSubject.java:72) --- .../kotlin/net/corda/client/jfx/model/ContractStateModel.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/jfx/src/main/kotlin/net/corda/client/jfx/model/ContractStateModel.kt b/client/jfx/src/main/kotlin/net/corda/client/jfx/model/ContractStateModel.kt index e62f531dcf..85a37acafb 100644 --- a/client/jfx/src/main/kotlin/net/corda/client/jfx/model/ContractStateModel.kt +++ b/client/jfx/src/main/kotlin/net/corda/client/jfx/model/ContractStateModel.kt @@ -40,7 +40,7 @@ class ContractStateModel { return this.map { stateAndRef -> if (stateAndRef.state.data is Cash.State) { // Kotlin doesn't unify here for some reason - uncheckedCast(stateAndRef) + uncheckedCast, StateAndRef>(stateAndRef) } else { null } From a21d361df8b94971f046d1f9516152387440e4df Mon Sep 17 00:00:00 2001 From: Alberto Arri <30873160+al-r3@users.noreply.github.com> Date: Thu, 2 Nov 2017 10:31:31 +0000 Subject: [PATCH 11/24] Clean up mentions of nodeReady future and friends from MockNetwork/Driver (#1979) * Clean up mentions of nodeReady future and friends from MockNetwork/Driver --- .../test/node/NodeStatePersistenceTests.kt | 11 +------ .../kotlin/net/corda/testing/driver/Driver.kt | 31 ++++++------------- .../kotlin/net/corda/testing/node/MockNode.kt | 1 - .../net/corda/verifier/VerifierTests.kt | 5 +-- 4 files changed, 11 insertions(+), 37 deletions(-) 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 9a71e5c60f..ebc494c3c1 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 @@ -7,7 +7,6 @@ import net.corda.core.flows.FlowLogic import net.corda.core.flows.StartableByRPC import net.corda.core.identity.AbstractParty import net.corda.core.identity.Party -import net.corda.core.internal.concurrent.transpose import net.corda.core.messaging.startFlow import net.corda.core.schemas.MappedSchema import net.corda.core.schemas.PersistentState @@ -22,8 +21,6 @@ import net.corda.node.services.FlowPermissions import net.corda.nodeapi.User import net.corda.testing.DUMMY_NOTARY import net.corda.testing.chooseIdentity -import net.corda.testing.driver.DriverDSLExposedInterface -import net.corda.testing.driver.NodeHandle import net.corda.testing.driver.driver import org.junit.Assume import org.junit.Test @@ -48,17 +45,15 @@ class NodeStatePersistenceTests { val (nodeName, notaryNodeHandle) = { val notaryNodeHandle = startNotaryNode(DUMMY_NOTARY.name, validating = false).getOrThrow() val nodeHandle = startNode(rpcUsers = listOf(user)).getOrThrow() - ensureAcquainted(notaryNodeHandle, nodeHandle) val nodeName = nodeHandle.nodeInfo.chooseIdentity().name nodeHandle.rpcClientToNode().start(user.username, user.password).use { it.proxy.startFlow(::SendMessageFlow, message).returnValue.getOrThrow() } - nodeHandle.stop().getOrThrow() + nodeHandle.stop() nodeName to notaryNodeHandle }() val nodeHandle = startNode(providedName = nodeName, rpcUsers = listOf(user)).getOrThrow() - ensureAcquainted(notaryNodeHandle, nodeHandle) nodeHandle.rpcClientToNode().start(user.username, user.password).use { val page = it.proxy.vaultQuery(MessageState::class.java) val stateAndRef = page.states.singleOrNull() @@ -68,10 +63,6 @@ class NodeStatePersistenceTests { } } } - - private fun DriverDSLExposedInterface.ensureAcquainted(one: NodeHandle, another: NodeHandle) { - listOf(one.pollUntilKnowsAbout(another), another.pollUntilKnowsAbout(one)).transpose().getOrThrow() - } } fun isQuasarAgentSpecified(): Boolean { 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 e955cc51cd..6cdad1c883 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 @@ -175,15 +175,6 @@ interface DriverDSLExposedInterface : CordformContext { return pollUntilNonNull(pollName, pollInterval, warnCount) { if (check()) Unit else null } } - /** - * Polls until a given node knows about presence of another node via its own NetworkMap - */ - fun NodeHandle.pollUntilKnowsAbout(another: NodeHandle, pollInterval: Duration = DEFAULT_POLL_INTERVAL, warnCount: Int = DEFAULT_WARN_COUNT): CordaFuture { - return pollUntilTrue("${nodeInfo.legalIdentities} knows about ${another.nodeInfo.legalIdentities}", pollInterval, warnCount) { - another.nodeInfo in rpc.networkMapSnapshot() - } - } - val shutdownManager: ShutdownManager } @@ -202,6 +193,11 @@ sealed class NodeHandle { abstract val configuration: NodeConfiguration abstract val webAddress: NetworkHostAndPort + /** + * Stops the referenced node. + */ + abstract fun stop() + data class OutOfProcess( override val nodeInfo: NodeInfo, override val rpc: CordaRPCOps, @@ -211,13 +207,12 @@ sealed class NodeHandle { val process: Process, private val onStopCallback: () -> Unit ) : NodeHandle() { - override fun stop(): CordaFuture { + override fun stop() { with(process) { destroy() waitFor() } onStopCallback() - return doneFuture(Unit) } } @@ -230,23 +225,17 @@ sealed class NodeHandle { val nodeThread: Thread, private val onStopCallback: () -> Unit ) : NodeHandle() { - override fun stop(): CordaFuture { + override fun stop() { node.dispose() with(nodeThread) { interrupt() join() } onStopCallback() - return doneFuture(Unit) } } fun rpcClientToNode(): CordaRPCClient = CordaRPCClient(configuration.rpcAddress!!) - - /** - * Stops the referenced node. - */ - abstract fun stop(): CordaFuture } data class WebserverHandle( @@ -742,8 +731,7 @@ class DriverDSL( "webAddress" to webAddress.toString(), "useTestClock" to useTestClock, "rpcUsers" to if (rpcUsers.isEmpty()) defaultRpcUserList else rpcUsers.map { it.toConfig().root().unwrapped() }, - "verifierType" to verifierType.name, - "noNetworkMapServiceMode" to true + "verifierType" to verifierType.name ) + customOverrides ) return startNodeInternal(config, webAddress, startInSameProcess, maximumHeapSize) @@ -769,8 +757,7 @@ class DriverDSL( baseDirectory = baseDirectory(name), allowMissingConfig = true, configOverrides = node.config + notary + mapOf( - "rpcUsers" to if (rpcUsers.isEmpty()) defaultRpcUserList else rpcUsers, - "noNetworkMapServiceMode" to true + "rpcUsers" to if (rpcUsers.isEmpty()) defaultRpcUserList else rpcUsers ) ) startNodeInternal(config, webAddress, startInSameProcess, maximumHeapSize) diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNode.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNode.kt index 81d4fa92f8..e925a3d022 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNode.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNode.kt @@ -300,7 +300,6 @@ class MockNetwork(defaultParameters: MockNetworkParameters = MockNetworkParamete return nodeFactory(MockNodeArgs(config, this, id, parameters.notaryIdentity, parameters.entropyRoot)).apply { if (start) { start() - if (threadPerNode) nodeReadyFuture.getOrThrow() // XXX: What about manually-started nodes? ensureAllNetworkMapCachesHaveAllNodeInfos() } _nodes.add(this) 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 dc228fd6f6..2ff5b34e62 100644 --- a/verifier/src/integration-test/kotlin/net/corda/verifier/VerifierTests.kt +++ b/verifier/src/integration-test/kotlin/net/corda/verifier/VerifierTests.kt @@ -7,7 +7,6 @@ import net.corda.core.messaging.startFlow import net.corda.core.transactions.LedgerTransaction import net.corda.core.transactions.WireTransaction import net.corda.core.utilities.OpaqueBytes -import net.corda.core.utilities.getOrThrow import net.corda.finance.DOLLARS import net.corda.finance.flows.CashIssueFlow import net.corda.finance.flows.CashPaymentFlow @@ -19,8 +18,8 @@ import net.corda.testing.DUMMY_NOTARY_SERVICE_NAME import org.junit.Test import java.util.* import java.util.concurrent.atomic.AtomicInteger -import kotlin.test.assertTrue import kotlin.test.assertNotNull +import kotlin.test.assertTrue class VerifierTests { private fun generateTransactions(number: Int): List { @@ -137,8 +136,6 @@ class VerifierTests { val alice = aliceNode.rpc.wellKnownPartyFromX500Name(ALICE_NAME)!! val notary = notaryNode.rpc.notaryPartyFromX500Name(DUMMY_NOTARY_SERVICE_NAME)!! startVerifier(notaryNode) - notaryNode.pollUntilKnowsAbout(aliceNode).getOrThrow() - aliceNode.pollUntilKnowsAbout(notaryNode).getOrThrow() aliceNode.rpc.startFlow(::CashIssueFlow, 10.DOLLARS, OpaqueBytes.of(0), notary).returnValue.get() notaryNode.waitUntilNumberOfVerifiers(1) for (i in 1..10) { From d882f8871e82998b2520058caf22cf47f3994891 Mon Sep 17 00:00:00 2001 From: Michele Sollecito Date: Thu, 2 Nov 2017 15:09:49 +0000 Subject: [PATCH 12/24] [CORDA-758]: Permissions are now checked for each RPC method. (#1985) * Permissions are now checked for each RPC method. * Fixed NodeMonitorModelTest * Fixed IRSDemoTest --- .../corda/client/jfx/NodeMonitorModelTest.kt | 16 +- .../client/rpc/CordaRPCJavaClientTest.java | 10 +- .../corda/client/rpc/CordaRPCClientTest.kt | 17 +- .../rpc/ClientRPCInfrastructureTests.kt | 4 +- .../corda/client/rpc/RPCPermissionsTests.kt | 23 +- .../net/corda/core/messaging/CordaRPCOps.kt | 32 +- .../core/transactions/MerkleTransaction.kt | 2 +- .../core/flows/ContractUpgradeFlowTest.kt | 14 +- .../CompatibleTransactionTests.kt | 21 +- .../LedgerTransactionQueryTests.kt | 5 +- .../TransactionEncumbranceTests.kt | 8 +- .../TransactionTests.kt | 6 +- docs/source/changelog.rst | 1 + docs/source/clientrpc.rst | 11 +- .../corda/docs/IntegrationTestingTutorial.kt | 17 +- .../net/corda/docs/ClientRpcTutorial.kt | 14 +- docs/source/tutorial-clientrpc-api.rst | 2 +- .../kotlin/net/corda/node/BootTests.kt | 4 +- .../corda/node/CordappScanningDriverTest.kt | 4 +- .../net/corda/node/NodePerformanceTests.kt | 8 +- .../node/services/DistributedServiceTests.kt | 11 +- .../test/node/NodeStatePersistenceTests.kt | 5 +- .../net/corda/node/internal/AbstractNode.kt | 2 +- .../corda/node/internal/CordaRPCOpsImpl.kt | 46 ++- .../internal/DefaultCordaRpcPermissions.kt | 25 ++ .../node/internal/RpcAuthorisationProxy.kt | 158 +++++++++ .../corda/node/internal/SecureCordaRPCOps.kt | 24 ++ .../net/corda/node/services/Permissions.kt | 59 ++++ .../net/corda/node/services/RPCUserService.kt | 34 -- .../node/services/messaging/RPCServer.kt | 2 +- .../services/messaging/RPCServerStructures.kt | 13 +- .../net/corda/node/CordaRPCOpsImplTest.kt | 311 ++++++++++-------- .../attachmentdemo/AttachmentDemoTest.kt | 13 +- .../corda/bank/BankOfCordaRPCClientTest.kt | 16 +- .../net/corda/bank/BankOfCordaDriver.kt | 17 +- .../kotlin/net/corda/irs/IRSDemoTest.kt | 12 +- .../corda/netmap/simulation/IRSSimulation.kt | 2 +- .../corda/notarydemo/SingleNotaryCordform.kt | 4 +- samples/trader-demo/build.gradle | 7 +- .../net/corda/traderdemo/TraderDemoTest.kt | 12 +- .../test/kotlin/net/corda/traderdemo/Main.kt | 14 +- .../corda/testing/FlowStackSnapshotTest.kt | 12 +- .../kotlin/net/corda/testing/driver/Driver.kt | 10 +- .../net/corda/explorer/ExplorerSimulation.kt | 14 +- .../main/resources/loadtest-reference.conf | 2 +- 45 files changed, 697 insertions(+), 347 deletions(-) rename core/src/test/kotlin/net/corda/core/{contracts => transactions}/CompatibleTransactionTests.kt (95%) rename core/src/test/kotlin/net/corda/core/{contracts => transactions}/LedgerTransactionQueryTests.kt (98%) rename core/src/test/kotlin/net/corda/core/{contracts => transactions}/TransactionEncumbranceTests.kt (96%) rename core/src/test/kotlin/net/corda/core/{contracts => transactions}/TransactionTests.kt (97%) create mode 100644 node/src/main/kotlin/net/corda/node/internal/DefaultCordaRpcPermissions.kt create mode 100644 node/src/main/kotlin/net/corda/node/internal/RpcAuthorisationProxy.kt create mode 100644 node/src/main/kotlin/net/corda/node/internal/SecureCordaRPCOps.kt create mode 100644 node/src/main/kotlin/net/corda/node/services/Permissions.kt 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 e0131e6e99..2b75244715 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 @@ -26,7 +26,8 @@ import net.corda.finance.USD import net.corda.finance.flows.CashExitFlow import net.corda.finance.flows.CashIssueFlow import net.corda.finance.flows.CashPaymentFlow -import net.corda.node.services.FlowPermissions.Companion.startFlowPermission +import net.corda.node.services.Permissions.Companion.invokeRpc +import net.corda.node.services.Permissions.Companion.startFlow import net.corda.nodeapi.User import net.corda.testing.* import net.corda.testing.driver.driver @@ -52,9 +53,16 @@ class NodeMonitorModelTest : DriverBasedTest() { override fun setup() = driver(extraCordappPackagesToScan = listOf("net.corda.finance")) { val cashUser = User("user1", "test", permissions = setOf( - startFlowPermission(), - startFlowPermission(), - startFlowPermission()) + startFlow(), + startFlow(), + startFlow(), + invokeRpc(CordaRPCOps::notaryIdentities), + invokeRpc("vaultTrackBy"), + invokeRpc("vaultQueryBy"), + invokeRpc(CordaRPCOps::internalVerifiedTransactionsFeed), + invokeRpc(CordaRPCOps::stateMachineRecordedTransactionMappingFeed), + invokeRpc(CordaRPCOps::stateMachinesFeed), + invokeRpc(CordaRPCOps::networkMapFeed)) ) val aliceNodeFuture = startNode(providedName = ALICE.name, rpcUsers = listOf(cashUser)) val notaryHandle = startNotaryNode(DUMMY_NOTARY.name, validating = false).getOrThrow() diff --git a/client/rpc/src/integration-test/java/net/corda/client/rpc/CordaRPCJavaClientTest.java b/client/rpc/src/integration-test/java/net/corda/client/rpc/CordaRPCJavaClientTest.java index d46afb11b9..698aca77c9 100644 --- a/client/rpc/src/integration-test/java/net/corda/client/rpc/CordaRPCJavaClientTest.java +++ b/client/rpc/src/integration-test/java/net/corda/client/rpc/CordaRPCJavaClientTest.java @@ -26,7 +26,8 @@ import static java.util.Objects.requireNonNull; import static kotlin.test.AssertionsKt.assertEquals; import static net.corda.finance.Currencies.DOLLARS; import static net.corda.finance.contracts.GetBalances.getCashBalance; -import static net.corda.node.services.FlowPermissions.startFlowPermission; +import static net.corda.node.services.Permissions.invokeRpc; +import static net.corda.node.services.Permissions.startFlow; import static net.corda.testing.TestConstants.getALICE; public class CordaRPCJavaClientTest extends NodeBasedTest { @@ -34,7 +35,12 @@ public class CordaRPCJavaClientTest extends NodeBasedTest { super(Arrays.asList("net.corda.finance.contracts", CashSchemaV1.class.getPackage().getName())); } - private List perms = Arrays.asList(startFlowPermission(CashPaymentFlow.class), startFlowPermission(CashIssueFlow.class)); + private List perms = Arrays.asList( + startFlow(CashPaymentFlow.class), + startFlow(CashIssueFlow.class), + invokeRpc("nodeInfo"), + invokeRpc("vaultQueryBy"), + invokeRpc("vaultQueryByCriteria")); private Set permSet = new HashSet<>(perms); private User rpcUser = new User("user1", "test", permSet); diff --git a/client/rpc/src/integration-test/kotlin/net/corda/client/rpc/CordaRPCClientTest.kt b/client/rpc/src/integration-test/kotlin/net/corda/client/rpc/CordaRPCClientTest.kt index e5b412b596..4d67be16d8 100644 --- a/client/rpc/src/integration-test/kotlin/net/corda/client/rpc/CordaRPCClientTest.kt +++ b/client/rpc/src/integration-test/kotlin/net/corda/client/rpc/CordaRPCClientTest.kt @@ -4,10 +4,7 @@ import net.corda.core.crypto.random63BitValue import net.corda.core.flows.FlowInitiator import net.corda.core.internal.concurrent.flatMap import net.corda.core.internal.packageName -import net.corda.core.messaging.FlowProgressHandle -import net.corda.core.messaging.StateMachineUpdate -import net.corda.core.messaging.startFlow -import net.corda.core.messaging.startTrackedFlow +import net.corda.core.messaging.* import net.corda.core.utilities.OpaqueBytes import net.corda.core.utilities.getOrThrow import net.corda.finance.DOLLARS @@ -20,7 +17,8 @@ import net.corda.finance.flows.CashPaymentFlow import net.corda.finance.schemas.CashSchemaV1 import net.corda.node.internal.Node import net.corda.node.internal.StartedNode -import net.corda.node.services.FlowPermissions.Companion.startFlowPermission +import net.corda.node.services.Permissions.Companion.invokeRpc +import net.corda.node.services.Permissions.Companion.startFlow import net.corda.nodeapi.User import net.corda.testing.ALICE import net.corda.testing.chooseIdentity @@ -36,9 +34,12 @@ import kotlin.test.assertTrue class CordaRPCClientTest : NodeBasedTest(listOf("net.corda.finance.contracts", CashSchemaV1::class.packageName)) { private val rpcUser = User("user1", "test", permissions = setOf( - startFlowPermission(), - startFlowPermission() - )) + startFlow(), + startFlow(), + invokeRpc("vaultQueryBy"), + invokeRpc(CordaRPCOps::stateMachinesFeed), + invokeRpc("vaultQueryByCriteria")) + ) private lateinit var node: StartedNode private lateinit var client: CordaRPCClient private var connection: CordaRPCConnection? = null diff --git a/client/rpc/src/test/kotlin/net/corda/client/rpc/ClientRPCInfrastructureTests.kt b/client/rpc/src/test/kotlin/net/corda/client/rpc/ClientRPCInfrastructureTests.kt index 3502145ae0..0eaa7fbacc 100644 --- a/client/rpc/src/test/kotlin/net/corda/client/rpc/ClientRPCInfrastructureTests.kt +++ b/client/rpc/src/test/kotlin/net/corda/client/rpc/ClientRPCInfrastructureTests.kt @@ -6,7 +6,7 @@ import net.corda.core.internal.concurrent.openFuture import net.corda.core.internal.concurrent.thenMatch import net.corda.core.messaging.RPCOps import net.corda.core.utilities.getOrThrow -import net.corda.node.services.messaging.getRpcContext +import net.corda.node.services.messaging.rpcContext import net.corda.testing.RPCDriverExposedDSLInterface import net.corda.testing.rpcDriver import net.corda.testing.rpcTestUser @@ -65,7 +65,7 @@ class ClientRPCInfrastructureTests : AbstractRPCTest() { override fun makeComplicatedObservable() = complicatedObservable override fun makeComplicatedListenableFuture() = complicatedListenableFuturee override fun addedLater(): Unit = throw IllegalStateException() - override fun captureUser(): String = getRpcContext().currentUser.username + override fun captureUser(): String = rpcContext().currentUser.username } @Test diff --git a/client/rpc/src/test/kotlin/net/corda/client/rpc/RPCPermissionsTests.kt b/client/rpc/src/test/kotlin/net/corda/client/rpc/RPCPermissionsTests.kt index cee75881aa..00392eb37a 100644 --- a/client/rpc/src/test/kotlin/net/corda/client/rpc/RPCPermissionsTests.kt +++ b/client/rpc/src/test/kotlin/net/corda/client/rpc/RPCPermissionsTests.kt @@ -1,7 +1,9 @@ package net.corda.client.rpc +import net.corda.core.messaging.CordaRPCOps import net.corda.core.messaging.RPCOps -import net.corda.node.services.messaging.getRpcContext +import net.corda.node.services.Permissions.Companion.invokeRpc +import net.corda.node.services.messaging.rpcContext import net.corda.node.services.messaging.requirePermission import net.corda.nodeapi.User import net.corda.testing.RPCDriverExposedDSLInterface @@ -9,6 +11,8 @@ import net.corda.testing.rpcDriver import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.Parameterized +import kotlin.reflect.KVisibility +import kotlin.reflect.full.declaredMemberFunctions import kotlin.test.assertFailsWith @RunWith(Parameterized::class) @@ -28,7 +32,7 @@ class RPCPermissionsTests : AbstractRPCTest() { class TestOpsImpl : TestOps { override val protocolVersion = 1 - override fun validatePermission(str: String) = getRpcContext().requirePermission(str) + override fun validatePermission(str: String) { rpcContext().requirePermission(str) } } /** @@ -89,4 +93,19 @@ class RPCPermissionsTests : AbstractRPCTest() { } } + @Test + fun `fine grained permissions are enforced`() { + val allPermissions = CordaRPCOps::class.declaredMemberFunctions.filter { it.visibility == KVisibility.PUBLIC }.map { invokeRpc(it) } + allPermissions.forEach { permission -> + rpcDriver { + val user = userOf("Mark", setOf(permission)) + val proxy = testProxyFor(user) + + proxy.validatePermission(permission) + (allPermissions - permission).forEach { notOwnedPermission -> + assertFailsWith(PermissionException::class, { proxy.validatePermission(notOwnedPermission) }) + } + } + } + } } diff --git a/core/src/main/kotlin/net/corda/core/messaging/CordaRPCOps.kt b/core/src/main/kotlin/net/corda/core/messaging/CordaRPCOps.kt index 3e87d4ba39..9556bb0a4f 100644 --- a/core/src/main/kotlin/net/corda/core/messaging/CordaRPCOps.kt +++ b/core/src/main/kotlin/net/corda/core/messaging/CordaRPCOps.kt @@ -100,21 +100,13 @@ interface CordaRPCOps : RPCOps { // Java Helpers // DOCSTART VaultQueryAPIHelpers - fun vaultQuery(contractStateType: Class): Vault.Page { - return vaultQueryBy(QueryCriteria.VaultQueryCriteria(), PageSpecification(), Sort(emptySet()), contractStateType) - } + fun vaultQuery(contractStateType: Class): Vault.Page - fun vaultQueryByCriteria(criteria: QueryCriteria, contractStateType: Class): Vault.Page { - return vaultQueryBy(criteria, PageSpecification(), Sort(emptySet()), contractStateType) - } + fun vaultQueryByCriteria(criteria: QueryCriteria, contractStateType: Class): Vault.Page - fun vaultQueryByWithPagingSpec(contractStateType: Class, criteria: QueryCriteria, paging: PageSpecification): Vault.Page { - return vaultQueryBy(criteria, paging, Sort(emptySet()), contractStateType) - } + fun vaultQueryByWithPagingSpec(contractStateType: Class, criteria: QueryCriteria, paging: PageSpecification): Vault.Page - fun vaultQueryByWithSorting(contractStateType: Class, criteria: QueryCriteria, sorting: Sort): Vault.Page { - return vaultQueryBy(criteria, PageSpecification(), sorting, contractStateType) - } + fun vaultQueryByWithSorting(contractStateType: Class, criteria: QueryCriteria, sorting: Sort): Vault.Page // DOCEND VaultQueryAPIHelpers /** @@ -141,21 +133,13 @@ interface CordaRPCOps : RPCOps { // Java Helpers // DOCSTART VaultTrackAPIHelpers - fun vaultTrack(contractStateType: Class): DataFeed, Vault.Update> { - return vaultTrackBy(QueryCriteria.VaultQueryCriteria(), PageSpecification(), Sort(emptySet()), contractStateType) - } + fun vaultTrack(contractStateType: Class): DataFeed, Vault.Update> - fun vaultTrackByCriteria(contractStateType: Class, criteria: QueryCriteria): DataFeed, Vault.Update> { - return vaultTrackBy(criteria, PageSpecification(), Sort(emptySet()), contractStateType) - } + fun vaultTrackByCriteria(contractStateType: Class, criteria: QueryCriteria): DataFeed, Vault.Update> - fun vaultTrackByWithPagingSpec(contractStateType: Class, criteria: QueryCriteria, paging: PageSpecification): DataFeed, Vault.Update> { - return vaultTrackBy(criteria, paging, Sort(emptySet()), contractStateType) - } + fun vaultTrackByWithPagingSpec(contractStateType: Class, criteria: QueryCriteria, paging: PageSpecification): DataFeed, Vault.Update> - fun vaultTrackByWithSorting(contractStateType: Class, criteria: QueryCriteria, sorting: Sort): DataFeed, Vault.Update> { - return vaultTrackBy(criteria, PageSpecification(), sorting, contractStateType) - } + fun vaultTrackByWithSorting(contractStateType: Class, criteria: QueryCriteria, sorting: Sort): DataFeed, Vault.Update> // DOCEND VaultTrackAPIHelpers /** diff --git a/core/src/main/kotlin/net/corda/core/transactions/MerkleTransaction.kt b/core/src/main/kotlin/net/corda/core/transactions/MerkleTransaction.kt index ba7cb8de1f..be7cdf0d7b 100644 --- a/core/src/main/kotlin/net/corda/core/transactions/MerkleTransaction.kt +++ b/core/src/main/kotlin/net/corda/core/transactions/MerkleTransaction.kt @@ -108,7 +108,7 @@ abstract class TraversableTransaction(open val componentGroups: List, val groupHashes: List 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 19b93ba663..52ad1049c4 100644 --- a/core/src/test/kotlin/net/corda/core/flows/ContractUpgradeFlowTest.kt +++ b/core/src/test/kotlin/net/corda/core/flows/ContractUpgradeFlowTest.kt @@ -16,9 +16,9 @@ import net.corda.finance.USD import net.corda.finance.`issued by` import net.corda.finance.contracts.asset.Cash import net.corda.finance.flows.CashIssueFlow -import net.corda.node.internal.CordaRPCOpsImpl +import net.corda.node.internal.SecureCordaRPCOps import net.corda.node.internal.StartedNode -import net.corda.node.services.FlowPermissions.Companion.startFlowPermission +import net.corda.node.services.Permissions.Companion.startFlow import net.corda.nodeapi.User import net.corda.testing.* import net.corda.testing.contracts.DummyContract @@ -118,7 +118,7 @@ class ContractUpgradeFlowTest { return startRpcClient( rpcAddress = startRpcServer( rpcUser = user, - ops = CordaRPCOpsImpl(node.services, node.smm, node.database, node.services) + ops = SecureCordaRPCOps(node.services, node.smm, node.database, node.services) ).get().broker.hostAndPort!!, username = user.username, password = user.password @@ -134,10 +134,10 @@ class ContractUpgradeFlowTest { val stx = bobNode.services.addSignature(signedByA) val user = rpcTestUser.copy(permissions = setOf( - startFlowPermission(), - startFlowPermission>(), - startFlowPermission(), - startFlowPermission() + startFlow(), + startFlow>(), + startFlow(), + startFlow() )) val rpcA = startProxy(aliceNode, user) val rpcB = startProxy(bobNode, user) diff --git a/core/src/test/kotlin/net/corda/core/contracts/CompatibleTransactionTests.kt b/core/src/test/kotlin/net/corda/core/transactions/CompatibleTransactionTests.kt similarity index 95% rename from core/src/test/kotlin/net/corda/core/contracts/CompatibleTransactionTests.kt rename to core/src/test/kotlin/net/corda/core/transactions/CompatibleTransactionTests.kt index 70995cd8cd..7464126983 100644 --- a/core/src/test/kotlin/net/corda/core/contracts/CompatibleTransactionTests.kt +++ b/core/src/test/kotlin/net/corda/core/transactions/CompatibleTransactionTests.kt @@ -1,9 +1,9 @@ -package net.corda.core.contracts +package net.corda.core.transactions +import net.corda.core.contracts.* import net.corda.core.contracts.ComponentGroupEnum.* import net.corda.core.crypto.* import net.corda.core.serialization.serialize -import net.corda.core.transactions.* import net.corda.core.utilities.OpaqueBytes import net.corda.testing.* import net.corda.testing.contracts.DummyContract @@ -399,8 +399,7 @@ class CompatibleTransactionTests { @Test fun `FilteredTransaction signer manipulation tests`() { // Required to call the private constructor. - val ftxConstructor = FilteredTransaction::class.java.declaredConstructors[1] - ftxConstructor.isAccessible = true + val ftxConstructor = ::FilteredTransaction // 1st and 3rd commands require a signature from KEY_1. val twoCommandsforKey1 = listOf(dummyCommand(DUMMY_KEY_1.public, DUMMY_KEY_2.public), dummyCommand(DUMMY_KEY_2.public), dummyCommand(DUMMY_KEY_1.public)) @@ -479,12 +478,12 @@ class CompatibleTransactionTests { // A command with no corresponding signer detected // because the pointer of CommandData (3rd leaf) cannot find a corresponding (3rd) signer. val updatedFilteredComponentsNoSignersKey1SamePMT = listOf(key1CommandsFtx.filteredComponentGroups[0], noLastSignerGroupSamePartialTree) - assertFails { ftxConstructor.newInstance(key1CommandsFtx.id, updatedFilteredComponentsNoSignersKey1SamePMT, key1CommandsFtx.groupHashes) } + assertFails { ftxConstructor.invoke(key1CommandsFtx.id, updatedFilteredComponentsNoSignersKey1SamePMT, key1CommandsFtx.groupHashes) } // Remove both last signer (KEY1) and related command. // Update partial Merkle tree for signers. val updatedFilteredComponentsNoLastCommandAndSigners = listOf(noLastCommandDataGroup, noLastSignerGroup) - val ftxNoLastCommandAndSigners = ftxConstructor.newInstance(key1CommandsFtx.id, updatedFilteredComponentsNoLastCommandAndSigners, key1CommandsFtx.groupHashes) as FilteredTransaction + val ftxNoLastCommandAndSigners = ftxConstructor.invoke(key1CommandsFtx.id, updatedFilteredComponentsNoLastCommandAndSigners, key1CommandsFtx.groupHashes) as FilteredTransaction // verify() will pass as the transaction is well-formed. ftxNoLastCommandAndSigners.verify() // checkCommandVisibility() will not pass, because checkAllComponentsVisible(ComponentGroupEnum.SIGNERS_GROUP) will fail. @@ -493,7 +492,7 @@ class CompatibleTransactionTests { // Remove last signer for which there is no pointer from a visible commandData. This is the case of Key2. // Do not change partial Merkle tree for signers. // This time the object can be constructed as there is no pointer mismatch. - val ftxNoLastSigner = ftxConstructor.newInstance(key2CommandsFtx.id, updatedFilteredComponentsNoSignersKey2SamePMT, key2CommandsFtx.groupHashes) as FilteredTransaction + val ftxNoLastSigner = ftxConstructor.invoke(key2CommandsFtx.id, updatedFilteredComponentsNoSignersKey2SamePMT, key2CommandsFtx.groupHashes) as FilteredTransaction // verify() will fail as we didn't change the partial Merkle tree. assertFailsWith { ftxNoLastSigner.verify() } // checkCommandVisibility() will not pass. @@ -501,7 +500,7 @@ class CompatibleTransactionTests { // Remove last signer for which there is no pointer from a visible commandData. This is the case of Key2. // Update partial Merkle tree for signers. - val ftxNoLastSignerB = ftxConstructor.newInstance(key2CommandsFtx.id, updatedFilteredComponentsNoSignersKey2, key2CommandsFtx.groupHashes) as FilteredTransaction + val ftxNoLastSignerB = ftxConstructor.invoke(key2CommandsFtx.id, updatedFilteredComponentsNoSignersKey2, key2CommandsFtx.groupHashes) as FilteredTransaction // verify() will pass, the transaction is well-formed. ftxNoLastSignerB.verify() // But, checkAllComponentsVisible() will not pass. @@ -526,20 +525,18 @@ class CompatibleTransactionTests { val alterFilteredComponents = listOf(key1CommandsFtx.filteredComponentGroups[0], alterSignerGroup) // Do not update groupHashes. - val ftxAlterSigner = ftxConstructor.newInstance(key1CommandsFtx.id, alterFilteredComponents, key1CommandsFtx.groupHashes) as FilteredTransaction + val ftxAlterSigner = ftxConstructor.invoke(key1CommandsFtx.id, alterFilteredComponents, key1CommandsFtx.groupHashes) as FilteredTransaction // Visible components in signers group cannot be verified against their partial Merkle tree. assertFailsWith { ftxAlterSigner.verify() } // Also, checkAllComponentsVisible() will not pass (groupHash matching will fail). assertFailsWith { ftxAlterSigner.checkCommandVisibility(DUMMY_KEY_1.public) } // Update groupHashes. - val ftxAlterSignerB = ftxConstructor.newInstance(key1CommandsFtx.id, alterFilteredComponents, key1CommandsFtx.groupHashes.subList(0, 6) + alterMTree.hash) as FilteredTransaction + val ftxAlterSignerB = ftxConstructor.invoke(key1CommandsFtx.id, alterFilteredComponents, key1CommandsFtx.groupHashes.subList(0, 6) + alterMTree.hash) as FilteredTransaction // Visible components in signers group cannot be verified against their partial Merkle tree. assertFailsWith { ftxAlterSignerB.verify() } // Also, checkAllComponentsVisible() will not pass (top level Merkle tree cannot be verified against transaction's id). assertFailsWith { ftxAlterSignerB.checkCommandVisibility(DUMMY_KEY_1.public) } - - ftxConstructor.isAccessible = false } } diff --git a/core/src/test/kotlin/net/corda/core/contracts/LedgerTransactionQueryTests.kt b/core/src/test/kotlin/net/corda/core/transactions/LedgerTransactionQueryTests.kt similarity index 98% rename from core/src/test/kotlin/net/corda/core/contracts/LedgerTransactionQueryTests.kt rename to core/src/test/kotlin/net/corda/core/transactions/LedgerTransactionQueryTests.kt index 6e835d5209..28d7b6615c 100644 --- a/core/src/test/kotlin/net/corda/core/contracts/LedgerTransactionQueryTests.kt +++ b/core/src/test/kotlin/net/corda/core/transactions/LedgerTransactionQueryTests.kt @@ -1,8 +1,7 @@ -package net.corda.core.contracts +package net.corda.core.transactions +import net.corda.core.contracts.* import net.corda.core.identity.AbstractParty -import net.corda.core.transactions.LedgerTransaction -import net.corda.core.transactions.TransactionBuilder import net.corda.testing.* import net.corda.testing.contracts.DummyContract import net.corda.testing.node.MockServices diff --git a/core/src/test/kotlin/net/corda/core/contracts/TransactionEncumbranceTests.kt b/core/src/test/kotlin/net/corda/core/transactions/TransactionEncumbranceTests.kt similarity index 96% rename from core/src/test/kotlin/net/corda/core/contracts/TransactionEncumbranceTests.kt rename to core/src/test/kotlin/net/corda/core/transactions/TransactionEncumbranceTests.kt index 8988d74a42..f96519dfec 100644 --- a/core/src/test/kotlin/net/corda/core/contracts/TransactionEncumbranceTests.kt +++ b/core/src/test/kotlin/net/corda/core/transactions/TransactionEncumbranceTests.kt @@ -1,7 +1,9 @@ -package net.corda.core.contracts +package net.corda.core.transactions +import net.corda.core.contracts.Contract +import net.corda.core.contracts.ContractState +import net.corda.core.contracts.requireThat import net.corda.core.identity.AbstractParty -import net.corda.core.transactions.LedgerTransaction import net.corda.finance.DOLLARS import net.corda.finance.`issued by` import net.corda.finance.contracts.asset.Cash @@ -12,7 +14,7 @@ import org.junit.Test import java.time.Instant import java.time.temporal.ChronoUnit -val TEST_TIMELOCK_ID = "net.corda.core.contracts.TransactionEncumbranceTests\$DummyTimeLock" +val TEST_TIMELOCK_ID = "net.corda.core.transactions.TransactionEncumbranceTests\$DummyTimeLock" class TransactionEncumbranceTests { val defaultIssuer = MEGA_CORP.ref(1) diff --git a/core/src/test/kotlin/net/corda/core/contracts/TransactionTests.kt b/core/src/test/kotlin/net/corda/core/transactions/TransactionTests.kt similarity index 97% rename from core/src/test/kotlin/net/corda/core/contracts/TransactionTests.kt rename to core/src/test/kotlin/net/corda/core/transactions/TransactionTests.kt index 26878fea9c..17cc1bb769 100644 --- a/core/src/test/kotlin/net/corda/core/contracts/TransactionTests.kt +++ b/core/src/test/kotlin/net/corda/core/transactions/TransactionTests.kt @@ -1,11 +1,9 @@ -package net.corda.core.contracts +package net.corda.core.transactions +import net.corda.core.contracts.* import net.corda.core.crypto.* import net.corda.core.crypto.CompositeKey import net.corda.core.identity.Party -import net.corda.core.transactions.LedgerTransaction -import net.corda.core.transactions.SignedTransaction -import net.corda.core.transactions.WireTransaction import net.corda.finance.contracts.asset.DUMMY_CASH_ISSUER_KEY import net.corda.testing.* import net.corda.testing.contracts.DummyContract diff --git a/docs/source/changelog.rst b/docs/source/changelog.rst index f3f5e0be7c..184a572810 100644 --- a/docs/source/changelog.rst +++ b/docs/source/changelog.rst @@ -6,6 +6,7 @@ from the previous milestone release. UNRELEASED ---------- +* ``CordaRPCOps`` implementation now checks permissions for any function invocation, rather than just when starting flows. * ``OpaqueBytes.bytes`` now returns a clone of its underlying ``ByteArray``, and has been redeclared as ``final``. This is a minor change to the public API, but is required to ensure that classes like ``SecureHash`` are immutable. diff --git a/docs/source/clientrpc.rst b/docs/source/clientrpc.rst index 14695b7943..14d29ba9b9 100644 --- a/docs/source/clientrpc.rst +++ b/docs/source/clientrpc.rst @@ -41,7 +41,7 @@ The syntax for adding an RPC user is: ... ] -Currently, users need special permissions to start flows via RPC. These permissions are added as follows: +Users need permissions to invoke any RPC call. By default, nothing is allowed. These permissions are specified as follows: .. container:: codeset @@ -62,6 +62,15 @@ Currently, users need special permissions to start flows via RPC. These permissi .. note:: Currently, the node's web server has super-user access, meaning that it can run any RPC operation without logging in. This will be changed in a future release. +Permissions Syntax +^^^^^^^^^^^^^^^^^^ + +Fine grained permissions allow a user to invoke a specific RPC operation, or to start a specific flow. The syntax is: + +- to start a specific flow: ``StartFlow.`` e.g., ``StartFlow.net.corda.flows.ExampleFlow1``. +- to invoke a RPC operation: ``InvokeRpc.`` e.g., ``InvokeRpc.nodeInfo``. +.. note:: Permission ``InvokeRpc.startFlow`` allows a user to initiate all flows. + Observables ----------- The RPC system handles observables in a special way. When a method returns an observable, whether directly or 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 c665a8cb67..a5ee68bce7 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,6 +1,7 @@ package net.corda.docs import net.corda.core.internal.concurrent.transpose +import net.corda.core.messaging.CordaRPCOps import net.corda.core.messaging.startFlow import net.corda.core.messaging.vaultTrackBy import net.corda.core.node.services.Vault @@ -10,7 +11,8 @@ import net.corda.finance.DOLLARS import net.corda.finance.contracts.asset.Cash import net.corda.finance.flows.CashIssueFlow import net.corda.finance.flows.CashPaymentFlow -import net.corda.node.services.FlowPermissions.Companion.startFlowPermission +import net.corda.node.services.Permissions.Companion.startFlow +import net.corda.node.services.Permissions.Companion.invokeRpc import net.corda.nodeapi.User import net.corda.testing.* import net.corda.testing.driver.driver @@ -24,11 +26,18 @@ class IntegrationTestingTutorial { driver(startNodesInProcess = true, extraCordappPackagesToScan = listOf("net.corda.finance.contracts.asset")) { val aliceUser = User("aliceUser", "testPassword1", permissions = setOf( - startFlowPermission(), - startFlowPermission() + startFlow(), + startFlow(), + invokeRpc(CordaRPCOps::waitUntilNetworkReady), + invokeRpc("vaultTrackBy"), + invokeRpc(CordaRPCOps::notaryIdentities), + invokeRpc(CordaRPCOps::networkMapFeed) )) val bobUser = User("bobUser", "testPassword2", permissions = setOf( - startFlowPermission() + startFlow(), + invokeRpc(CordaRPCOps::waitUntilNetworkReady), + invokeRpc("vaultTrackBy"), + invokeRpc(CordaRPCOps::networkMapFeed) )) val (alice, bob) = listOf( startNode(providedName = ALICE.name, rpcUsers = listOf(aliceUser)), 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 5b11d79549..1cfb712ec8 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 @@ -14,7 +14,8 @@ import net.corda.finance.contracts.asset.Cash import net.corda.finance.flows.CashExitFlow import net.corda.finance.flows.CashIssueFlow import net.corda.finance.flows.CashPaymentFlow -import net.corda.node.services.FlowPermissions.Companion.startFlowPermission +import net.corda.node.services.Permissions.Companion.startFlow +import net.corda.node.services.Permissions.Companion.invokeRpc import net.corda.nodeapi.User import net.corda.testing.ALICE import net.corda.testing.DUMMY_NOTARY @@ -42,10 +43,11 @@ fun main(args: Array) { val printOrVisualise = PrintOrVisualise.valueOf(args[0]) val baseDirectory = Paths.get("build/rpc-api-tutorial") - val user = User("user", "password", permissions = setOf(startFlowPermission(), - startFlowPermission(), - startFlowPermission())) - + val user = User("user", "password", permissions = setOf(startFlow(), + startFlow(), + startFlow(), + invokeRpc(CordaRPCOps::nodeInfo) + )) driver(driverDirectory = baseDirectory, extraCordappPackagesToScan = listOf("net.corda.finance")) { startNotaryNode(DUMMY_NOTARY.name) val node = startNode(providedName = ALICE.name, rpcUsers = listOf(user)).get() @@ -143,4 +145,4 @@ class ExampleRPCSerializationWhitelist : SerializationWhitelist { // Add classes like this. override val whitelist = listOf(ExampleRPCValue::class.java) } -// END 7 +// END 7 \ No newline at end of file diff --git a/docs/source/tutorial-clientrpc-api.rst b/docs/source/tutorial-clientrpc-api.rst index aa49889b4f..66c9846dae 100644 --- a/docs/source/tutorial-clientrpc-api.rst +++ b/docs/source/tutorial-clientrpc-api.rst @@ -122,7 +122,7 @@ In the instructions above the server node permissions are configured programmati .. code-block:: text driver(driverDirectory = baseDirectory) { - val user = User("user", "password", permissions = setOf(startFlowPermission())) + val user = User("user", "password", permissions = setOf(startFlow())) val node = startNode("CN=Alice Corp,O=Alice Corp,L=London,C=GB", rpcUsers = listOf(user)).get() When starting a standalone node using a configuration file we must supply the RPC credentials as follows: 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 f17465ac47..e4c4ff5198 100644 --- a/node/src/integration-test/kotlin/net/corda/node/BootTests.kt +++ b/node/src/integration-test/kotlin/net/corda/node/BootTests.kt @@ -7,7 +7,7 @@ import net.corda.core.internal.div import net.corda.core.messaging.startFlow import net.corda.core.utilities.getOrThrow import net.corda.node.internal.NodeStartup -import net.corda.node.services.FlowPermissions.Companion.startFlowPermission +import net.corda.node.services.Permissions.Companion.startFlow import net.corda.nodeapi.User import net.corda.nodeapi.internal.ServiceInfo import net.corda.nodeapi.internal.ServiceType @@ -29,7 +29,7 @@ class BootTests { @Test fun `java deserialization is disabled`() { driver { - val user = User("u", "p", setOf(startFlowPermission())) + val user = User("u", "p", setOf(startFlow())) val future = startNode(rpcUsers = listOf(user)).getOrThrow().rpcClientToNode(). start(user.username, user.password).proxy.startFlow(::ObjectInputStreamFlow).returnValue assertThatThrownBy { future.getOrThrow() }.isInstanceOf(InvalidClassException::class.java).hasMessage("filter status: REJECTED") 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 50788582b5..8515b9b4ec 100644 --- a/node/src/integration-test/kotlin/net/corda/node/CordappScanningDriverTest.kt +++ b/node/src/integration-test/kotlin/net/corda/node/CordappScanningDriverTest.kt @@ -7,7 +7,7 @@ import net.corda.core.internal.concurrent.transpose import net.corda.core.messaging.startFlow import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.unwrap -import net.corda.node.services.FlowPermissions.Companion.startFlowPermission +import net.corda.node.services.Permissions.Companion.startFlow import net.corda.nodeapi.User import net.corda.testing.ALICE import net.corda.testing.BOB @@ -19,7 +19,7 @@ import org.junit.Test class CordappScanningDriverTest { @Test fun `sub-classed initiated flow pointing to the same initiating flow as its super-class`() { - val user = User("u", "p", setOf(startFlowPermission())) + val user = User("u", "p", setOf(startFlow())) // The driver will automatically pick up the annotated flows below driver { val (alice, bob) = listOf( 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 6cddc95bed..eac55b5544 100644 --- a/node/src/integration-test/kotlin/net/corda/node/NodePerformanceTests.kt +++ b/node/src/integration-test/kotlin/net/corda/node/NodePerformanceTests.kt @@ -12,7 +12,7 @@ import net.corda.core.utilities.minutes import net.corda.finance.DOLLARS import net.corda.finance.flows.CashIssueFlow import net.corda.finance.flows.CashPaymentFlow -import net.corda.node.services.FlowPermissions.Companion.startFlowPermission +import net.corda.node.services.Permissions.Companion.startFlow import net.corda.nodeapi.User import net.corda.testing.DUMMY_NOTARY import net.corda.testing.chooseIdentity @@ -59,7 +59,7 @@ class NodePerformanceTests { @Test fun `empty flow per second`() { driver(startNodesInProcess = true) { - val a = startNode(rpcUsers = listOf(User("A", "A", setOf(startFlowPermission())))).get() + val a = startNode(rpcUsers = listOf(User("A", "A", setOf(startFlow())))).get() a.rpcClientToNode().use("A", "A") { connection -> val timings = Collections.synchronizedList(ArrayList()) @@ -89,7 +89,7 @@ class NodePerformanceTests { @Test fun `empty flow rate`() { driver(startNodesInProcess = true) { - val a = startNode(rpcUsers = listOf(User("A", "A", setOf(startFlowPermission())))).get() + val a = startNode(rpcUsers = listOf(User("A", "A", setOf(startFlow())))).get() a as NodeHandle.InProcess val metricRegistry = startReporter(shutdownManager, a.node.services.monitoringService.metrics) a.rpcClientToNode().use("A", "A") { connection -> @@ -105,7 +105,7 @@ class NodePerformanceTests { driver(startNodesInProcess = true, extraCordappPackagesToScan = listOf("net.corda.finance")) { val a = startNotaryNode( DUMMY_NOTARY.name, - rpcUsers = listOf(User("A", "A", setOf(startFlowPermission(), startFlowPermission()))) + rpcUsers = listOf(User("A", "A", setOf(startFlow(), startFlow()))) ).getOrThrow() a as NodeHandle.InProcess val metricRegistry = startReporter(shutdownManager, a.node.services.monitoringService.metrics) 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 06ed089e16..6952390506 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 @@ -11,7 +11,8 @@ import net.corda.core.utilities.getOrThrow import net.corda.finance.POUNDS import net.corda.finance.flows.CashIssueFlow import net.corda.finance.flows.CashPaymentFlow -import net.corda.node.services.FlowPermissions.Companion.startFlowPermission +import net.corda.node.services.Permissions.Companion.startFlow +import net.corda.node.services.Permissions.Companion.invokeRpc import net.corda.node.services.transactions.RaftValidatingNotaryService import net.corda.nodeapi.User import net.corda.testing.* @@ -34,8 +35,10 @@ class DistributedServiceTests : DriverBasedTest() { // Start Alice and 3 notaries in a RAFT cluster val clusterSize = 3 val testUser = User("test", "test", permissions = setOf( - startFlowPermission(), - startFlowPermission()) + startFlow(), + startFlow(), + invokeRpc(CordaRPCOps::nodeInfo), + invokeRpc(CordaRPCOps::stateMachinesFeed)) ) val aliceFuture = startNode(providedName = ALICE.name, rpcUsers = listOf(testUser)) val notariesFuture = startNotaryCluster( @@ -137,4 +140,4 @@ class DistributedServiceTests : DriverBasedTest() { private fun paySelf(amount: Amount) { aliceProxy.startFlow(::CashPaymentFlow, amount, alice.nodeInfo.chooseIdentity()).returnValue.getOrThrow() } -} +} \ No newline at end of file diff --git a/node/src/integration-test/kotlin/net/corda/test/node/NodeStatePersistenceTests.kt b/node/src/integration-test/kotlin/net/corda/test/node/NodeStatePersistenceTests.kt index ebc494c3c1..00edc6d782 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 @@ -17,7 +17,8 @@ import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.TransactionBuilder import net.corda.core.utilities.ProgressTracker import net.corda.core.utilities.getOrThrow -import net.corda.node.services.FlowPermissions +import net.corda.node.services.Permissions.Companion.startFlow +import net.corda.node.services.Permissions.Companion.invokeRpc import net.corda.nodeapi.User import net.corda.testing.DUMMY_NOTARY import net.corda.testing.chooseIdentity @@ -39,7 +40,7 @@ class NodeStatePersistenceTests { // More investigation is needed to establish why. Assume.assumeFalse(System.getProperty("os.name").toLowerCase().startsWith("win")) - val user = User("mark", "dadada", setOf(FlowPermissions.startFlowPermission())) + val user = User("mark", "dadada", setOf(startFlow(), invokeRpc("vaultQuery"))) val message = Message("Hello world!") driver(isDebug = true, startNodesInProcess = isQuasarAgentSpecified()) { val (nodeName, notaryNodeHandle) = { 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 e61471a8e1..23b08e1542 100644 --- a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt +++ b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt @@ -154,7 +154,7 @@ abstract class AbstractNode(config: NodeConfiguration, /** The implementation of the [CordaRPCOps] interface used by this node. */ open fun makeRPCOps(flowStarter: FlowStarter): CordaRPCOps { - return CordaRPCOpsImpl(services, smm, database, flowStarter) + return SecureCordaRPCOps(services, smm, database, flowStarter) } private fun saveOwnNodeInfo() { diff --git a/node/src/main/kotlin/net/corda/node/internal/CordaRPCOpsImpl.kt b/node/src/main/kotlin/net/corda/node/internal/CordaRPCOpsImpl.kt index 863835c25a..6871c234ba 100644 --- a/node/src/main/kotlin/net/corda/node/internal/CordaRPCOpsImpl.kt +++ b/node/src/main/kotlin/net/corda/node/internal/CordaRPCOpsImpl.kt @@ -20,11 +20,9 @@ import net.corda.core.node.services.vault.QueryCriteria import net.corda.core.node.services.vault.Sort import net.corda.core.transactions.SignedTransaction import net.corda.core.utilities.getOrThrow -import net.corda.node.services.FlowPermissions.Companion.startFlowPermission import net.corda.node.services.api.FlowStarter import net.corda.node.services.api.ServiceHubInternal -import net.corda.node.services.messaging.getRpcContext -import net.corda.node.services.messaging.requirePermission +import net.corda.node.services.messaging.rpcContext import net.corda.node.services.statemachine.StateMachineManager import net.corda.node.utilities.CordaPersistence import rx.Observable @@ -36,7 +34,7 @@ import java.time.Instant * Server side implementations of RPCs available to MQ based client tools. Execution takes place on the server * thread (i.e. serially). Arguments are serialised and deserialised automatically. */ -class CordaRPCOpsImpl( +internal class CordaRPCOpsImpl( private val services: ServiceHubInternal, private val smm: StateMachineManager, private val database: CordaPersistence, @@ -149,9 +147,7 @@ class CordaRPCOpsImpl( private fun startFlow(logicType: Class>, args: Array): FlowStateMachine { require(logicType.isAnnotationPresent(StartableByRPC::class.java)) { "${logicType.name} was not designed for RPC" } - val rpcContext = getRpcContext() - rpcContext.requirePermission(startFlowPermission(logicType)) - val currentUser = FlowInitiator.RPC(rpcContext.currentUser.username) + val currentUser = FlowInitiator.RPC(rpcContext().currentUser.username) // TODO RPC flows should have mapping user -> identity that should be resolved automatically on starting flow. return flowStarter.invokeFlowAsync(logicType, currentUser, *args).getOrThrow() } @@ -221,6 +217,38 @@ class CordaRPCOpsImpl( } } + override fun vaultQuery(contractStateType: Class): Vault.Page { + return vaultQueryBy(QueryCriteria.VaultQueryCriteria(), PageSpecification(), Sort(emptySet()), contractStateType) + } + + override fun vaultQueryByCriteria(criteria: QueryCriteria, contractStateType: Class): Vault.Page { + return vaultQueryBy(criteria, PageSpecification(), Sort(emptySet()), contractStateType) + } + + override fun vaultQueryByWithPagingSpec(contractStateType: Class, criteria: QueryCriteria, paging: PageSpecification): Vault.Page { + return vaultQueryBy(criteria, paging, Sort(emptySet()), contractStateType) + } + + override fun vaultQueryByWithSorting(contractStateType: Class, criteria: QueryCriteria, sorting: Sort): Vault.Page { + return vaultQueryBy(criteria, PageSpecification(), sorting, contractStateType) + } + + override fun vaultTrack(contractStateType: Class): DataFeed, Vault.Update> { + return vaultTrackBy(QueryCriteria.VaultQueryCriteria(), PageSpecification(), Sort(emptySet()), contractStateType) + } + + override fun vaultTrackByCriteria(contractStateType: Class, criteria: QueryCriteria): DataFeed, Vault.Update> { + return vaultTrackBy(criteria, PageSpecification(), Sort(emptySet()), contractStateType) + } + + override fun vaultTrackByWithPagingSpec(contractStateType: Class, criteria: QueryCriteria, paging: PageSpecification): DataFeed, Vault.Update> { + return vaultTrackBy(criteria, paging, Sort(emptySet()), contractStateType) + } + + override fun vaultTrackByWithSorting(contractStateType: Class, criteria: QueryCriteria, sorting: Sort): DataFeed, Vault.Update> { + return vaultTrackBy(criteria, PageSpecification(), sorting, contractStateType) + } + companion object { private fun stateMachineInfoFromFlowLogic(flowLogic: FlowLogic<*>): StateMachineInfo { return StateMachineInfo(flowLogic.runId, flowLogic.javaClass.name, flowLogic.stateMachine.flowInitiator, flowLogic.track()) @@ -233,6 +261,4 @@ class CordaRPCOpsImpl( } } } - -} - +} \ No newline at end of file diff --git a/node/src/main/kotlin/net/corda/node/internal/DefaultCordaRpcPermissions.kt b/node/src/main/kotlin/net/corda/node/internal/DefaultCordaRpcPermissions.kt new file mode 100644 index 0000000000..dccec894e7 --- /dev/null +++ b/node/src/main/kotlin/net/corda/node/internal/DefaultCordaRpcPermissions.kt @@ -0,0 +1,25 @@ +package net.corda.node.internal + +import net.corda.core.flows.FlowLogic +import net.corda.core.messaging.CordaRPCOps +import net.corda.node.services.Permissions.Companion.all +import net.corda.node.services.Permissions.Companion.startFlow +import net.corda.node.services.Permissions.Companion.invokeRpc +import kotlin.reflect.KVisibility +import kotlin.reflect.full.declaredMemberFunctions + +object DefaultCordaRpcPermissions { + + private val invokePermissions = CordaRPCOps::class.declaredMemberFunctions.filter { it.visibility == KVisibility.PUBLIC }.associate { it.name to setOf(invokeRpc(it), all()) } + private val startFlowPermissions = setOf("startFlow", "startFlowDynamic", "startTrackedFlow", "startTrackedFlowDynamic").associate { it to this::startFlowPermission } + + fun permissionsAllowing(methodName: String, args: List): Set { + + val invoke = invokePermissions[methodName] ?: emptySet() + val start = startFlowPermissions[methodName]?.invoke(args) + return if (start != null) invoke + start else invoke + } + + @Suppress("UNCHECKED_CAST") + private fun startFlowPermission(args: List): String = if (args[0] is Class<*>) startFlow(args[0] as Class>) else startFlow(args[0] as String) +} \ No newline at end of file diff --git a/node/src/main/kotlin/net/corda/node/internal/RpcAuthorisationProxy.kt b/node/src/main/kotlin/net/corda/node/internal/RpcAuthorisationProxy.kt new file mode 100644 index 0000000000..c99103b06d --- /dev/null +++ b/node/src/main/kotlin/net/corda/node/internal/RpcAuthorisationProxy.kt @@ -0,0 +1,158 @@ +package net.corda.node.internal + +import net.corda.core.contracts.ContractState +import net.corda.core.crypto.SecureHash +import net.corda.core.flows.FlowLogic +import net.corda.core.identity.AbstractParty +import net.corda.core.identity.CordaX500Name +import net.corda.core.identity.Party +import net.corda.core.messaging.CordaRPCOps +import net.corda.core.messaging.DataFeed +import net.corda.core.node.NodeInfo +import net.corda.core.node.services.NetworkMapCache +import net.corda.core.node.services.Vault +import net.corda.core.node.services.vault.PageSpecification +import net.corda.core.node.services.vault.QueryCriteria +import net.corda.core.node.services.vault.Sort +import net.corda.node.services.messaging.RpcContext +import net.corda.node.services.messaging.requireEitherPermission +import java.io.InputStream +import java.security.PublicKey + +// TODO change to KFunction reference after Kotlin fixes https://youtrack.jetbrains.com/issue/KT-12140 +class RpcAuthorisationProxy(private val implementation: CordaRPCOps, private val context: () -> RpcContext, private val permissionsAllowing: (methodName: String, args: List) -> Set) : CordaRPCOps { + + override fun stateMachinesSnapshot() = guard("stateMachinesSnapshot") { + implementation.stateMachinesSnapshot() + } + + override fun stateMachinesFeed() = guard("stateMachinesFeed") { + implementation.stateMachinesFeed() + } + + override fun vaultQueryBy(criteria: QueryCriteria, paging: PageSpecification, sorting: Sort, contractStateType: Class) = guard("vaultQueryBy") { + implementation.vaultQueryBy(criteria, paging, sorting, contractStateType) + } + + override fun vaultTrackBy(criteria: QueryCriteria, paging: PageSpecification, sorting: Sort, contractStateType: Class) = guard("vaultTrackBy") { + implementation.vaultTrackBy(criteria, paging, sorting, contractStateType) + } + + override fun internalVerifiedTransactionsSnapshot() = guard("internalVerifiedTransactionsSnapshot", implementation::internalVerifiedTransactionsSnapshot) + + override fun internalVerifiedTransactionsFeed() = guard("internalVerifiedTransactionsFeed", implementation::internalVerifiedTransactionsFeed) + + override fun stateMachineRecordedTransactionMappingSnapshot() = guard("stateMachineRecordedTransactionMappingSnapshot", implementation::stateMachineRecordedTransactionMappingSnapshot) + + override fun stateMachineRecordedTransactionMappingFeed() = guard("stateMachineRecordedTransactionMappingFeed", implementation::stateMachineRecordedTransactionMappingFeed) + + override fun networkMapSnapshot(): List = guard("networkMapSnapshot", implementation::networkMapSnapshot) + + override fun networkMapFeed(): DataFeed, NetworkMapCache.MapChange> = guard("networkMapFeed", implementation::networkMapFeed) + + override fun startFlowDynamic(logicType: Class>, vararg args: Any?) = guard("startFlowDynamic", listOf(logicType)) { + implementation.startFlowDynamic(logicType, *args) + } + + override fun startTrackedFlowDynamic(logicType: Class>, vararg args: Any?) = guard("startTrackedFlowDynamic", listOf(logicType)) { + implementation.startTrackedFlowDynamic(logicType, *args) + } + + override fun nodeInfo(): NodeInfo = guard("nodeInfo", implementation::nodeInfo) + + override fun notaryIdentities(): List = guard("notaryIdentities", implementation::notaryIdentities) + + override fun addVaultTransactionNote(txnId: SecureHash, txnNote: String) = guard("addVaultTransactionNote") { + implementation.addVaultTransactionNote(txnId, txnNote) + } + + override fun getVaultTransactionNotes(txnId: SecureHash): Iterable = guard("getVaultTransactionNotes") { + implementation.getVaultTransactionNotes(txnId) + } + + override fun attachmentExists(id: SecureHash) = guard("attachmentExists") { + implementation.attachmentExists(id) + } + + override fun openAttachment(id: SecureHash) = guard("openAttachment") { + implementation.openAttachment(id) + } + + override fun uploadAttachment(jar: InputStream) = guard("uploadAttachment") { + implementation.uploadAttachment(jar) + } + + override fun currentNodeTime() = guard("currentNodeTime", implementation::currentNodeTime) + + override fun waitUntilNetworkReady() = guard("waitUntilNetworkReady", implementation::waitUntilNetworkReady) + + override fun wellKnownPartyFromAnonymous(party: AbstractParty) = guard("wellKnownPartyFromAnonymous") { + implementation.wellKnownPartyFromAnonymous(party) + } + + override fun partyFromKey(key: PublicKey) = guard("partyFromKey") { + implementation.partyFromKey(key) + } + + override fun wellKnownPartyFromX500Name(x500Name: CordaX500Name) = guard("wellKnownPartyFromX500Name") { + implementation.wellKnownPartyFromX500Name(x500Name) + } + + override fun notaryPartyFromX500Name(x500Name: CordaX500Name) = guard("notaryPartyFromX500Name") { + implementation.notaryPartyFromX500Name(x500Name) + } + + override fun partiesFromName(query: String, exactMatch: Boolean) = guard("partiesFromName") { + implementation.partiesFromName(query, exactMatch) + } + + override fun registeredFlows() = guard("registeredFlows", implementation::registeredFlows) + + override fun nodeInfoFromParty(party: AbstractParty) = guard("nodeInfoFromParty") { + implementation.nodeInfoFromParty(party) + } + + override fun clearNetworkMapCache() = guard("clearNetworkMapCache", implementation::clearNetworkMapCache) + + override fun vaultQuery(contractStateType: Class): Vault.Page = guard("vaultQuery") { + implementation.vaultQuery(contractStateType) + } + + override fun vaultQueryByCriteria(criteria: QueryCriteria, contractStateType: Class): Vault.Page = guard("vaultQueryByCriteria") { + implementation.vaultQueryByCriteria(criteria, contractStateType) + } + + override fun vaultQueryByWithPagingSpec(contractStateType: Class, criteria: QueryCriteria, paging: PageSpecification): Vault.Page = guard("vaultQueryByWithPagingSpec") { + implementation.vaultQueryByWithPagingSpec(contractStateType, criteria, paging) + } + + override fun vaultQueryByWithSorting(contractStateType: Class, criteria: QueryCriteria, sorting: Sort): Vault.Page = guard("vaultQueryByWithSorting") { + implementation.vaultQueryByWithSorting(contractStateType, criteria, sorting) + } + + override fun vaultTrack(contractStateType: Class): DataFeed, Vault.Update> = guard("vaultTrack") { + implementation.vaultTrack(contractStateType) + } + + override fun vaultTrackByCriteria(contractStateType: Class, criteria: QueryCriteria): DataFeed, Vault.Update> = guard("vaultTrackByCriteria") { + implementation.vaultTrackByCriteria(contractStateType, criteria) + } + + override fun vaultTrackByWithPagingSpec(contractStateType: Class, criteria: QueryCriteria, paging: PageSpecification): DataFeed, Vault.Update> = guard("vaultTrackByWithPagingSpec") { + implementation.vaultTrackByWithPagingSpec(contractStateType, criteria, paging) + } + + override fun vaultTrackByWithSorting(contractStateType: Class, criteria: QueryCriteria, sorting: Sort): DataFeed, Vault.Update> = guard("vaultTrackByWithSorting") { + implementation.vaultTrackByWithSorting(contractStateType, criteria, sorting) + } + + // TODO change to KFunction reference after Kotlin fixes https://youtrack.jetbrains.com/issue/KT-12140 + private inline fun guard(methodName: String, action: () -> RESULT) = guard(methodName, emptyList(), action) + + // TODO change to KFunction reference after Kotlin fixes https://youtrack.jetbrains.com/issue/KT-12140 + private inline fun guard(methodName: String, args: List, action: () -> RESULT): RESULT { + + context.invoke().requireEitherPermission(permissionsAllowing.invoke(methodName, args)) + return action.invoke() + } +} \ No newline at end of file diff --git a/node/src/main/kotlin/net/corda/node/internal/SecureCordaRPCOps.kt b/node/src/main/kotlin/net/corda/node/internal/SecureCordaRPCOps.kt new file mode 100644 index 0000000000..7083a434d2 --- /dev/null +++ b/node/src/main/kotlin/net/corda/node/internal/SecureCordaRPCOps.kt @@ -0,0 +1,24 @@ +package net.corda.node.internal + +import net.corda.core.messaging.CordaRPCOps +import net.corda.node.services.api.FlowStarter +import net.corda.node.services.api.ServiceHubInternal +import net.corda.node.services.messaging.rpcContext +import net.corda.node.services.statemachine.StateMachineManager +import net.corda.node.utilities.CordaPersistence + +/** + * Implementation of [CordaRPCOps] that checks authorisation. + */ +class SecureCordaRPCOps(services: ServiceHubInternal, + smm: StateMachineManager, + database: CordaPersistence, + flowStarter: FlowStarter, + val unsafe: CordaRPCOps = CordaRPCOpsImpl(services, smm, database, flowStarter)) : CordaRPCOps by RpcAuthorisationProxy(unsafe, ::rpcContext, DefaultCordaRpcPermissions::permissionsAllowing) { + + /** + * Returns the RPC protocol version, which is the same the node's Platform Version. Exists since version 1 so guaranteed + * to be present. + */ + override val protocolVersion: Int get() = unsafe.nodeInfo().platformVersion +} \ No newline at end of file diff --git a/node/src/main/kotlin/net/corda/node/services/Permissions.kt b/node/src/main/kotlin/net/corda/node/services/Permissions.kt new file mode 100644 index 0000000000..f87060fba1 --- /dev/null +++ b/node/src/main/kotlin/net/corda/node/services/Permissions.kt @@ -0,0 +1,59 @@ +package net.corda.node.services + +import net.corda.core.flows.FlowLogic +import kotlin.reflect.KFunction + +/** + * Helper class for creating permissions. + */ +class Permissions { + + companion object { + + /** + * Global admin permissions. + */ + @JvmStatic + fun all() = "ALL" + + /** + * Creates the flow permission string of the format "StartFlow.{ClassName}". + * + * @param className a flow class name for which permission is created. + */ + @JvmStatic + fun startFlow(className: String) = "StartFlow.$className" + + /** + * An overload for the [startFlow] + * + * @param clazz a class for which permission is created. + */ + @JvmStatic + fun

> startFlow(clazz: Class

) = startFlow(clazz.name) + + /** + * An overload for the [startFlow]. + * + * @param P a class for which permission is created. + */ + @JvmStatic + inline fun > startFlow(): String = startFlow(P::class.java) + + /** + * Creates a permission string with format "InvokeRpc.{MethodName}". + * + * @param methodName a RPC method name for which permission is created. + */ + @JvmStatic + fun invokeRpc(methodName: String) = "InvokeRpc.$methodName" + + /** + * Creates a permission string with format "InvokeRpc.{method.name}". + * + * @param method a RPC [KFunction] for which permission is created. + */ + @JvmStatic + fun invokeRpc(method: KFunction<*>) = invokeRpc(method.name) + } +} \ No newline at end of file diff --git a/node/src/main/kotlin/net/corda/node/services/RPCUserService.kt b/node/src/main/kotlin/net/corda/node/services/RPCUserService.kt index 58a50dceb7..371815ed94 100644 --- a/node/src/main/kotlin/net/corda/node/services/RPCUserService.kt +++ b/node/src/main/kotlin/net/corda/node/services/RPCUserService.kt @@ -1,6 +1,5 @@ package net.corda.node.services -import net.corda.core.flows.FlowLogic import net.corda.nodeapi.User /** @@ -26,36 +25,3 @@ class RPCUserServiceImpl(override val users: List) : RPCUserService { override fun getUser(username: String): User? = users.find { it.username == username } } - -/** - * Helper class for creating flow class permissions. - */ -class FlowPermissions { - companion object { - - /** - * Creates the flow permission string of the format "StartFlow.{ClassName}". - * - * @param className a flow class name for which permission is created. - */ - @JvmStatic - fun startFlowPermission(className: String) = "StartFlow.$className" - - /** - * An overload for the [startFlowPermission] - * - * @param clazz a class for which permission is created. - * - */ - @JvmStatic - fun

> startFlowPermission(clazz: Class

) = startFlowPermission(clazz.name) - - /** - * An overload for the [startFlowPermission]. - * - * @param P a class for which permission is created. - */ - @JvmStatic - inline fun > startFlowPermission(): String = startFlowPermission(P::class.java) - } -} diff --git a/node/src/main/kotlin/net/corda/node/services/messaging/RPCServer.kt b/node/src/main/kotlin/net/corda/node/services/messaging/RPCServer.kt index ae4f328378..d74ffa35c6 100644 --- a/node/src/main/kotlin/net/corda/node/services/messaging/RPCServer.kt +++ b/node/src/main/kotlin/net/corda/node/services/messaging/RPCServer.kt @@ -373,7 +373,7 @@ internal val CURRENT_RPC_CONTEXT: ThreadLocal = ThreadLocal() * throw. If you'd like to use the context outside of the call (e.g. in another thread) then pass the returned reference * around explicitly. */ -fun getRpcContext(): RpcContext = CURRENT_RPC_CONTEXT.get() +fun rpcContext(): RpcContext = CURRENT_RPC_CONTEXT.get() /** * @param currentUser This is available to RPC implementations to query the validated [User] that is calling it. Each diff --git a/node/src/main/kotlin/net/corda/node/services/messaging/RPCServerStructures.kt b/node/src/main/kotlin/net/corda/node/services/messaging/RPCServerStructures.kt index e8e91d793f..6e68a81687 100644 --- a/node/src/main/kotlin/net/corda/node/services/messaging/RPCServerStructures.kt +++ b/node/src/main/kotlin/net/corda/node/services/messaging/RPCServerStructures.kt @@ -3,13 +3,18 @@ package net.corda.node.services.messaging import net.corda.client.rpc.PermissionException +import net.corda.node.services.Permissions.Companion.all import net.corda.nodeapi.ArtemisMessagingComponent /** Helper method which checks that the current RPC user is entitled for the given permission. Throws a [PermissionException] otherwise. */ -fun RpcContext.requirePermission(permission: String) { +fun RpcContext.requirePermission(permission: String): RpcContext = requireEitherPermission(setOf(permission)) + +/** Helper method which checks that the current RPC user is entitled with any of the given permissions. Throws a [PermissionException] otherwise. */ +fun RpcContext.requireEitherPermission(permissions: Set): RpcContext { // TODO remove the NODE_USER condition once webserver doesn't need it val currentUserPermissions = currentUser.permissions - if (currentUser.username != ArtemisMessagingComponent.NODE_USER && currentUserPermissions.intersect(listOf(permission, "ALL")).isEmpty()) { - throw PermissionException("User not permissioned for $permission, permissions are $currentUserPermissions") + if (currentUser.username != ArtemisMessagingComponent.NODE_USER && currentUserPermissions.intersect(permissions + all()).isEmpty()) { + throw PermissionException("User not permissioned with any of $permissions, permissions are $currentUserPermissions") } -} + return this +} \ No newline at end of file diff --git a/node/src/test/kotlin/net/corda/node/CordaRPCOpsImplTest.kt b/node/src/test/kotlin/net/corda/node/CordaRPCOpsImplTest.kt index ae39004b31..cae74c44ba 100644 --- a/node/src/test/kotlin/net/corda/node/CordaRPCOpsImplTest.kt +++ b/node/src/test/kotlin/net/corda/node/CordaRPCOpsImplTest.kt @@ -23,9 +23,10 @@ import net.corda.finance.USD import net.corda.finance.contracts.asset.Cash import net.corda.finance.flows.CashIssueFlow import net.corda.finance.flows.CashPaymentFlow -import net.corda.node.internal.CordaRPCOpsImpl +import net.corda.node.internal.SecureCordaRPCOps import net.corda.node.internal.StartedNode -import net.corda.node.services.FlowPermissions.Companion.startFlowPermission +import net.corda.node.services.Permissions.Companion.startFlow +import net.corda.node.services.Permissions.Companion.invokeRpc import net.corda.node.services.messaging.CURRENT_RPC_CONTEXT import net.corda.node.services.messaging.RpcContext import net.corda.nodeapi.User @@ -63,19 +64,20 @@ class CordaRPCOpsImplTest { lateinit var transactions: Observable lateinit var vaultTrackCash: Observable> + private val user = User("user", "pwd", permissions = emptySet()) + @Before fun setup() { mockNet = MockNetwork(cordappPackages = listOf("net.corda.finance.contracts.asset")) aliceNode = mockNet.createNode() notaryNode = mockNet.createNotaryNode(validating = false) - rpc = CordaRPCOpsImpl(aliceNode.services, aliceNode.smm, aliceNode.database, aliceNode.services) - CURRENT_RPC_CONTEXT.set(RpcContext(User("user", "pwd", permissions = setOf( - startFlowPermission(), - startFlowPermission() - )))) + rpc = SecureCordaRPCOps(aliceNode.services, aliceNode.smm, aliceNode.database, aliceNode.services) + CURRENT_RPC_CONTEXT.set(RpcContext(user)) mockNet.runNetwork() - notary = rpc.notaryIdentities().first() + withPermissions(invokeRpc(CordaRPCOps::notaryIdentities)) { + notary = rpc.notaryIdentities().first() + } } @After @@ -85,170 +87,185 @@ class CordaRPCOpsImplTest { @Test fun `cash issue accepted`() { - aliceNode.database.transaction { - stateMachineUpdates = rpc.stateMachinesFeed().updates - vaultTrackCash = rpc.vaultTrackBy().updates - } - val quantity = 1000L - val ref = OpaqueBytes(ByteArray(1) { 1 }) + withPermissions(invokeRpc("vaultTrackBy"), invokeRpc("vaultQueryBy"), invokeRpc(CordaRPCOps::stateMachinesFeed), startFlow()) { - // Check the monitoring service wallet is empty - aliceNode.database.transaction { - assertFalse(aliceNode.services.vaultService.queryBy().totalStatesAvailable > 0) - } + aliceNode.database.transaction { + stateMachineUpdates = rpc.stateMachinesFeed().updates + vaultTrackCash = rpc.vaultTrackBy().updates + } - // Tell the monitoring service node to issue some cash - val result = rpc.startFlow(::CashIssueFlow, Amount(quantity, GBP), ref, notary) - mockNet.runNetwork() + val quantity = 1000L + val ref = OpaqueBytes(ByteArray(1) { 1 }) - var issueSmId: StateMachineRunId? = null - stateMachineUpdates.expectEvents { - sequence( - // ISSUE - expect { add: StateMachineUpdate.Added -> - issueSmId = add.id - }, - expect { remove: StateMachineUpdate.Removed -> - require(remove.id == issueSmId) - } - ) - } + // Check the monitoring service wallet is empty + aliceNode.database.transaction { + assertFalse(aliceNode.services.vaultService.queryBy().totalStatesAvailable > 0) + } - val anonymisedRecipient = result.returnValue.getOrThrow().recipient!! - val expectedState = Cash.State(Amount(quantity, - Issued(aliceNode.info.chooseIdentity().ref(ref), GBP)), - anonymisedRecipient) + // Tell the monitoring service node to issue some cash + val result = rpc.startFlow(::CashIssueFlow, Amount(quantity, GBP), ref, notary) + mockNet.runNetwork() - // Query vault via RPC - val cash = rpc.vaultQueryBy() - assertEquals(expectedState, cash.states.first().state.data) + var issueSmId: StateMachineRunId? = null + stateMachineUpdates.expectEvents { + sequence( + // ISSUE + expect { add: StateMachineUpdate.Added -> + issueSmId = add.id + }, + expect { remove: StateMachineUpdate.Removed -> + require(remove.id == issueSmId) + } + ) + } - vaultTrackCash.expectEvents { - expect { update -> - val actual = update.produced.single().state.data - assertEquals(expectedState, actual) + val anonymisedRecipient = result.returnValue.getOrThrow().recipient!! + val expectedState = Cash.State(Amount(quantity, + Issued(aliceNode.info.chooseIdentity().ref(ref), GBP)), + anonymisedRecipient) + + // Query vault via RPC + val cash = rpc.vaultQueryBy() + assertEquals(expectedState, cash.states.first().state.data) + + vaultTrackCash.expectEvents { + expect { update -> + val actual = update.produced.single().state.data + assertEquals(expectedState, actual) + } } } } @Test fun `issue and move`() { - aliceNode.database.transaction { - stateMachineUpdates = rpc.stateMachinesFeed().updates - transactions = rpc.internalVerifiedTransactionsFeed().updates - vaultTrackCash = rpc.vaultTrackBy().updates - } - val result = rpc.startFlow(::CashIssueFlow, - 100.DOLLARS, - OpaqueBytes(ByteArray(1, { 1 })), - notary - ) + withPermissions(invokeRpc(CordaRPCOps::stateMachinesFeed), + invokeRpc(CordaRPCOps::internalVerifiedTransactionsFeed), + invokeRpc("vaultTrackBy"), + startFlow(), + startFlow()) { + aliceNode.database.transaction { + stateMachineUpdates = rpc.stateMachinesFeed().updates + transactions = rpc.internalVerifiedTransactionsFeed().updates + vaultTrackCash = rpc.vaultTrackBy().updates + } - mockNet.runNetwork() - - rpc.startFlow(::CashPaymentFlow, 100.DOLLARS, aliceNode.info.chooseIdentity()) - - mockNet.runNetwork() - - var issueSmId: StateMachineRunId? = null - var moveSmId: StateMachineRunId? = null - stateMachineUpdates.expectEvents { - sequence( - // ISSUE - expect { add: StateMachineUpdate.Added -> - issueSmId = add.id - }, - expect { remove: StateMachineUpdate.Removed -> - require(remove.id == issueSmId) - }, - // MOVE - expect { add: StateMachineUpdate.Added -> - moveSmId = add.id - }, - expect { remove: StateMachineUpdate.Removed -> - require(remove.id == moveSmId) - } + val result = rpc.startFlow(::CashIssueFlow, + 100.DOLLARS, + OpaqueBytes(ByteArray(1, { 1 })), + notary ) - } - result.returnValue.getOrThrow() - transactions.expectEvents { - sequence( - // ISSUE - expect { stx -> - require(stx.tx.inputs.isEmpty()) - require(stx.tx.outputs.size == 1) - val signaturePubKeys = stx.sigs.map { it.by }.toSet() - // Only Alice signed, as issuer - val aliceKey = aliceNode.info.chooseIdentity().owningKey - require(signaturePubKeys.size <= aliceKey.keys.size) - require(aliceKey.isFulfilledBy(signaturePubKeys)) - }, - // MOVE - expect { stx -> - require(stx.tx.inputs.size == 1) - require(stx.tx.outputs.size == 1) - val signaturePubKeys = stx.sigs.map { it.by }.toSet() - // Alice and Notary signed - require(aliceNode.services.keyManagementService.filterMyKeys(signaturePubKeys).toList().isNotEmpty()) - require(notary.owningKey.isFulfilledBy(signaturePubKeys)) - } - ) - } + mockNet.runNetwork() - vaultTrackCash.expectEvents { - sequence( - // ISSUE - expect { (consumed, produced) -> - require(consumed.isEmpty()) { consumed.size } - require(produced.size == 1) { produced.size } - }, - // MOVE - expect { (consumed, produced) -> - require(consumed.size == 1) { consumed.size } - require(produced.size == 1) { produced.size } - } - ) + rpc.startFlow(::CashPaymentFlow, 100.DOLLARS, aliceNode.info.chooseIdentity()) + + mockNet.runNetwork() + + var issueSmId: StateMachineRunId? = null + var moveSmId: StateMachineRunId? = null + stateMachineUpdates.expectEvents { + sequence( + // ISSUE + expect { add: StateMachineUpdate.Added -> + issueSmId = add.id + }, + expect { remove: StateMachineUpdate.Removed -> + require(remove.id == issueSmId) + }, + // MOVE + expect { add: StateMachineUpdate.Added -> + moveSmId = add.id + }, + expect { remove: StateMachineUpdate.Removed -> + require(remove.id == moveSmId) + } + ) + } + + result.returnValue.getOrThrow() + transactions.expectEvents { + sequence( + // ISSUE + expect { stx -> + require(stx.tx.inputs.isEmpty()) + require(stx.tx.outputs.size == 1) + val signaturePubKeys = stx.sigs.map { it.by }.toSet() + // Only Alice signed, as issuer + val aliceKey = aliceNode.info.chooseIdentity().owningKey + require(signaturePubKeys.size <= aliceKey.keys.size) + require(aliceKey.isFulfilledBy(signaturePubKeys)) + }, + // MOVE + expect { stx -> + require(stx.tx.inputs.size == 1) + require(stx.tx.outputs.size == 1) + val signaturePubKeys = stx.sigs.map { it.by }.toSet() + // Alice and Notary signed + require(aliceNode.services.keyManagementService.filterMyKeys(signaturePubKeys).toList().isNotEmpty()) + require(notary.owningKey.isFulfilledBy(signaturePubKeys)) + } + ) + } + + vaultTrackCash.expectEvents { + sequence( + // ISSUE + expect { (consumed, produced) -> + require(consumed.isEmpty()) { consumed.size } + require(produced.size == 1) { produced.size } + }, + // MOVE + expect { (consumed, produced) -> + require(consumed.size == 1) { consumed.size } + require(produced.size == 1) { produced.size } + } + ) + } } } @Test fun `cash command by user not permissioned for cash`() { - CURRENT_RPC_CONTEXT.set(RpcContext(User("user", "pwd", permissions = emptySet()))) - assertThatExceptionOfType(PermissionException::class.java).isThrownBy { - rpc.startFlow(::CashIssueFlow, Amount(100, USD), OpaqueBytes(ByteArray(1, { 1 })), notary) + withoutAnyPermissions { + assertThatExceptionOfType(PermissionException::class.java).isThrownBy { + rpc.startFlow(::CashIssueFlow, Amount(100, USD), OpaqueBytes(ByteArray(1, { 1 })), notary) + } } } @Test fun `can upload an attachment`() { - val inputJar = Thread.currentThread().contextClassLoader.getResourceAsStream(testJar) - val secureHash = rpc.uploadAttachment(inputJar) - assertTrue(rpc.attachmentExists(secureHash)) + withPermissions(invokeRpc(CordaRPCOps::uploadAttachment), invokeRpc(CordaRPCOps::attachmentExists)) { + val inputJar = Thread.currentThread().contextClassLoader.getResourceAsStream(testJar) + val secureHash = rpc.uploadAttachment(inputJar) + assertTrue(rpc.attachmentExists(secureHash)) + } } @Test fun `can download an uploaded attachment`() { - val inputJar = Thread.currentThread().contextClassLoader.getResourceAsStream(testJar) - val secureHash = rpc.uploadAttachment(inputJar) - val bufferFile = ByteArrayOutputStream() - val bufferRpc = ByteArrayOutputStream() + withPermissions(invokeRpc(CordaRPCOps::uploadAttachment), invokeRpc(CordaRPCOps::openAttachment)) { + val inputJar = Thread.currentThread().contextClassLoader.getResourceAsStream(testJar) + val secureHash = rpc.uploadAttachment(inputJar) + val bufferFile = ByteArrayOutputStream() + val bufferRpc = ByteArrayOutputStream() - IOUtils.copy(Thread.currentThread().contextClassLoader.getResourceAsStream(testJar), bufferFile) - IOUtils.copy(rpc.openAttachment(secureHash), bufferRpc) + IOUtils.copy(Thread.currentThread().contextClassLoader.getResourceAsStream(testJar), bufferFile) + IOUtils.copy(rpc.openAttachment(secureHash), bufferRpc) - assertArrayEquals(bufferFile.toByteArray(), bufferRpc.toByteArray()) + assertArrayEquals(bufferFile.toByteArray(), bufferRpc.toByteArray()) + } } @Test fun `attempt to start non-RPC flow`() { - CURRENT_RPC_CONTEXT.set(RpcContext(User("user", "pwd", permissions = setOf( - startFlowPermission() - )))) - assertThatExceptionOfType(IllegalArgumentException::class.java).isThrownBy { - rpc.startFlow(::NonRPCFlow) + withPermissions(startFlow()) { + assertThatExceptionOfType(IllegalArgumentException::class.java).isThrownBy { + rpc.startFlow(::NonRPCFlow) + } } } @@ -259,12 +276,11 @@ class CordaRPCOpsImplTest { @Test fun `attempt to start RPC flow with void return`() { - CURRENT_RPC_CONTEXT.set(RpcContext(User("user", "pwd", permissions = setOf( - startFlowPermission() - )))) - val result = rpc.startFlow(::VoidRPCFlow) - mockNet.runNetwork() - assertNull(result.returnValue.getOrThrow()) + withPermissions(startFlow()) { + val result = rpc.startFlow(::VoidRPCFlow) + mockNet.runNetwork() + assertNull(result.returnValue.getOrThrow()) + } } @StartableByRPC @@ -272,4 +288,17 @@ class CordaRPCOpsImplTest { @Suspendable override fun call(): Void? = null } -} + + private fun withPermissions(vararg permissions: String, action: () -> Unit) { + + val previous = CURRENT_RPC_CONTEXT.get() + try { + CURRENT_RPC_CONTEXT.set(RpcContext(user.copy(permissions = permissions.toSet()))) + action.invoke() + } finally { + CURRENT_RPC_CONTEXT.set(previous) + } + } + + private fun withoutAnyPermissions(action: () -> Unit) = withPermissions(action = action) +} \ No newline at end of file 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 b1c0ace017..210b62c4fa 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,7 +1,9 @@ package net.corda.attachmentdemo +import net.corda.core.messaging.CordaRPCOps import net.corda.core.utilities.getOrThrow -import net.corda.node.services.FlowPermissions.Companion.startFlowPermission +import net.corda.node.services.Permissions.Companion.invokeRpc +import net.corda.node.services.Permissions.Companion.startFlow import net.corda.nodeapi.User import net.corda.testing.DUMMY_BANK_A import net.corda.testing.DUMMY_BANK_B @@ -17,7 +19,14 @@ class AttachmentDemoTest { fun `attachment demo using a 10MB zip file`() { val numOfExpectedBytes = 10_000_000 driver(isDebug = true, portAllocation = PortAllocation.Incremental(20000)) { - val demoUser = listOf(User("demo", "demo", setOf(startFlowPermission()))) + val demoUser = listOf(User("demo", "demo", setOf( + startFlow(), + invokeRpc(CordaRPCOps::attachmentExists), + invokeRpc(CordaRPCOps::uploadAttachment), + invokeRpc(CordaRPCOps::openAttachment), + invokeRpc(CordaRPCOps::wellKnownPartyFromX500Name), + invokeRpc(CordaRPCOps::internalVerifiedTransactionsFeed) + ))) val (nodeA, nodeB) = listOf( startNode(providedName = DUMMY_BANK_A.name, rpcUsers = demoUser, maximumHeapSize = "1g"), startNode(providedName = DUMMY_BANK_B.name, rpcUsers = demoUser, maximumHeapSize = "1g"), 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 147d6a65e0..83d3f0858f 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.core.messaging.CordaRPCOps import net.corda.core.messaging.startFlow import net.corda.core.node.services.Vault import net.corda.core.node.services.vault.QueryCriteria @@ -7,7 +8,8 @@ import net.corda.core.utilities.getOrThrow import net.corda.finance.DOLLARS import net.corda.finance.contracts.asset.Cash import net.corda.finance.flows.CashIssueAndPaymentFlow -import net.corda.node.services.FlowPermissions.Companion.startFlowPermission +import net.corda.node.services.Permissions.Companion.startFlow +import net.corda.node.services.Permissions.Companion.invokeRpc import net.corda.nodeapi.User import net.corda.testing.* import net.corda.testing.driver.driver @@ -16,10 +18,16 @@ import org.junit.Test class BankOfCordaRPCClientTest { @Test fun `issuer flow via RPC`() { + val commonPermissions = setOf( + invokeRpc("vaultTrackByCriteria"), + invokeRpc(CordaRPCOps::waitUntilNetworkReady), + invokeRpc(CordaRPCOps::wellKnownPartyFromX500Name), + invokeRpc(CordaRPCOps::notaryIdentities) + ) driver(extraCordappPackagesToScan = listOf("net.corda.finance"), dsl = { val bocManager = User("bocManager", "password1", permissions = setOf( - startFlowPermission())) - val bigCorpCFO = User("bigCorpCFO", "password2", permissions = emptySet()) + startFlow()) + commonPermissions) + val bigCorpCFO = User("bigCorpCFO", "password2", permissions = emptySet() + commonPermissions) val nodeBankOfCordaFuture = startNotaryNode(BOC.name, rpcUsers = listOf(bocManager), validating = false) val nodeBigCorporationFuture = startNode(providedName = BIGCORP_LEGAL_NAME, rpcUsers = listOf(bigCorpCFO)) val (nodeBankOfCorda, nodeBigCorporation) = listOf(nodeBankOfCordaFuture, nodeBigCorporationFuture).map { it.getOrThrow() } @@ -80,4 +88,4 @@ class BankOfCordaRPCClientTest { } }, isDebug = true) } -} +} \ No newline at end of file diff --git a/samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/BankOfCordaDriver.kt b/samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/BankOfCordaDriver.kt index b526e580fe..d7ad085a9d 100644 --- a/samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/BankOfCordaDriver.kt +++ b/samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/BankOfCordaDriver.kt @@ -9,7 +9,7 @@ import net.corda.finance.flows.CashConfigDataFlow import net.corda.finance.flows.CashExitFlow import net.corda.finance.flows.CashIssueAndPaymentFlow import net.corda.finance.flows.CashPaymentFlow -import net.corda.node.services.FlowPermissions.Companion.startFlowPermission +import net.corda.node.services.Permissions.Companion.startFlow import net.corda.node.services.transactions.ValidatingNotaryService import net.corda.nodeapi.User import net.corda.testing.BOC @@ -63,19 +63,19 @@ private class BankOfCordaDriver { BANK_USERNAME, "test", permissions = setOf( - startFlowPermission(), - startFlowPermission(), - startFlowPermission(), - startFlowPermission(), - startFlowPermission() + startFlow(), + startFlow(), + startFlow(), + startFlow(), + startFlow() )) val bankOfCorda = startNode( providedName = BOC.name, rpcUsers = listOf(bankUser)) val bigCorpUser = User(BIGCORP_USERNAME, "test", permissions = setOf( - startFlowPermission(), - startFlowPermission())) + startFlow(), + startFlow())) startNode(providedName = BIGCORP_LEGAL_NAME, rpcUsers = listOf(bigCorpUser)) startWebserver(bankOfCorda.get()) waitForAllNodesToFinish() @@ -120,4 +120,3 @@ private class BankOfCordaDriver { } } - 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 0fa498891f..98e7012ef0 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 @@ -37,14 +37,10 @@ class IRSDemoTest : IntegrationTestCategory { val log = loggerFor() } - val rpcUsers = listOf(User("user", "password", - setOf("StartFlow.net.corda.irs.flows.AutoOfferFlow\$Requester", - "StartFlow.net.corda.irs.flows.UpdateBusinessDayFlow\$Broadcast", - "StartFlow.net.corda.irs.api.NodeInterestRates\$UploadFixesFlow"))) - - val currentDate: LocalDate = LocalDate.now() - val futureDate: LocalDate = currentDate.plusMonths(6) - val maxWaitTime: Duration = 60.seconds + private val rpcUsers = listOf(User("user", "password", setOf("ALL"))) + private val currentDate: LocalDate = LocalDate.now() + private val futureDate: LocalDate = currentDate.plusMonths(6) + private val maxWaitTime: Duration = 60.seconds @Test fun `runs IRS demo`() { diff --git a/samples/network-visualiser/src/main/kotlin/net/corda/netmap/simulation/IRSSimulation.kt b/samples/network-visualiser/src/main/kotlin/net/corda/netmap/simulation/IRSSimulation.kt index ae34b399da..59f6d525ee 100644 --- a/samples/network-visualiser/src/main/kotlin/net/corda/netmap/simulation/IRSSimulation.kt +++ b/samples/network-visualiser/src/main/kotlin/net/corda/netmap/simulation/IRSSimulation.kt @@ -140,7 +140,7 @@ class IRSSimulation(networkSendManuallyPumped: Boolean, runAsync: Boolean, laten node1.internals.registerInitiatedFlow(FixingFlow.Fixer::class.java) node2.internals.registerInitiatedFlow(FixingFlow.Fixer::class.java) - val notaryId = node1.rpcOps.notaryIdentities().first() + val notaryId = notary.info.legalIdentities[1] @InitiatingFlow class StartDealFlow(val otherParty: Party, diff --git a/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/SingleNotaryCordform.kt b/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/SingleNotaryCordform.kt index 8e349494b8..97168834c0 100644 --- a/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/SingleNotaryCordform.kt +++ b/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/SingleNotaryCordform.kt @@ -3,7 +3,7 @@ package net.corda.notarydemo import net.corda.cordform.CordformContext import net.corda.cordform.CordformDefinition import net.corda.core.internal.div -import net.corda.node.services.FlowPermissions.Companion.startFlowPermission +import net.corda.node.services.Permissions.Companion.all import net.corda.node.services.config.NotaryConfig import net.corda.nodeapi.User import net.corda.notarydemo.flows.DummyIssueAndMove @@ -19,7 +19,7 @@ import net.corda.testing.internal.demorun.runNodes fun main(args: Array) = SingleNotaryCordform().runNodes() -val notaryDemoUser = User("demou", "demop", setOf(startFlowPermission(), startFlowPermission())) +val notaryDemoUser = User("demou", "demop", setOf(all())) // This is not the intended final design for how to use CordformDefinition, please treat this as experimental and DO // NOT use this as a design to copy. diff --git a/samples/trader-demo/build.gradle b/samples/trader-demo/build.gradle index 392ecd60ac..1a00d51bec 100644 --- a/samples/trader-demo/build.gradle +++ b/samples/trader-demo/build.gradle @@ -42,12 +42,7 @@ dependencies { } task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) { - ext.rpcUsers = [['username': "demo", 'password': "demo", 'permissions': [ - 'StartFlow.net.corda.finance.flows.CashIssueFlow', - 'StartFlow.net.corda.finance.flows.CashPaymentFlow', - 'StartFlow.net.corda.traderdemo.flow.CommercialPaperIssueFlow', - 'StartFlow.net.corda.traderdemo.flow.SellerFlow' - ]]] + ext.rpcUsers = [['username': "demo", 'password': "demo", 'permissions': ["ALL"]]] directory "./build/nodes" // This name "Notary" is hard-coded into TraderDemoClientApi so if you change it here, change it there too. 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 8e7e639967..df4c1ec6db 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 @@ -6,7 +6,8 @@ import net.corda.core.utilities.millis import net.corda.finance.DOLLARS import net.corda.finance.flows.CashIssueFlow import net.corda.finance.flows.CashPaymentFlow -import net.corda.node.services.FlowPermissions +import net.corda.node.services.Permissions.Companion.all +import net.corda.node.services.Permissions.Companion.startFlow import net.corda.nodeapi.User import net.corda.testing.* import net.corda.testing.driver.NodeHandle @@ -22,11 +23,12 @@ import java.util.concurrent.Executors class TraderDemoTest { @Test fun `runs trader demo`() { - val demoUser = User("demo", "demo", setOf(FlowPermissions.startFlowPermission())) + val demoUser = User("demo", "demo", setOf(startFlow(), all())) val bankUser = User("user1", "test", permissions = setOf( - FlowPermissions.startFlowPermission(), - FlowPermissions.startFlowPermission(), - FlowPermissions.startFlowPermission())) + startFlow(), + startFlow(), + startFlow(), + all())) driver(startNodesInProcess = true, extraCordappPackagesToScan = listOf("net.corda.finance")) { val (nodeA, nodeB, bankNode) = listOf( startNode(providedName = DUMMY_BANK_A.name, rpcUsers = listOf(demoUser)), diff --git a/samples/trader-demo/src/test/kotlin/net/corda/traderdemo/Main.kt b/samples/trader-demo/src/test/kotlin/net/corda/traderdemo/Main.kt index 4a032576f5..a349d4c210 100644 --- a/samples/trader-demo/src/test/kotlin/net/corda/traderdemo/Main.kt +++ b/samples/trader-demo/src/test/kotlin/net/corda/traderdemo/Main.kt @@ -2,7 +2,8 @@ package net.corda.traderdemo import net.corda.core.internal.div import net.corda.finance.flows.CashIssueFlow -import net.corda.node.services.FlowPermissions.Companion.startFlowPermission +import net.corda.node.services.Permissions.Companion.all +import net.corda.node.services.Permissions.Companion.startFlow import net.corda.nodeapi.User import net.corda.testing.BOC import net.corda.testing.DUMMY_BANK_A @@ -18,13 +19,14 @@ import net.corda.traderdemo.flow.SellerFlow */ fun main(args: Array) { val permissions = setOf( - startFlowPermission(), - startFlowPermission()) + startFlow(), + startFlow(), + all()) val demoUser = listOf(User("demo", "demo", permissions)) driver(driverDirectory = "build" / "trader-demo-nodes", isDebug = true) { - val user = User("user1", "test", permissions = setOf(startFlowPermission(), - startFlowPermission(), - startFlowPermission())) + val user = User("user1", "test", permissions = setOf(startFlow(), + startFlow(), + startFlow())) startNotaryNode(DUMMY_NOTARY.name, validating = false) startNode(providedName = DUMMY_BANK_A.name, rpcUsers = demoUser) startNode(providedName = DUMMY_BANK_B.name, rpcUsers = demoUser) diff --git a/testing/node-driver/src/integration-test/kotlin/net/corda/testing/FlowStackSnapshotTest.kt b/testing/node-driver/src/integration-test/kotlin/net/corda/testing/FlowStackSnapshotTest.kt index 8adba7f34d..68537ea0ab 100644 --- a/testing/node-driver/src/integration-test/kotlin/net/corda/testing/FlowStackSnapshotTest.kt +++ b/testing/node-driver/src/integration-test/kotlin/net/corda/testing/FlowStackSnapshotTest.kt @@ -8,7 +8,7 @@ import net.corda.core.internal.list import net.corda.core.internal.read import net.corda.core.messaging.startFlow import net.corda.core.serialization.CordaSerializable -import net.corda.node.services.FlowPermissions.Companion.startFlowPermission +import net.corda.node.services.Permissions.Companion.startFlow import net.corda.nodeapi.User import net.corda.testing.driver.driver import net.corda.testing.node.MockNetwork @@ -229,7 +229,7 @@ class FlowStackSnapshotTest { @Test 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(startFlowPermission())))).get() + val a = startNode(rpcUsers = listOf(User(Constants.USER, Constants.PASSWORD, setOf(startFlow())))).get() a.rpcClientToNode().use(Constants.USER, Constants.PASSWORD) { connection -> val stackSnapshotFrames = connection.proxy.startFlow(::SideEffectFlow).returnValue.get() val iterator = stackSnapshotFrames.listIterator() @@ -244,7 +244,7 @@ class FlowStackSnapshotTest { @Test 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(startFlowPermission())))).get() + val a = startNode(rpcUsers = listOf(User(Constants.USER, Constants.PASSWORD, setOf(startFlow())))).get() a.rpcClientToNode().use(Constants.USER, Constants.PASSWORD) { connection -> val stackSnapshotFrames = connection.proxy.startFlow(::NoSideEffectFlow).returnValue.get() val iterator = stackSnapshotFrames.listIterator() @@ -259,7 +259,7 @@ class FlowStackSnapshotTest { @Test 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(startFlowPermission())))).get() + val a = startNode(rpcUsers = listOf(User(Constants.USER, Constants.PASSWORD, setOf(startFlow())))).get() a.rpcClientToNode().use(Constants.USER, Constants.PASSWORD) { connection -> val flowId = connection.proxy.startFlow(::PersistingNoSideEffectFlow).returnValue.get() @@ -276,7 +276,7 @@ class FlowStackSnapshotTest { @Test fun `persistFlowStackSnapshot persists multiple snapshots in different files`() { driver(startNodesInProcess = true) { - val a = startNode(rpcUsers = listOf(User(Constants.USER, Constants.PASSWORD, setOf(startFlowPermission())))).get() + val a = startNode(rpcUsers = listOf(User(Constants.USER, Constants.PASSWORD, setOf(startFlow())))).get() a.rpcClientToNode().use(Constants.USER, Constants.PASSWORD) { connection -> val numberOfFlowSnapshots = 5 @@ -306,7 +306,7 @@ class FlowStackSnapshotTest { @Test fun `persistFlowStackSnapshot stack traces are aligned with stack objects`() { driver(startNodesInProcess = true) { - val a = startNode(rpcUsers = listOf(User(Constants.USER, Constants.PASSWORD, setOf(startFlowPermission())))).get() + val a = startNode(rpcUsers = listOf(User(Constants.USER, Constants.PASSWORD, setOf(startFlow())))).get() a.rpcClientToNode().use(Constants.USER, Constants.PASSWORD) { connection -> val flowId = connection.proxy.startFlow(::PersistingSideEffectFlow).returnValue.get() 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 6cdad1c883..c552f30f07 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 @@ -26,6 +26,7 @@ import net.corda.node.internal.Node import net.corda.node.internal.NodeStartup import net.corda.node.internal.StartedNode import net.corda.node.internal.cordapp.CordappLoader +import net.corda.node.services.Permissions.Companion.invokeRpc import net.corda.node.services.config.* import net.corda.node.services.network.NetworkMapService import net.corda.node.utilities.ServiceIdentityGenerator @@ -76,6 +77,12 @@ private val DEFAULT_POLL_INTERVAL = 500.millis private const val DEFAULT_WARN_COUNT = 120 +private val DRIVER_REQUIRED_PERMISSIONS = setOf( + invokeRpc(CordaRPCOps::nodeInfo), + invokeRpc(CordaRPCOps::networkMapFeed), + invokeRpc(CordaRPCOps::networkMapSnapshot) +) + /** * This is the interface that's exposed to DSL users. */ @@ -721,6 +728,7 @@ class DriverDSL( val webAddress = portAllocation.nextHostAndPort() // TODO: Derive name from the full picked name, don't just wrap the common name val name = providedName ?: CordaX500Name(organisation = "${oneOf(names).organisation}-${p2pAddress.port}", locality = "London", country = "GB") + val users = rpcUsers.map { it.copy(permissions = it.permissions + DRIVER_REQUIRED_PERMISSIONS) } val config = ConfigHelper.loadConfig( baseDirectory = baseDirectory(name), allowMissingConfig = true, @@ -730,7 +738,7 @@ class DriverDSL( "rpcAddress" to rpcAddress.toString(), "webAddress" to webAddress.toString(), "useTestClock" to useTestClock, - "rpcUsers" to if (rpcUsers.isEmpty()) defaultRpcUserList else rpcUsers.map { it.toConfig().root().unwrapped() }, + "rpcUsers" to if (users.isEmpty()) defaultRpcUserList else users.map { it.toConfig().root().unwrapped() }, "verifierType" to verifierType.name ) + customOverrides ) 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 d22e628596..d1913e2c01 100644 --- a/tools/explorer/src/main/kotlin/net/corda/explorer/ExplorerSimulation.kt +++ b/tools/explorer/src/main/kotlin/net/corda/explorer/ExplorerSimulation.kt @@ -20,7 +20,7 @@ import net.corda.finance.contracts.asset.Cash import net.corda.finance.flows.* import net.corda.finance.flows.CashExitFlow.ExitRequest import net.corda.finance.flows.CashIssueAndPaymentFlow.IssueAndPaymentRequest -import net.corda.node.services.FlowPermissions.Companion.startFlowPermission +import net.corda.node.services.Permissions.Companion.startFlow import net.corda.nodeapi.User import net.corda.testing.ALICE import net.corda.testing.BOB @@ -33,14 +33,14 @@ import java.util.* class ExplorerSimulation(val options: OptionSet) { private val user = User("user1", "test", permissions = setOf( - startFlowPermission(), - startFlowPermission() + startFlow(), + startFlow() )) private val manager = User("manager", "test", permissions = setOf( - startFlowPermission(), - startFlowPermission(), - startFlowPermission(), - startFlowPermission()) + startFlow(), + startFlow(), + startFlow(), + startFlow()) ) private lateinit var notaryNode: NodeHandle diff --git a/tools/loadtest/src/main/resources/loadtest-reference.conf b/tools/loadtest/src/main/resources/loadtest-reference.conf index 8b03044650..67ea98c2ae 100644 --- a/tools/loadtest/src/main/resources/loadtest-reference.conf +++ b/tools/loadtest/src/main/resources/loadtest-reference.conf @@ -8,4 +8,4 @@ localTunnelStartingPort = 10000 remoteNodeDirectory = "/opt/corda" rpcPort = 10003 remoteSystemdServiceName = "corda" -rpcUser = {username = corda, password = not_blockchain, permissions = []} +rpcUser = {username = corda, password = not_blockchain, permissions = ["ALL"]} From 7521675ef2be6dc784ffbd665a25dc905eecc115 Mon Sep 17 00:00:00 2001 From: Andrzej Cichocki Date: Thu, 2 Nov 2017 16:55:36 +0000 Subject: [PATCH 13/24] It's not commonName it's organisation. (#1987) --- .../demobench/model/NodeControllerTest.kt | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/tools/demobench/src/test/kotlin/net/corda/demobench/model/NodeControllerTest.kt b/tools/demobench/src/test/kotlin/net/corda/demobench/model/NodeControllerTest.kt index 6ed56f94e0..21899794d8 100644 --- a/tools/demobench/src/test/kotlin/net/corda/demobench/model/NodeControllerTest.kt +++ b/tools/demobench/src/test/kotlin/net/corda/demobench/model/NodeControllerTest.kt @@ -26,7 +26,7 @@ class NodeControllerTest { @Test fun `register notary`() { assertFalse(controller.hasNotary()) - val config = createConfig(commonName = "Name", notary = NotaryService(false)) + val config = createConfig(organisation = "Name", notary = NotaryService(false)) controller.register(config) assertTrue(controller.hasNotary()) } @@ -34,7 +34,7 @@ class NodeControllerTest { @Test fun `register non notary`() { assertFalse(controller.hasNotary()) - val config = createConfig(commonName = "Name") + val config = createConfig(organisation = "Name") controller.register(config) assertFalse(controller.hasNotary()) } @@ -65,15 +65,14 @@ class NodeControllerTest { @Test fun `test register unique nodes`() { - val config = createConfig(commonName = organisation2Name) + val config = createConfig(organisation = organisation2Name) assertTrue(controller.register(config)) assertFalse(controller.register(config)) } @Test fun `test unique key after register`() { - val config = createConfig(commonName = organisation2Name) - + val config = createConfig(organisation = organisation2Name) assertFalse(controller.keyExists("organisation2")) controller.register(config) assertTrue(controller.keyExists("organisation2")) @@ -81,8 +80,7 @@ class NodeControllerTest { @Test fun `test matching name after register`() { - val config = createConfig(commonName = organisation2Name) - + val config = createConfig(organisation = organisation2Name) assertFalse(controller.nameExists("Organisation 2")) assertFalse(controller.nameExists("Organisation2")) assertFalse(controller.nameExists("organisation 2")) @@ -138,7 +136,7 @@ class NodeControllerTest { @Test fun `dispose node`() { - val config = createConfig(commonName = "MyName") + val config = createConfig(organisation = "MyName") controller.register(config) assertEquals(NodeState.STARTING, config.state) @@ -149,7 +147,7 @@ class NodeControllerTest { } private fun createConfig( - commonName: String = "Unknown", + organisation: String = "Unknown", p2pPort: Int = 0, rpcPort: Int = 0, webPort: Int = 0, @@ -159,7 +157,7 @@ class NodeControllerTest { ): NodeConfigWrapper { val nodeConfig = NodeConfig( myLegalName = CordaX500Name( - organisation = commonName, + organisation = organisation, locality = "New York", country = "US" ), From 2e0e78e883db219e1d0dfe0fe88268eba0890ed4 Mon Sep 17 00:00:00 2001 From: Andrzej Cichocki Date: Thu, 2 Nov 2017 16:56:08 +0000 Subject: [PATCH 14/24] Retire DriverBasedTest. (#1986) --- .../corda/client/jfx/NodeMonitorModelTest.kt | 12 ++--- .../node/services/DistributedServiceTests.kt | 10 ++-- .../net/corda/testing/node/DriverBasedTest.kt | 46 ------------------- 3 files changed, 9 insertions(+), 59 deletions(-) delete mode 100644 testing/node-driver/src/main/kotlin/net/corda/testing/node/DriverBasedTest.kt 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 2b75244715..e703b5fa7c 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 @@ -31,11 +31,10 @@ import net.corda.node.services.Permissions.Companion.startFlow import net.corda.nodeapi.User import net.corda.testing.* import net.corda.testing.driver.driver -import net.corda.testing.node.DriverBasedTest import org.junit.Test import rx.Observable -class NodeMonitorModelTest : DriverBasedTest() { +class NodeMonitorModelTest { lateinit var aliceNode: NodeInfo lateinit var bobNode: NodeInfo lateinit var notaryParty: Party @@ -50,8 +49,7 @@ class NodeMonitorModelTest : DriverBasedTest() { lateinit var vaultUpdates: Observable> lateinit var networkMapUpdates: Observable lateinit var newNode: (CordaX500Name) -> NodeInfo - - override fun setup() = driver(extraCordappPackagesToScan = listOf("net.corda.finance")) { + private fun setup(runTest: () -> Unit) = driver(extraCordappPackagesToScan = listOf("net.corda.finance")) { val cashUser = User("user1", "test", permissions = setOf( startFlow(), startFlow(), @@ -91,7 +89,7 @@ class NodeMonitorModelTest : DriverBasedTest() { } @Test - fun `network map update`() { + fun `network map update`() = setup { val charlieNode = newNode(CHARLIE.name) val nonServiceIdentities = aliceNode.legalIdentitiesAndCerts + bobNode.legalIdentitiesAndCerts + charlieNode.legalIdentitiesAndCerts networkMapUpdates.filter { it.node.legalIdentitiesAndCerts.any { it in nonServiceIdentities } } @@ -112,7 +110,7 @@ class NodeMonitorModelTest : DriverBasedTest() { } @Test - fun `cash issue works end to end`() { + fun `cash issue works end to end`() = setup { rpc.startFlow(::CashIssueFlow, Amount(100, USD), OpaqueBytes(ByteArray(1, { 1 })), @@ -136,7 +134,7 @@ class NodeMonitorModelTest : DriverBasedTest() { } @Test - fun `cash issue and move`() { + fun `cash issue and move`() = setup { val (_, issueIdentity) = rpc.startFlow(::CashIssueFlow, 100.DOLLARS, OpaqueBytes.of(1), notaryParty).returnValue.getOrThrow() rpc.startFlow(::CashPaymentFlow, 100.DOLLARS, bobNode.chooseIdentity()).returnValue.getOrThrow() 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 6952390506..3f5fd63e45 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 @@ -18,20 +18,18 @@ import net.corda.nodeapi.User import net.corda.testing.* import net.corda.testing.driver.NodeHandle import net.corda.testing.driver.driver -import net.corda.testing.node.DriverBasedTest import org.junit.Test import rx.Observable import java.util.* import kotlin.test.assertEquals -class DistributedServiceTests : DriverBasedTest() { +class DistributedServiceTests { lateinit var alice: NodeHandle lateinit var notaries: List lateinit var aliceProxy: CordaRPCOps lateinit var raftNotaryIdentity: Party lateinit var notaryStateMachines: Observable> - - override fun setup() = driver(extraCordappPackagesToScan = listOf("net.corda.finance.contracts")) { + private fun setup(runTest: () -> Unit) = driver(extraCordappPackagesToScan = listOf("net.corda.finance.contracts")) { // Start Alice and 3 notaries in a RAFT cluster val clusterSize = 3 val testUser = User("test", "test", permissions = setOf( @@ -72,7 +70,7 @@ class DistributedServiceTests : DriverBasedTest() { // TODO Use a dummy distributed service rather than a Raft Notary Service as this test is only about Artemis' ability // to handle distributed services @Test - fun `requests are distributed evenly amongst the nodes`() { + fun `requests are distributed evenly amongst the nodes`() = setup { // Issue 100 pounds, then pay ourselves 50x2 pounds issueCash(100.POUNDS) @@ -100,7 +98,7 @@ class DistributedServiceTests : DriverBasedTest() { // TODO This should be in RaftNotaryServiceTests @Test - fun `cluster survives if a notary is killed`() { + fun `cluster survives if a notary is killed`() = setup { // Issue 100 pounds, then pay ourselves 10x5 pounds issueCash(100.POUNDS) diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/DriverBasedTest.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/DriverBasedTest.kt deleted file mode 100644 index d9b4807845..0000000000 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/DriverBasedTest.kt +++ /dev/null @@ -1,46 +0,0 @@ -package net.corda.testing.node - -import com.google.common.util.concurrent.SettableFuture -import net.corda.core.utilities.getOrThrow -import net.corda.testing.driver.DriverDSLExposedInterface -import org.junit.After -import org.junit.Before -import java.util.concurrent.CountDownLatch -import kotlin.concurrent.thread - -abstract class DriverBasedTest { - private val stopDriver = CountDownLatch(1) - private var driverThread: Thread? = null - private lateinit var driverStarted: SettableFuture - - protected sealed class RunTestToken { - internal object Token : RunTestToken() - } - - protected abstract fun setup(): RunTestToken - - protected fun DriverDSLExposedInterface.runTest(): RunTestToken { - driverStarted.set(Unit) - stopDriver.await() - return RunTestToken.Token - } - - @Before - fun start() { - driverStarted = SettableFuture.create() - driverThread = thread { - try { - setup() - } catch (t: Throwable) { - driverStarted.setException(t) - } - } - driverStarted.getOrThrow() - } - - @After - fun stop() { - stopDriver.countDown() - driverThread?.join() - } -} \ No newline at end of file From 837e8800e80469bebd0a7d2adf3faaaee77f4c2a Mon Sep 17 00:00:00 2001 From: Shams Asari Date: Wed, 1 Nov 2017 15:35:56 +0000 Subject: [PATCH 15/24] Checking that the smoke tests don't have the node on their classpath. --- .idea/compiler.xml | 4 ++ core/build.gradle | 68 ++++++++++++++++++- .../corda/core/cordapp}/CordappSmokeTest.kt | 9 +-- node/build.gradle | 40 ----------- node/src/main/kotlin/net/corda/node/Corda.kt | 1 + .../net/corda/smoketesting/NodeProcess.kt | 29 +++++--- 6 files changed, 96 insertions(+), 55 deletions(-) rename {node/src/smoke-test/kotlin/net/corda/node => core/src/smoke-test/kotlin/net/corda/core/cordapp}/CordappSmokeTest.kt (94%) diff --git a/.idea/compiler.xml b/.idea/compiler.xml index 44a28247dd..d45af787ba 100644 --- a/.idea/compiler.xml +++ b/.idea/compiler.xml @@ -2,6 +2,8 @@ + + @@ -27,7 +29,9 @@ + + diff --git a/core/build.gradle b/core/build.gradle index d821ec7d37..34d77bf8fc 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -7,6 +7,52 @@ apply plugin: 'com.jfrog.artifactory' description 'Corda core' +evaluationDependsOn(':node:capsule') + +configurations { + integrationTestCompile.extendsFrom testCompile + integrationTestRuntime.extendsFrom testRuntime + + smokeTestCompile.extendsFrom compile + smokeTestRuntime.extendsFrom runtime +} + +sourceSets { + integrationTest { + kotlin { + compileClasspath += main.output + test.output + runtimeClasspath += main.output + test.output + srcDir file('src/integration-test/kotlin') + } + java { + compileClasspath += main.output + test.output + runtimeClasspath += main.output + test.output + srcDir file('src/integration-test/java') + } + } + smokeTest { + kotlin { + // We must NOT have any Node code on the classpath, so do NOT + // include the test or integrationTest dependencies here. + compileClasspath += main.output + runtimeClasspath += main.output + srcDir file('src/smoke-test/kotlin') + } + java { + compileClasspath += main.output + runtimeClasspath += main.output + srcDir file('src/smoke-test/java') + } + } +} + +processSmokeTestResources { + // Bring in the fully built corda.jar for use by NodeFactory in the smoke tests + from(project(':node:capsule').tasks['buildCordaJAR']) { + rename 'corda-(.*)', 'corda.jar' + } +} + buildscript { repositories { mavenCentral() @@ -14,7 +60,6 @@ buildscript { } dependencies { - testCompile "junit:junit:$junit_version" testCompile "commons-fileupload:commons-fileupload:$fileupload_version" @@ -48,6 +93,11 @@ dependencies { // Guava: Google utilities library. testCompile "com.google.guava:guava:$guava_version" + // Smoke tests do NOT have any Node code on the classpath! + smokeTestCompile project(':smoke-test-utils') + smokeTestCompile "org.assertj:assertj-core:${assertj_version}" + smokeTestCompile "junit:junit:$junit_version" + // RxJava: observable streams of events. compile "io.reactivex:rxjava:$rxjava_version" @@ -87,6 +137,22 @@ task testJar(type: Jar) { from sourceSets.test.output } +task integrationTest(type: Test) { + testClassesDirs = sourceSets.integrationTest.output.classesDirs + classpath = sourceSets.integrationTest.runtimeClasspath +} + +task smokeTestJar(type: Jar) { + classifier 'smokeTests' + from sourceSets.smokeTest.output +} + +task smokeTest(type: Test) { + dependsOn smokeTestJar + testClassesDirs = sourceSets.smokeTest.output.classesDirs + classpath = sourceSets.smokeTest.runtimeClasspath +} + artifacts { testArtifacts testJar } diff --git a/node/src/smoke-test/kotlin/net/corda/node/CordappSmokeTest.kt b/core/src/smoke-test/kotlin/net/corda/core/cordapp/CordappSmokeTest.kt similarity index 94% rename from node/src/smoke-test/kotlin/net/corda/node/CordappSmokeTest.kt rename to core/src/smoke-test/kotlin/net/corda/core/cordapp/CordappSmokeTest.kt index 82c48bf82e..2f1991af12 100644 --- a/node/src/smoke-test/kotlin/net/corda/node/CordappSmokeTest.kt +++ b/core/src/smoke-test/kotlin/net/corda/core/cordapp/CordappSmokeTest.kt @@ -1,4 +1,4 @@ -package net.corda.node +package net.corda.core.cordapp import co.paralleluniverse.fibers.Suspendable import net.corda.core.flows.* @@ -11,7 +11,6 @@ import net.corda.core.internal.list import net.corda.core.messaging.startFlow import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.unwrap -import net.corda.node.internal.cordapp.CordappLoader import net.corda.nodeapi.User import net.corda.smoketesting.NodeConfig import net.corda.smoketesting.NodeProcess @@ -23,6 +22,7 @@ import kotlin.streams.toList class CordappSmokeTest { private companion object { + private const val CORDAPPS_DIR_NAME = "cordapps" val user = User("user1", "test", permissions = setOf("ALL")) val port = AtomicInteger(15100) } @@ -38,9 +38,10 @@ class CordappSmokeTest { users = listOf(user) ) + @Test fun `FlowContent appName returns the filename of the CorDapp jar`() { - val cordappsDir = (factory.baseDirectory(aliceConfig) / CordappLoader.CORDAPPS_DIR_NAME).createDirectories() + val cordappsDir = (factory.baseDirectory(aliceConfig) / CORDAPPS_DIR_NAME).createDirectories() // Find the jar file for the smoke tests of this module val selfCordapp = Paths.get("build", "libs").list { it.filter { "-smokeTests" in it.toString() }.toList().single() @@ -61,7 +62,7 @@ class CordappSmokeTest { @Test fun `empty cordapps directory`() { - (factory.baseDirectory(aliceConfig) / CordappLoader.CORDAPPS_DIR_NAME).createDirectories() + (factory.baseDirectory(aliceConfig) / CORDAPPS_DIR_NAME).createDirectories() factory.create(aliceConfig).close() } diff --git a/node/build.gradle b/node/build.gradle index 9bc48d5ca8..e1896cfd28 100644 --- a/node/build.gradle +++ b/node/build.gradle @@ -19,9 +19,6 @@ configurations { integrationTestCompile.extendsFrom testCompile integrationTestRuntime.extendsFrom testRuntime - - smokeTestCompile.extendsFrom compile - smokeTestRuntime.extendsFrom runtime } sourceSets { @@ -40,20 +37,6 @@ sourceSets { srcDir file('src/integration-test/resources') } } - smokeTest { - kotlin { - // We must NOT have any Node code on the classpath, so do NOT - // include the test or integrationTest dependencies here. - compileClasspath += main.output - runtimeClasspath += main.output - srcDir file('src/smoke-test/kotlin') - } - java { - compileClasspath += main.output - runtimeClasspath += main.output - srcDir file('src/smoke-test/java') - } - } } // Use manual resource copying of log4j2.xml rather than source sets. @@ -62,13 +45,6 @@ processResources { from file("$rootDir/config/dev/log4j2.xml") } -processSmokeTestResources { - // Bring in the fully built corda.jar for use by NodeFactory in the smoke tests - from(project(':node:capsule').tasks.buildCordaJAR) { - rename 'corda-(.*)', 'corda.jar' - } -} - // To find potential version conflicts, run "gradle htmlDependencyReport" and then look in // build/reports/project/dependencies/index.html for green highlighted parts of the tree. @@ -181,11 +157,6 @@ dependencies { integrationTestCompile "junit:junit:$junit_version" integrationTestCompile "org.assertj:assertj-core:${assertj_version}" - // Smoke tests do NOT have any Node code on the classpath! - smokeTestCompile project(':smoke-test-utils') - smokeTestCompile "org.assertj:assertj-core:${assertj_version}" - smokeTestCompile "junit:junit:$junit_version" - // Jetty dependencies for NetworkMapClient test. // Web stuff: for HTTP[S] servlets testCompile "org.eclipse.jetty:jetty-servlet:${jetty_version}" @@ -203,17 +174,6 @@ task integrationTest(type: Test) { classpath = sourceSets.integrationTest.runtimeClasspath } -task smokeTestJar(type: Jar) { - classifier 'smokeTests' - from sourceSets.smokeTest.output -} - -task smokeTest(type: Test) { - dependsOn smokeTestJar - testClassesDirs = sourceSets.smokeTest.output.classesDirs - classpath = sourceSets.smokeTest.runtimeClasspath -} - jar { baseName 'corda-node' } diff --git a/node/src/main/kotlin/net/corda/node/Corda.kt b/node/src/main/kotlin/net/corda/node/Corda.kt index 2ac14bb0f6..8b0f638040 100644 --- a/node/src/main/kotlin/net/corda/node/Corda.kt +++ b/node/src/main/kotlin/net/corda/node/Corda.kt @@ -1,3 +1,4 @@ +// This class is used by the smoke tests as a check that the node module isn't on their classpath @file:JvmName("Corda") package net.corda.node diff --git a/testing/smoke-test-utils/src/main/kotlin/net/corda/smoketesting/NodeProcess.kt b/testing/smoke-test-utils/src/main/kotlin/net/corda/smoketesting/NodeProcess.kt index 28d1afb9bf..4eb02c0d88 100644 --- a/testing/smoke-test-utils/src/main/kotlin/net/corda/smoketesting/NodeProcess.kt +++ b/testing/smoke-test-utils/src/main/kotlin/net/corda/smoketesting/NodeProcess.kt @@ -15,15 +15,13 @@ import java.util.concurrent.Executors import java.util.concurrent.TimeUnit.SECONDS class NodeProcess( - val config: NodeConfig, - val nodeDir: Path, + private val config: NodeConfig, + private val nodeDir: Path, private val node: Process, private val client: CordaRPCClient ) : AutoCloseable { private companion object { val log = loggerFor() - val javaPath: Path = Paths.get(System.getProperty("java.home"), "bin", "java") - val formatter: DateTimeFormatter = DateTimeFormatter.ofPattern("yyyyMMddHHmmss").withZone(systemDefault()) } fun connect(): CordaRPCConnection { @@ -43,14 +41,25 @@ class NodeProcess( (nodeDir / "artemis").toFile().deleteRecursively() } - class Factory(val buildDirectory: Path = Paths.get("build"), - val cordaJar: Path = Paths.get(this::class.java.getResource("/corda.jar").toURI())) { - val nodesDirectory = buildDirectory / formatter.format(Instant.now()) - - init { - nodesDirectory.createDirectories() + class Factory( + private val buildDirectory: Path = Paths.get("build"), + private val cordaJar: Path = Paths.get(this::class.java.getResource("/corda.jar").toURI()) + ) { + companion object { + val javaPath: Path = Paths.get(System.getProperty("java.home"), "bin", "java") + val formatter: DateTimeFormatter = DateTimeFormatter.ofPattern("yyyyMMddHHmmss").withZone(systemDefault()) + init { + try { + Class.forName("net.corda.node.Corda") + throw Error("Smoke test has the node in its classpath. Please remove the offending dependency.") + } catch (e: ClassNotFoundException) { + // If the class can't be found then we're good! + } + } } + private val nodesDirectory = (buildDirectory / formatter.format(Instant.now())).createDirectories() + fun baseDirectory(config: NodeConfig): Path = nodesDirectory / config.commonName fun create(config: NodeConfig): NodeProcess { From 549046575063b9f7ba2bcff226e0c7c1fc1630b3 Mon Sep 17 00:00:00 2001 From: Alberto Arri <30873160+al-r3@users.noreply.github.com> Date: Fri, 3 Nov 2017 09:28:52 +0000 Subject: [PATCH 16/24] [CORDA-446] Remove entities from netmap cache & friends (#1980) * Remove PersistentNetworkMapService and related classes. They were part of the old network map node code. --- .../net/corda/node/internal/AbstractNode.kt | 2 - .../kotlin/net/corda/node/internal/Node.kt | 11 -- .../services/network/NetworkMapService.kt | 68 ---------- .../network/PersistentNetworkMapService.kt | 124 ------------------ .../node/services/schema/NodeSchemaService.kt | 9 +- .../DistributedImmutableMapTests.kt | 7 +- .../net/corda/netmap/NetworkMapVisualiser.kt | 1 - .../kotlin/net/corda/testing/driver/Driver.kt | 3 +- .../kotlin/net/corda/testing/node/MockNode.kt | 7 - 9 files changed, 5 insertions(+), 227 deletions(-) delete mode 100644 node/src/main/kotlin/net/corda/node/services/network/NetworkMapService.kt delete mode 100644 node/src/main/kotlin/net/corda/node/services/network/PersistentNetworkMapService.kt 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 23b08e1542..b7ff38dc6f 100644 --- a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt +++ b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt @@ -563,8 +563,6 @@ abstract class AbstractNode(config: NodeConfiguration, return PersistentKeyManagementService(identityService, partyKeys) } - abstract protected fun makeNetworkMapService(network: MessagingService, networkMapCache: NetworkMapCacheInternal): NetworkMapService - private fun makeCoreNotaryService(notaryConfig: NotaryConfig): NotaryService { val notaryKey = myNotaryIdentity?.owningKey ?: throw IllegalArgumentException("No notary identity initialized when creating a notary service") return if (notaryConfig.validating) { diff --git a/node/src/main/kotlin/net/corda/node/internal/Node.kt b/node/src/main/kotlin/net/corda/node/internal/Node.kt index 8a4e18c290..4bee6df1f4 100644 --- a/node/src/main/kotlin/net/corda/node/internal/Node.kt +++ b/node/src/main/kotlin/net/corda/node/internal/Node.kt @@ -19,14 +19,11 @@ import net.corda.node.serialization.KryoServerSerializationScheme import net.corda.node.serialization.NodeClock import net.corda.node.services.RPCUserService import net.corda.node.services.RPCUserServiceImpl -import net.corda.node.services.api.NetworkMapCacheInternal import net.corda.node.services.api.SchemaService import net.corda.node.services.config.NodeConfiguration import net.corda.node.services.messaging.ArtemisMessagingServer import net.corda.node.services.messaging.MessagingService import net.corda.node.services.messaging.NodeMessagingClient -import net.corda.node.services.network.NetworkMapService -import net.corda.node.services.network.PersistentNetworkMapService import net.corda.node.utilities.AddressUtils import net.corda.node.utilities.AffinityExecutor import net.corda.node.utilities.TestClock @@ -35,10 +32,6 @@ import net.corda.nodeapi.internal.ShutdownHook import net.corda.nodeapi.internal.addShutdownHook import net.corda.nodeapi.internal.serialization.* import net.corda.nodeapi.internal.serialization.amqp.AMQPServerSerializationScheme -import org.apache.activemq.artemis.api.core.ActiveMQNotConnectedException -import org.apache.activemq.artemis.api.core.RoutingType -import org.apache.activemq.artemis.api.core.client.ActiveMQClient -import org.apache.activemq.artemis.api.core.client.ClientMessage import org.slf4j.Logger import org.slf4j.LoggerFactory import java.time.Clock @@ -208,10 +201,6 @@ open class Node(override val configuration: NodeConfiguration, return listOf(address.hostAndPort) } - override fun makeNetworkMapService(network: MessagingService, networkMapCache: NetworkMapCacheInternal): NetworkMapService { - return PersistentNetworkMapService() - } - /** * If the node is persisting to an embedded H2 database, then expose this via TCP with a JDBC URL of the form: * jdbc:h2:tcp://:/node diff --git a/node/src/main/kotlin/net/corda/node/services/network/NetworkMapService.kt b/node/src/main/kotlin/net/corda/node/services/network/NetworkMapService.kt deleted file mode 100644 index 0c6499ec96..0000000000 --- a/node/src/main/kotlin/net/corda/node/services/network/NetworkMapService.kt +++ /dev/null @@ -1,68 +0,0 @@ -package net.corda.node.services.network - -import net.corda.core.identity.PartyAndCertificate -import net.corda.core.internal.ThreadBox -import net.corda.core.messaging.SingleMessageRecipient -import net.corda.core.node.NodeInfo -import net.corda.core.node.services.NetworkMapCache -import net.corda.core.serialization.CordaSerializable -import net.corda.node.utilities.AddOrRemove -import java.time.Instant -import java.util.concurrent.ConcurrentHashMap -import javax.annotation.concurrent.ThreadSafe - -/** - * A network map contains lists of nodes on the network along with information about their identity keys, services - * they provide and host names or IP addresses where they can be connected to. This information is cached locally within - * nodes, by the [NetworkMapCache]. Currently very basic consensus controls are applied, using signed changes which - * replace each other based on a serial number present in the change. - */ -// TODO: A better architecture for the network map service might be one like the Tor directory authorities, where -// several nodes linked by RAFT or Paxos elect a leader and that leader distributes signed documents describing the -// network layout. Those documents can then be cached by every node and thus a network map can/ be retrieved given only -// a single successful peer connection. -// -// It may also be that this is replaced or merged with the identity management service; for example if the network has -// a concept of identity changes over time, should that include the node for an identity? If so, that is likely to -// replace this service. -@ThreadSafe -interface NetworkMapService { - - val nodeRegistrations: Map - - // Map from subscriber address, to most recently acknowledged update map version. - val subscribers: ThreadBox> -} - - -@ThreadSafe -class InMemoryNetworkMapService: NetworkMapService { - - override val nodeRegistrations: MutableMap = ConcurrentHashMap() - override val subscribers = ThreadBox(mutableMapOf()) -} - - -/** - * A node registration state in the network map. - * - * @param node the node being added/removed. - * @param serial an increasing value which represents the version of this registration. Not expected to be sequential, - * but later versions of the registration must have higher values (or they will be ignored by the map service). - * Similar to the serial number on DNS records. - * @param type add if the node is being added to the map, or remove if a previous node is being removed (indicated as - * going offline). - * @param expires when the registration expires. Only used when adding a node to a map. - */ -// TODO: This might alternatively want to have a node and party, with the node being optional, so registering a node -// involves providing both node and paerty, and deregistering a node involves a request with party but no node. -@CordaSerializable -data class NodeRegistration(val node: NodeInfo, val serial: Long, val type: AddOrRemove, var expires: Instant) { - override fun toString(): String = "$node #$serial ($type)" -} - -@CordaSerializable -data class LastAcknowledgeInfo(val mapVersion: Int) - -@CordaSerializable -data class NodeRegistrationInfo(val reg: NodeRegistration, val mapVersion: Int) diff --git a/node/src/main/kotlin/net/corda/node/services/network/PersistentNetworkMapService.kt b/node/src/main/kotlin/net/corda/node/services/network/PersistentNetworkMapService.kt deleted file mode 100644 index 02c04ac968..0000000000 --- a/node/src/main/kotlin/net/corda/node/services/network/PersistentNetworkMapService.kt +++ /dev/null @@ -1,124 +0,0 @@ -package net.corda.node.services.network - -import net.corda.core.crypto.toStringShort -import net.corda.core.identity.PartyAndCertificate -import net.corda.core.internal.ThreadBox -import net.corda.core.messaging.SingleMessageRecipient -import net.corda.core.serialization.SerializationDefaults -import net.corda.core.serialization.deserialize -import net.corda.core.serialization.serialize -import net.corda.core.utilities.MAX_HASH_HEX_SIZE -import net.corda.node.utilities.NODE_DATABASE_PREFIX -import net.corda.node.utilities.PersistentMap -import net.corda.nodeapi.ArtemisMessagingComponent -import java.io.ByteArrayInputStream -import java.security.cert.CertificateFactory -import java.util.* -import javax.persistence.* - -/** - * A network map service backed by a database to survive restarts of the node hosting it. - * - * Majority of the logic is inherited from [NetworkMapService]. - * - * This class needs database transactions to be in-flight during method calls and init, otherwise it will throw - * exceptions. - */ -class PersistentNetworkMapService: NetworkMapService { - - // Only the node_party_path column is needed to reconstruct a PartyAndCertificate but we have the others for human readability - @Entity - @Table(name = "${NODE_DATABASE_PREFIX}network_map_nodes") - class NetworkNode( - @Id - @Column(name = "node_party_key_hash", length = MAX_HASH_HEX_SIZE) - var publicKeyHash: String, - - @Column - var nodeParty: NodeParty = NodeParty(), - - @Lob @Column - var registrationInfo: ByteArray = ByteArray(0) - ) - - @Embeddable - class NodeParty( - @Column(name = "node_party_name") - var name: String = "", - - @Column(name = "node_party_certificate", length = 4096) - var certificate: ByteArray = ByteArray(0), - - @Column(name = "node_party_path", length = 4096) - var certPath: ByteArray = ByteArray(0) - ) - - private companion object { - private val factory = CertificateFactory.getInstance("X.509") - - fun createNetworkNodesMap(): PersistentMap { - return PersistentMap( - toPersistentEntityKey = { it.owningKey.toStringShort() }, - fromPersistentEntity = { - Pair(PartyAndCertificate(factory.generateCertPath(ByteArrayInputStream(it.nodeParty.certPath))), - it.registrationInfo.deserialize(context = SerializationDefaults.STORAGE_CONTEXT)) - }, - toPersistentEntity = { key: PartyAndCertificate, value: NodeRegistrationInfo -> - NetworkNode( - publicKeyHash = key.owningKey.toStringShort(), - nodeParty = NodeParty( - key.name.toString(), - key.certificate.encoded, - key.certPath.encoded - ), - registrationInfo = value.serialize(context = SerializationDefaults.STORAGE_CONTEXT).bytes - ) - }, - persistentEntityClass = NetworkNode::class.java - ) - } - - fun createNetworkSubscribersMap(): PersistentMap { - return PersistentMap( - toPersistentEntityKey = { it.getPrimaryKeyBasedOnSubType() }, - fromPersistentEntity = { - Pair(it.key.deserialize(context = SerializationDefaults.STORAGE_CONTEXT), - it.value.deserialize(context = SerializationDefaults.STORAGE_CONTEXT)) - }, - toPersistentEntity = { k: SingleMessageRecipient, v: LastAcknowledgeInfo -> - NetworkSubscriber( - id = k.getPrimaryKeyBasedOnSubType(), - key = k.serialize(context = SerializationDefaults.STORAGE_CONTEXT).bytes, - value = v.serialize(context = SerializationDefaults.STORAGE_CONTEXT).bytes - ) - }, - persistentEntityClass = NetworkSubscriber::class.java - ) - } - - fun SingleMessageRecipient.getPrimaryKeyBasedOnSubType() = - if (this is ArtemisMessagingComponent.ArtemisPeerAddress) { - this.hostAndPort.toString() - } else { - this.toString() - } - } - - override val nodeRegistrations: MutableMap = - Collections.synchronizedMap(createNetworkNodesMap()) - - @Entity - @Table(name = "${NODE_DATABASE_PREFIX}network_map_subscribers") - class NetworkSubscriber( - @Id @Column - var id: String = "", - - @Column(length = 4096) - var key: ByteArray = ByteArray(0), - - @Column(length = 4096) - var value: ByteArray = ByteArray(0) - ) - - override val subscribers = ThreadBox(createNetworkSubscribersMap()) -} diff --git a/node/src/main/kotlin/net/corda/node/services/schema/NodeSchemaService.kt b/node/src/main/kotlin/net/corda/node/services/schema/NodeSchemaService.kt index 8436f9459b..ea02e3d45f 100644 --- a/node/src/main/kotlin/net/corda/node/services/schema/NodeSchemaService.kt +++ b/node/src/main/kotlin/net/corda/node/services/schema/NodeSchemaService.kt @@ -3,11 +3,7 @@ package net.corda.node.services.schema import net.corda.core.contracts.ContractState import net.corda.core.contracts.FungibleAsset import net.corda.core.contracts.LinearState -import net.corda.core.schemas.CommonSchemaV1 -import net.corda.core.schemas.MappedSchema -import net.corda.core.schemas.NodeInfoSchemaV1 -import net.corda.core.schemas.PersistentState -import net.corda.core.schemas.QueryableState +import net.corda.core.schemas.* import net.corda.core.serialization.SingletonSerializeAsToken import net.corda.node.internal.cordapp.CordappLoader import net.corda.node.services.api.SchemaService @@ -15,7 +11,6 @@ import net.corda.node.services.events.NodeSchedulerService import net.corda.node.services.identity.PersistentIdentityService import net.corda.node.services.keys.PersistentKeyManagementService import net.corda.node.services.messaging.NodeMessagingClient -import net.corda.node.services.network.PersistentNetworkMapService import net.corda.node.services.persistence.DBCheckpointStorage import net.corda.node.services.persistence.DBTransactionMappingStorage import net.corda.node.services.persistence.DBTransactionStorage @@ -48,8 +43,6 @@ class NodeSchemaService(cordappLoader: CordappLoader?) : SchemaService, Singleto PersistentUniquenessProvider.PersistentNotaryCommit::class.java, NodeSchedulerService.PersistentScheduledState::class.java, NodeAttachmentService.DBAttachment::class.java, - PersistentNetworkMapService.NetworkNode::class.java, - PersistentNetworkMapService.NetworkSubscriber::class.java, NodeMessagingClient.ProcessedMessage::class.java, NodeMessagingClient.RetryMessage::class.java, NodeAttachmentService.DBAttachment::class.java, diff --git a/node/src/test/kotlin/net/corda/node/services/transactions/DistributedImmutableMapTests.kt b/node/src/test/kotlin/net/corda/node/services/transactions/DistributedImmutableMapTests.kt index b2a34d127e..f2ca28c8ec 100644 --- a/node/src/test/kotlin/net/corda/node/services/transactions/DistributedImmutableMapTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/transactions/DistributedImmutableMapTests.kt @@ -8,11 +8,12 @@ import io.atomix.copycat.server.storage.Storage import io.atomix.copycat.server.storage.StorageLevel import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.getOrThrow -import net.corda.node.services.network.NetworkMapService import net.corda.node.utilities.CordaPersistence import net.corda.node.utilities.DatabaseTransaction import net.corda.node.utilities.configureDatabase -import net.corda.testing.* +import net.corda.testing.LogHelper +import net.corda.testing.SerializationEnvironmentRule +import net.corda.testing.freeLocalHostAndPort import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties import net.corda.testing.node.MockServices.Companion.makeTestDatabaseProperties import net.corda.testing.node.MockServices.Companion.makeTestIdentityService @@ -37,14 +38,12 @@ class DistributedImmutableMapTests { @Before fun setup() { LogHelper.setLevel("-org.apache.activemq") - LogHelper.setLevel(NetworkMapService::class) cluster = setUpCluster() } @After fun tearDown() { LogHelper.reset("org.apache.activemq") - LogHelper.reset(NetworkMapService::class) cluster.forEach { it.client.close() it.server.shutdown() 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 b531d4e49b..3411eb5d0d 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 @@ -15,7 +15,6 @@ import net.corda.core.serialization.deserialize import net.corda.core.utilities.ProgressTracker import net.corda.netmap.VisualiserViewModel.Style import net.corda.netmap.simulation.IRSSimulation -import net.corda.node.services.network.NetworkMapService import net.corda.node.services.statemachine.SessionConfirm import net.corda.node.services.statemachine.SessionEnd import net.corda.node.services.statemachine.SessionInit 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 c552f30f07..127d010f31 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 @@ -28,7 +28,6 @@ import net.corda.node.internal.StartedNode import net.corda.node.internal.cordapp.CordappLoader import net.corda.node.services.Permissions.Companion.invokeRpc import net.corda.node.services.config.* -import net.corda.node.services.network.NetworkMapService import net.corda.node.utilities.ServiceIdentityGenerator import net.corda.nodeapi.NodeInfoFilesCopier import net.corda.nodeapi.User @@ -492,7 +491,7 @@ fun addressMustBeBoundFuture(executorService: ScheduledExecutorService, hostAndP /* * The default timeout value of 40 seconds have been chosen based on previous node shutdown time estimate. - * It's been observed that nodes can take up to 30 seconds to shut down, so just to stay on the safe side the 40 seconds + * It's been observed that nodes can take up to 30 seconds to shut down, so just to stay on the safe side the 60 seconds * timeout has been chosen. */ fun addressMustNotBeBound(executorService: ScheduledExecutorService, hostAndPort: NetworkHostAndPort, timeout: Duration = 40.seconds) { diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNode.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNode.kt index e925a3d022..098efd5e38 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNode.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNode.kt @@ -23,15 +23,12 @@ import net.corda.core.utilities.loggerFor import net.corda.node.internal.AbstractNode import net.corda.node.internal.StartedNode import net.corda.node.internal.cordapp.CordappLoader -import net.corda.node.services.api.NetworkMapCacheInternal import net.corda.node.services.api.SchemaService import net.corda.node.services.config.BFTSMaRtConfiguration import net.corda.node.services.config.NodeConfiguration import net.corda.node.services.config.NotaryConfig import net.corda.node.services.keys.E2ETestKeyManagementService import net.corda.node.services.messaging.MessagingService -import net.corda.node.services.network.InMemoryNetworkMapService -import net.corda.node.services.network.NetworkMapService import net.corda.node.services.transactions.BFTNonValidatingNotaryService import net.corda.node.services.transactions.BFTSMaRt import net.corda.node.services.transactions.InMemoryTransactionVerifierService @@ -213,10 +210,6 @@ class MockNetwork(defaultParameters: MockNetworkParameters = MockNetworkParamete // Nothing to do } - override fun makeNetworkMapService(network: MessagingService, networkMapCache: NetworkMapCacheInternal): NetworkMapService { - return InMemoryNetworkMapService() - } - // This is not thread safe, but node construction is done on a single thread, so that should always be fine override fun generateKeyPair(): KeyPair { counter = counter.add(BigInteger.ONE) From d04e48740b43096a514f38146dc541da0958ae43 Mon Sep 17 00:00:00 2001 From: Shams Asari Date: Thu, 2 Nov 2017 20:45:27 +0000 Subject: [PATCH 17/24] Introducing network parameters. network-parameters file read in by the node at startup, of which only the list of notaries is used. For now, the driver and MockNetwork have been updated to require notaries to be started first. This is so that the same set of network parameters can be defined for all the nodes. CN in the legal name is not longer disallowed since it's no longer reserved for distributed notary names. Single-node notaries now only have one identity, their main identity. Nodes part of a cluster continue to have two. (Based off Kasia's work) --- .../corda/client/jfx/NodeMonitorModelTest.kt | 36 ++-- .../client/jfx/model/NetworkIdentityModel.kt | 10 +- .../net/corda/core/concurrent/CordaFuture.kt | 2 +- .../net/corda/core/crypto/CryptoUtils.kt | 8 +- .../kotlin/net/corda/core/flows/NotaryFlow.kt | 6 +- .../net/corda/core/node/NetworkParameters.kt | 40 ++++ .../core/node/services/NetworkMapCache.kt | 12 +- docs/source/corda-configuration-file.rst | 4 - .../corda/docs/IntegrationTestingTutorial.kt | 6 +- .../net/corda/nodeapi/internal/ServiceInfo.kt | 27 --- .../net/corda/nodeapi/internal/ServiceType.kt | 44 ---- .../kotlin/net/corda/node/BootTests.kt | 15 -- .../node/services/AttachmentLoadingTests.kt | 17 +- .../node/services/BFTNotaryServiceTests.kt | 64 +++--- .../node/services/DistributedServiceTests.kt | 2 +- .../services/network/NodeInfoWatcherTest.kt | 13 +- .../network/PersistentNetworkMapCacheTest.kt | 10 +- .../statemachine/LargeTransactionsTest.kt | 4 +- .../messaging/MQSecurityAsNodeTest.kt | 1 - .../test/node/NodeStatePersistenceTests.kt | 11 +- .../net/corda/node/internal/AbstractNode.kt | 190 ++++++++++-------- .../kotlin/net/corda/node/internal/Node.kt | 22 +- .../node/services/config/NodeConfiguration.kt | 14 +- .../node/services/network/NodeInfoWatcher.kt | 9 +- .../network/PersistentNetworkMapCache.kt | 38 ++-- .../utilities/ServiceIdentityGenerator.kt | 10 +- .../net/corda/node/CordaRPCOpsImplTest.kt | 18 +- .../node/messaging/TwoPartyTradeFlowTests.kt | 19 +- .../corda/node/services/NotaryChangeTests.kt | 40 ++-- .../messaging/ArtemisMessagingTests.kt | 25 +-- .../statemachine/FlowFrameworkTests.kt | 12 +- .../attachmentdemo/AttachmentDemoTest.kt | 6 +- .../net/corda/bank/BankOfCordaHttpAPITest.kt | 21 +- .../corda/bank/BankOfCordaRPCClientTest.kt | 7 +- .../net/corda/bank/BankOfCordaDriver.kt | 4 +- .../corda/irs/api/NodeInterestRatesTest.kt | 4 +- .../corda/netmap/simulation/IRSSimulation.kt | 2 +- .../net/corda/notarydemo/BFTNotaryCordform.kt | 13 +- .../corda/notarydemo/RaftNotaryCordform.kt | 6 +- .../net/corda/traderdemo/TraderDemoTest.kt | 8 +- .../net/corda/testing/DriverConstants.kt | 8 +- .../kotlin/net/corda/testing/NodeTestUtils.kt | 1 - .../kotlin/net/corda/testing/driver/Driver.kt | 125 +++++++----- .../corda/testing/internal/NodeBasedTest.kt | 10 + .../testing/internal/demorun/DemoRunner.kt | 1 + .../corda/testing/node/MockNetworkMapCache.kt | 5 +- .../kotlin/net/corda/testing/node/MockNode.kt | 148 ++++++++------ .../net/corda/smoketesting/NodeProcess.kt | 18 +- testing/test-common/build.gradle | 4 + .../internal/NetworkParametersCopier.kt | 32 +++ .../common/internal/ParametersUtilities.kt | 18 ++ testing/test-utils/build.gradle | 1 - .../kotlin/net/corda/testing/TestConstants.kt | 1 - .../net/corda/explorer/views/Network.kt | 4 +- .../net/corda/loadtest/tests/NotaryTest.kt | 2 +- .../net/corda/verifier/VerifierDriver.kt | 3 +- .../net/corda/verifier/VerifierTests.kt | 12 +- 57 files changed, 664 insertions(+), 529 deletions(-) create mode 100644 core/src/main/kotlin/net/corda/core/node/NetworkParameters.kt delete mode 100644 node-api/src/main/kotlin/net/corda/nodeapi/internal/ServiceInfo.kt delete mode 100644 node-api/src/main/kotlin/net/corda/nodeapi/internal/ServiceType.kt create mode 100644 testing/test-common/src/main/kotlin/net/corda/testing/common/internal/NetworkParametersCopier.kt create mode 100644 testing/test-common/src/main/kotlin/net/corda/testing/common/internal/ParametersUtilities.kt 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 e703b5fa7c..c6af5cc734 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 @@ -11,6 +11,7 @@ import net.corda.core.flows.StateMachineRunId import net.corda.core.identity.CordaX500Name import net.corda.core.identity.Party import net.corda.core.internal.bufferUntilSubscribed +import net.corda.core.internal.concurrent.transpose import net.corda.core.messaging.CordaRPCOps import net.corda.core.messaging.StateMachineTransactionMapping import net.corda.core.messaging.StateMachineUpdate @@ -35,20 +36,20 @@ import org.junit.Test import rx.Observable class NodeMonitorModelTest { - lateinit var aliceNode: NodeInfo - lateinit var bobNode: NodeInfo - lateinit var notaryParty: Party + private lateinit var aliceNode: NodeInfo + private lateinit var bobNode: NodeInfo + private lateinit var notaryParty: Party - lateinit var rpc: CordaRPCOps - lateinit var rpcBob: CordaRPCOps - lateinit var stateMachineTransactionMapping: Observable - lateinit var stateMachineUpdates: Observable - lateinit var stateMachineUpdatesBob: Observable - lateinit var progressTracking: Observable - lateinit var transactions: Observable - lateinit var vaultUpdates: Observable> - lateinit var networkMapUpdates: Observable - lateinit var newNode: (CordaX500Name) -> NodeInfo + private lateinit var rpc: CordaRPCOps + private lateinit var rpcBob: CordaRPCOps + private lateinit var stateMachineTransactionMapping: Observable + private lateinit var stateMachineUpdates: Observable + private lateinit var stateMachineUpdatesBob: Observable + private lateinit var progressTracking: Observable + private lateinit var transactions: Observable + private lateinit var vaultUpdates: Observable> + private lateinit var networkMapUpdates: Observable + private lateinit var newNode: (CordaX500Name) -> NodeInfo private fun setup(runTest: () -> Unit) = driver(extraCordappPackagesToScan = listOf("net.corda.finance")) { val cashUser = User("user1", "test", permissions = setOf( startFlow(), @@ -62,9 +63,10 @@ class NodeMonitorModelTest { invokeRpc(CordaRPCOps::stateMachinesFeed), invokeRpc(CordaRPCOps::networkMapFeed)) ) - val aliceNodeFuture = startNode(providedName = ALICE.name, rpcUsers = listOf(cashUser)) - val notaryHandle = startNotaryNode(DUMMY_NOTARY.name, validating = false).getOrThrow() - val aliceNodeHandle = aliceNodeFuture.getOrThrow() + val (notaryHandle, aliceNodeHandle) = listOf( + startNotaryNode(DUMMY_NOTARY.name, validating = false), + startNode(providedName = ALICE.name, rpcUsers = listOf(cashUser)) + ).transpose().getOrThrow() aliceNode = aliceNodeHandle.nodeInfo newNode = { nodeName -> startNode(providedName = nodeName).getOrThrow().nodeInfo } val monitor = NodeMonitorModel() @@ -77,7 +79,7 @@ class NodeMonitorModelTest { monitor.register(aliceNodeHandle.configuration.rpcAddress!!, cashUser.username, cashUser.password) rpc = monitor.proxyObservable.value!! - notaryParty = notaryHandle.nodeInfo.legalIdentities[1] + notaryParty = notaryHandle.nodeInfo.legalIdentities[0] val bobNodeHandle = startNode(providedName = BOB.name, rpcUsers = listOf(cashUser)).getOrThrow() bobNode = bobNodeHandle.nodeInfo diff --git a/client/jfx/src/main/kotlin/net/corda/client/jfx/model/NetworkIdentityModel.kt b/client/jfx/src/main/kotlin/net/corda/client/jfx/model/NetworkIdentityModel.kt index 2213d40bbc..5451f2acce 100644 --- a/client/jfx/src/main/kotlin/net/corda/client/jfx/model/NetworkIdentityModel.kt +++ b/client/jfx/src/main/kotlin/net/corda/client/jfx/model/NetworkIdentityModel.kt @@ -5,6 +5,7 @@ import com.google.common.cache.CacheLoader import javafx.beans.value.ObservableValue import javafx.collections.FXCollections import javafx.collections.ObservableList +import net.corda.client.jfx.utils.ChosenList import net.corda.client.jfx.utils.filterNotNull import net.corda.client.jfx.utils.fold import net.corda.client.jfx.utils.map @@ -12,7 +13,6 @@ import net.corda.core.identity.AnonymousParty import net.corda.core.identity.Party import net.corda.core.node.NodeInfo import net.corda.core.node.services.NetworkMapCache.MapChange -import net.corda.nodeapi.internal.ServiceType import java.security.PublicKey class NetworkIdentityModel { @@ -36,16 +36,10 @@ class NetworkIdentityModel { .build>(CacheLoader.from { publicKey -> publicKey?.let { rpcProxy.map { it?.nodeInfoFromParty(AnonymousParty(publicKey)) } } }) - - val notaries: ObservableList = networkIdentities.map { - it.legalIdentitiesAndCerts.find { it.name.commonName?.let { ServiceType.parse(it).isNotary() } == true } - }.map { it?.party }.filterNotNull() - + val notaries = ChosenList(rpcProxy.map { FXCollections.observableList(it?.notaryIdentities() ?: emptyList()) }) val notaryNodes: ObservableList = notaries.map { rpcProxy.value?.nodeInfoFromParty(it) }.filterNotNull() val parties: ObservableList = networkIdentities .filtered { it.legalIdentities.all { it !in notaries } } - // TODO: REMOVE THIS HACK WHEN NETWORK MAP REDESIGN WORK IS COMPLETED. - .filtered { it.legalIdentities.all { it.name.organisation != "Network Map Service" } } val myIdentity = rpcProxy.map { it?.nodeInfo()?.legalIdentitiesAndCerts?.first()?.party } fun partyFromPublicKey(publicKey: PublicKey): ObservableValue = identityCache[publicKey] diff --git a/core/src/main/kotlin/net/corda/core/concurrent/CordaFuture.kt b/core/src/main/kotlin/net/corda/core/concurrent/CordaFuture.kt index 3ecab2e395..4977f3a34b 100644 --- a/core/src/main/kotlin/net/corda/core/concurrent/CordaFuture.kt +++ b/core/src/main/kotlin/net/corda/core/concurrent/CordaFuture.kt @@ -13,7 +13,7 @@ interface CordaFuture : Future { * If the completion thread is problematic for you e.g. deadlock, you can submit to an executor manually. * If callback fails, its throwable is logged. */ - fun then(callback: (CordaFuture) -> W): Unit + fun then(callback: (CordaFuture) -> W) /** * @return a new [CompletableFuture] with the same outcome as this Future. diff --git a/core/src/main/kotlin/net/corda/core/crypto/CryptoUtils.kt b/core/src/main/kotlin/net/corda/core/crypto/CryptoUtils.kt index 17bd078862..10e7c2f69c 100644 --- a/core/src/main/kotlin/net/corda/core/crypto/CryptoUtils.kt +++ b/core/src/main/kotlin/net/corda/core/crypto/CryptoUtils.kt @@ -33,7 +33,9 @@ fun PrivateKey.sign(bytesToSign: ByteArray): DigitalSignature = DigitalSignature * @throws SignatureException if signing is not possible due to malformed data or private key. */ @Throws(InvalidKeyException::class, SignatureException::class) -fun PrivateKey.sign(bytesToSign: ByteArray, publicKey: PublicKey) = DigitalSignature.WithKey(publicKey, this.sign(bytesToSign).bytes) +fun PrivateKey.sign(bytesToSign: ByteArray, publicKey: PublicKey): DigitalSignature.WithKey { + return DigitalSignature.WithKey(publicKey, this.sign(bytesToSign).bytes) +} /** * Helper function to sign with a key pair. @@ -44,11 +46,11 @@ fun PrivateKey.sign(bytesToSign: ByteArray, publicKey: PublicKey) = DigitalSigna * @throws SignatureException if signing is not possible due to malformed data or private key. */ @Throws(InvalidKeyException::class, SignatureException::class) -fun KeyPair.sign(bytesToSign: ByteArray) = private.sign(bytesToSign, public) +fun KeyPair.sign(bytesToSign: ByteArray): DigitalSignature.WithKey = private.sign(bytesToSign, public) /** Helper function to sign the bytes of [bytesToSign] with a key pair. */ @Throws(InvalidKeyException::class, SignatureException::class) -fun KeyPair.sign(bytesToSign: OpaqueBytes) = sign(bytesToSign.bytes) +fun KeyPair.sign(bytesToSign: OpaqueBytes): DigitalSignature.WithKey = sign(bytesToSign.bytes) /** * Helper function for signing a [SignableData] object. diff --git a/core/src/main/kotlin/net/corda/core/flows/NotaryFlow.kt b/core/src/main/kotlin/net/corda/core/flows/NotaryFlow.kt index c96070fba7..0fccf0cb4c 100644 --- a/core/src/main/kotlin/net/corda/core/flows/NotaryFlow.kt +++ b/core/src/main/kotlin/net/corda/core/flows/NotaryFlow.kt @@ -49,6 +49,7 @@ class NotaryFlow { progressTracker.currentStep = REQUESTING val notaryParty = stx.notary ?: throw IllegalStateException("Transaction does not specify a Notary") + check(serviceHub.networkMapCache.isNotary(notaryParty)) { "$notaryParty is not a notary on the network" } check(stx.inputs.all { stateRef -> serviceHub.loadState(stateRef).notary == notaryParty }) { "Input states must have the same Notary" } @@ -115,6 +116,9 @@ class NotaryFlow { @Suspendable override fun call(): Void? { + check(serviceHub.myInfo.legalIdentities.any { serviceHub.networkMapCache.isNotary(it) }) { + "We are not a notary on the network" + } val (id, inputs, timeWindow, notary) = receiveAndVerifyTx() checkNotary(notary) service.validateTimeWindow(timeWindow) @@ -135,7 +139,7 @@ class NotaryFlow { protected fun checkNotary(notary: Party?) { // TODO This check implies that it's OK to use the node's main identity. Shouldn't it be just limited to the // notary identities? - if (notary !in serviceHub.myInfo.legalIdentities) { + if (notary == null || !serviceHub.myInfo.isLegalIdentity(notary)) { throw NotaryException(NotaryError.WrongNotary) } } diff --git a/core/src/main/kotlin/net/corda/core/node/NetworkParameters.kt b/core/src/main/kotlin/net/corda/core/node/NetworkParameters.kt new file mode 100644 index 0000000000..d5a035175a --- /dev/null +++ b/core/src/main/kotlin/net/corda/core/node/NetworkParameters.kt @@ -0,0 +1,40 @@ +package net.corda.core.node + +import net.corda.core.identity.Party +import net.corda.core.serialization.CordaSerializable +import java.time.Duration +import java.time.Instant + +/** + * @property minimumPlatformVersion + * @property notaries + * @property eventHorizon + * @property maxMessageSize Maximum P2P message sent over the wire in bytes. + * @property maxTransactionSize Maximum permitted transaction size in bytes. + * @property modifiedTime + * @property epoch Version number of the network parameters. Starting from 1, this will always increment on each new set + * of parameters. + */ +// TODO Wire up the parameters +@CordaSerializable +data class NetworkParameters( + val minimumPlatformVersion: Int, + val notaries: List, + val eventHorizon: Duration, + val maxMessageSize: Int, + val maxTransactionSize: Int, + val modifiedTime: Instant, + val epoch: Int +) { + init { + require(minimumPlatformVersion > 0) { "minimumPlatformVersion must be at least 1" } + require(notaries.distinctBy { it.identity } == notaries) { "Duplicate notary identities" } + require(epoch > 0) { "epoch must be at least 1" } + } +} + +/** + * + */ +@CordaSerializable +data class NotaryInfo(val identity: Party, val validating: Boolean) diff --git a/core/src/main/kotlin/net/corda/core/node/services/NetworkMapCache.kt b/core/src/main/kotlin/net/corda/core/node/services/NetworkMapCache.kt index b5c415c578..d1bb9fd503 100644 --- a/core/src/main/kotlin/net/corda/core/node/services/NetworkMapCache.kt +++ b/core/src/main/kotlin/net/corda/core/node/services/NetworkMapCache.kt @@ -52,7 +52,6 @@ interface NetworkMapCacheBase { * * Note that the identities are sorted based on legal name, and the ordering might change once new notaries are introduced. */ - // TODO this list will be taken from NetworkParameters distributed by NetworkMap. val notaryIdentities: List // DOCEND 1 @@ -116,12 +115,15 @@ interface NetworkMapCacheBase { fun getNotary(name: CordaX500Name): Party? = notaryIdentities.firstOrNull { it.name == name } // DOCEND 2 - /** Checks whether a given party is an advertised notary identity. */ + /** Returns true if and only if the given [Party] is a notary, which is defined by the network parameters. */ fun isNotary(party: Party): Boolean = party in notaryIdentities - /** Checks whether a given party is an validating notary identity. */ - // TODO This implementation will change after introducing of NetworkParameters. - fun isValidatingNotary(party: Party): Boolean = isNotary(party) && "validating" in party.name.commonName!! + /** + * Returns true if and only if the given [Party] is validating notary. For every party that is a validating notary, + * [isNotary] is only true. + * @see isNotary + */ + fun isValidatingNotary(party: Party): Boolean /** Clear all network map data from local node cache. */ fun clearNetworkMapCache() diff --git a/docs/source/corda-configuration-file.rst b/docs/source/corda-configuration-file.rst index 9bc2aba5e8..fbb144d795 100644 --- a/docs/source/corda-configuration-file.rst +++ b/docs/source/corda-configuration-file.rst @@ -118,10 +118,6 @@ path to the node's base directory. Only one of ``raft``, ``bftSMaRt`` or ``custom`` configuration values may be specified. -:minimumPlatformVersion: Used by the node if it's running the network map service to enforce a minimum version requirement - on registrations - any node on a Platform Version lower than this value will have their registration rejected. - Defaults to 1 if absent. - :useHTTPS: If false the node's web server will be plain HTTP. If true the node will use the same certificate and private key from the ``/certificates/sslkeystore.jks`` file as the ArtemisMQ port for HTTPS. If HTTPS is enabled then unencrypted HTTP traffic to the node's **webAddress** port is not supported. 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 a5ee68bce7..be3ab27dc4 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 @@ -39,10 +39,10 @@ class IntegrationTestingTutorial { invokeRpc("vaultTrackBy"), invokeRpc(CordaRPCOps::networkMapFeed) )) - val (alice, bob) = listOf( + val (_, alice, bob) = listOf( + startNotaryNode(DUMMY_NOTARY.name), startNode(providedName = ALICE.name, rpcUsers = listOf(aliceUser)), - startNode(providedName = BOB.name, rpcUsers = listOf(bobUser)), - startNotaryNode(DUMMY_NOTARY.name) + startNode(providedName = BOB.name, rpcUsers = listOf(bobUser)) ).transpose().getOrThrow() // END 1 diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/ServiceInfo.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/ServiceInfo.kt deleted file mode 100644 index 25e3734165..0000000000 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/ServiceInfo.kt +++ /dev/null @@ -1,27 +0,0 @@ -package net.corda.nodeapi.internal - -import net.corda.core.identity.CordaX500Name -import net.corda.core.serialization.CordaSerializable - -/** - * A container for additional information for an advertised service. - * - * @param type the ServiceType identifier - * @param name the service name, used for differentiating multiple services of the same type. Can also be used as a - * grouping identifier for nodes collectively running a distributed service. - */ -@CordaSerializable -data class ServiceInfo(val type: ServiceType, val name: CordaX500Name? = null) { - companion object { - fun parse(encoded: String): ServiceInfo { - val parts = encoded.split("|") - require(parts.size in 1..2) { "Invalid number of elements found" } - val type = ServiceType.parse(parts[0]) - val name = parts.getOrNull(1) - val principal = name?.let { CordaX500Name.parse(it) } - return ServiceInfo(type, principal) - } - } - - override fun toString() = if (name != null) "$type|$name" else type.toString() -} diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/ServiceType.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/ServiceType.kt deleted file mode 100644 index 7ec0a1ce8f..0000000000 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/ServiceType.kt +++ /dev/null @@ -1,44 +0,0 @@ -package net.corda.nodeapi.internal - -import net.corda.core.serialization.CordaSerializable - -/** - * Identifier for service types a node can expose over the network to other peers. These types are placed into network - * map advertisements. Services that are purely local and are not providing functionality to other parts of the network - * don't need a declared service type. - */ -@CordaSerializable -class ServiceType private constructor(val id: String) { - init { - // Enforce: - // - // * IDs must start with a lower case letter - // * IDs can only contain alphanumeric, full stop and underscore ASCII characters - require(id.matches(Regex("[a-z][a-zA-Z0-9._]+"))) { id } - } - - companion object { - val corda: ServiceType - get() { - val stack = Throwable().stackTrace - val caller = stack.first().className - require(caller.startsWith("net.corda.")) { "Corda ServiceType namespace is reserved for Corda core components" } - return ServiceType("corda") - } - - val notary: ServiceType = corda.getSubType("notary") - - fun parse(id: String): ServiceType = ServiceType(id) - - private fun baseWithSubType(baseId: String, subTypeId: String) = ServiceType("$baseId.$subTypeId") - } - - fun getSubType(subTypeId: String): ServiceType = baseWithSubType(id, subTypeId) - - fun isSubTypeOf(superType: ServiceType) = (id == superType.id) || id.startsWith(superType.id + ".") - fun isNotary() = isSubTypeOf(notary) - - override fun equals(other: Any?): Boolean = other === this || other is ServiceType && other.id == this.id - override fun hashCode(): Int = id.hashCode() - override fun toString(): String = id -} 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 e4c4ff5198..9973be0bf7 100644 --- a/node/src/integration-test/kotlin/net/corda/node/BootTests.kt +++ b/node/src/integration-test/kotlin/net/corda/node/BootTests.kt @@ -9,20 +9,15 @@ import net.corda.core.utilities.getOrThrow import net.corda.node.internal.NodeStartup import net.corda.node.services.Permissions.Companion.startFlow import net.corda.nodeapi.User -import net.corda.nodeapi.internal.ServiceInfo -import net.corda.nodeapi.internal.ServiceType import net.corda.testing.ALICE import net.corda.testing.ProjectStructure.projectRootDir -import net.corda.testing.driver.ListenProcessDeathException import net.corda.testing.driver.driver import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThatThrownBy -import org.junit.Ignore import org.junit.Test import java.io.* import java.nio.file.Files import kotlin.test.assertEquals -import kotlin.test.assertFailsWith class BootTests { @@ -53,16 +48,6 @@ class BootTests { assertEquals(1, numberOfNodesThatLogged) } } - - @Ignore("Need rewriting to produce too big network map registration (adverticed services trick doesn't work after services removal).") - @Test - fun `node quits on failure to register with network map`() { - val tooManyAdvertisedServices = (1..100).map { ServiceInfo(ServiceType.notary.getSubType("$it")) }.toSet() - driver { - val future = startNode(providedName = ALICE.name) - assertFailsWith(ListenProcessDeathException::class) { future.getOrThrow() } - } - } } @StartableByRPC diff --git a/node/src/integration-test/kotlin/net/corda/node/services/AttachmentLoadingTests.kt b/node/src/integration-test/kotlin/net/corda/node/services/AttachmentLoadingTests.kt index 2065c7d350..9f6ad6a4d3 100644 --- a/node/src/integration-test/kotlin/net/corda/node/services/AttachmentLoadingTests.kt +++ b/node/src/integration-test/kotlin/net/corda/node/services/AttachmentLoadingTests.kt @@ -20,10 +20,13 @@ import net.corda.core.utilities.seconds import net.corda.node.internal.cordapp.CordappLoader import net.corda.node.internal.cordapp.CordappProviderImpl import net.corda.nodeapi.User -import net.corda.testing.* +import net.corda.testing.DUMMY_BANK_A +import net.corda.testing.DUMMY_NOTARY +import net.corda.testing.SerializationEnvironmentRule import net.corda.testing.driver.DriverDSLExposedInterface import net.corda.testing.driver.NodeHandle import net.corda.testing.driver.driver +import net.corda.testing.eventually import net.corda.testing.node.MockServices import org.junit.Assert.assertEquals import org.junit.Before @@ -54,16 +57,16 @@ class AttachmentLoadingTests { val bankAName = CordaX500Name("BankA", "Zurich", "CH") val bankBName = CordaX500Name("BankB", "Zurich", "CH") val notaryName = CordaX500Name("Notary", "Zurich", "CH") - val flowInitiatorClass = + val flowInitiatorClass: Class> = Class.forName("net.corda.finance.contracts.isolated.IsolatedDummyFlow\$Initiator", true, URLClassLoader(arrayOf(isolatedJAR))) .asSubclass(FlowLogic::class.java) - private fun DriverDSLExposedInterface.createTwoNodesAndNotary(): List { + private fun DriverDSLExposedInterface.createNotaryAndTwoNodes(): List { val adminUser = User("admin", "admin", permissions = setOf("ALL")) val nodes = listOf( + startNotaryNode(providedName = notaryName, rpcUsers = listOf(adminUser), validating = false), startNode(providedName = bankAName, rpcUsers = listOf(adminUser)), - startNode(providedName = bankBName, rpcUsers = listOf(adminUser)), - startNotaryNode(providedName = notaryName, rpcUsers = listOf(adminUser), validating = false) + startNode(providedName = bankBName, rpcUsers = listOf(adminUser)) ).transpose().getOrThrow() // Wait for all nodes to start up. nodes.forEach { it.rpc.waitUntilNetworkReady().getOrThrow() } return nodes @@ -119,7 +122,7 @@ class AttachmentLoadingTests { fun `test that attachments retrieved over the network are not used for code`() { driver(initialiseSerialization = false) { installIsolatedCordappTo(bankAName) - val (bankA, bankB, _) = createTwoNodesAndNotary() + val (_, bankA, bankB) = createNotaryAndTwoNodes() eventuallyPassingTest { assertFailsWith("Party C=CH,L=Zurich,O=BankB rejected session request: Don't know net.corda.finance.contracts.isolated.IsolatedDummyFlow\$Initiator") { bankA.rpc.startFlowDynamic(flowInitiatorClass, bankB.nodeInfo.legalIdentities.first()).returnValue.getOrThrow() @@ -133,7 +136,7 @@ class AttachmentLoadingTests { driver(initialiseSerialization = false) { installIsolatedCordappTo(bankAName) installIsolatedCordappTo(bankBName) - val (bankA, bankB, _) = createTwoNodesAndNotary() + val (_, bankA, bankB) = createNotaryAndTwoNodes() eventuallyPassingTest { bankA.rpc.startFlowDynamic(flowInitiatorClass, bankB.nodeInfo.legalIdentities.first()).returnValue.getOrThrow() } 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 3605ab14a4..dafdb08c7f 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 @@ -13,6 +13,8 @@ import net.corda.core.identity.CordaX500Name import net.corda.core.identity.Party import net.corda.core.internal.deleteIfExists import net.corda.core.internal.div +import net.corda.core.node.NotaryInfo +import net.corda.core.node.services.NotaryService import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.TransactionBuilder import net.corda.core.utilities.NetworkHostAndPort @@ -21,15 +23,16 @@ 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.BFTNonValidatingNotaryService import net.corda.node.services.transactions.minClusterSize import net.corda.node.services.transactions.minCorrectReplicas import net.corda.node.utilities.ServiceIdentityGenerator import net.corda.testing.chooseIdentity +import net.corda.testing.common.internal.NetworkParametersCopier +import net.corda.testing.common.internal.testNetworkParameters import net.corda.testing.contracts.DummyContract import net.corda.testing.dummyCommand -import net.corda.testing.getDefaultNotary import net.corda.testing.node.MockNetwork +import net.corda.testing.node.MockNetwork.MockNode import net.corda.testing.node.MockNodeParameters import org.junit.After import org.junit.Test @@ -38,39 +41,47 @@ import kotlin.test.assertEquals import kotlin.test.assertTrue class BFTNotaryServiceTests { - companion object { - private val clusterName = CordaX500Name(BFTNonValidatingNotaryService.id, "BFT", "Zurich", "CH") - } - private val mockNet = MockNetwork() - private val node = mockNet.createNode() + private lateinit var notary: Party + private lateinit var node: StartedNode @After fun stopNodes() { mockNet.stopNodes() } - private fun bftNotaryCluster(clusterSize: Int, exposeRaces: Boolean = false) { + private fun startBftClusterAndNode(clusterSize: Int, exposeRaces: Boolean = false) { (Paths.get("config") / "currentView").deleteIfExists() // XXX: Make config object warn if this exists? val replicaIds = (0 until clusterSize) - ServiceIdentityGenerator.generateToDisk( + + notary = ServiceIdentityGenerator.generateToDisk( replicaIds.map { mockNet.baseDirectory(mockNet.nextNodeId + it) }, - clusterName) + CordaX500Name("BFT", "Zurich", "CH"), + NotaryService.constructId(validating = false, bft = true)) + + val networkParameters = NetworkParametersCopier(testNetworkParameters(listOf(NotaryInfo(notary, false)))) + val clusterAddresses = replicaIds.map { NetworkHostAndPort("localhost", 11000 + it * 10) } - replicaIds.forEach { replicaId -> - mockNet.createNode(MockNodeParameters(configOverrides = { + + val nodes = replicaIds.map { replicaId -> + mockNet.createUnstartedNode(MockNodeParameters(configOverrides = { val notary = NotaryConfig(validating = false, bftSMaRt = BFTSMaRtConfiguration(replicaId, clusterAddresses, exposeRaces = exposeRaces)) doReturn(notary).whenever(it).notary })) - } - mockNet.runNetwork() // Exchange initial network map registration messages. + } + mockNet.createUnstartedNode() + + // MockNetwork doesn't support BFT clusters, so we create all the nodes we need unstarted, and then install the + // network-parameters in their directories before they're started. + node = nodes.map { node -> + networkParameters.install(mockNet.baseDirectory(node.id)) + node.start() + }.last() } /** Failure mode is the redundant replica gets stuck in startup, so we can't dispose it cleanly at the end. */ @Test fun `all replicas start even if there is a new consensus during startup`() { - bftNotaryCluster(minClusterSize(1), true) // This true adds a sleep to expose the race. - val notary = node.services.getDefaultNotary() + startBftClusterAndNode(minClusterSize(1), exposeRaces = true) // This true adds a sleep to expose the race. val f = node.run { val trivialTx = signInitialTransaction(notary) { addOutputState(DummyContract.SingleOwnerState(owner = info.chooseIdentity()), DummyContract.PROGRAM_ID, AlwaysAcceptAttachmentConstraint) @@ -94,8 +105,7 @@ class BFTNotaryServiceTests { private fun detectDoubleSpend(faultyReplicas: Int) { val clusterSize = minClusterSize(faultyReplicas) - bftNotaryCluster(clusterSize) - val notary = node.services.getDefaultNotary() + startBftClusterAndNode(clusterSize) node.run { val issueTx = signInitialTransaction(notary) { addOutputState(DummyContract.SingleOwnerState(owner = info.chooseIdentity()), DummyContract.PROGRAM_ID, AlwaysAcceptAttachmentConstraint) @@ -138,15 +148,13 @@ class BFTNotaryServiceTests { } } } -} -private fun StartedNode<*>.signInitialTransaction( - notary: Party, - block: TransactionBuilder.() -> Any? -): SignedTransaction { - return services.signInitialTransaction( - TransactionBuilder(notary).apply { - addCommand(dummyCommand(services.myInfo.chooseIdentity().owningKey)) - block() - }) + private fun StartedNode<*>.signInitialTransaction(notary: Party, block: TransactionBuilder.() -> Any?): SignedTransaction { + return services.signInitialTransaction( + TransactionBuilder(notary).apply { + addCommand(dummyCommand(services.myInfo.chooseIdentity().owningKey)) + block() + } + ) + } } 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 3f5fd63e45..bc41e797cd 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 @@ -38,12 +38,12 @@ class DistributedServiceTests { invokeRpc(CordaRPCOps::nodeInfo), invokeRpc(CordaRPCOps::stateMachinesFeed)) ) - val aliceFuture = startNode(providedName = ALICE.name, rpcUsers = listOf(testUser)) val notariesFuture = startNotaryCluster( DUMMY_NOTARY.name.copy(commonName = RaftValidatingNotaryService.id), rpcUsers = listOf(testUser), clusterSize = clusterSize ) + val aliceFuture = startNode(providedName = ALICE.name, rpcUsers = listOf(testUser)) alice = aliceFuture.get() val (notaryIdentity, notaryNodes) = notariesFuture.get() diff --git a/node/src/integration-test/kotlin/net/corda/node/services/network/NodeInfoWatcherTest.kt b/node/src/integration-test/kotlin/net/corda/node/services/network/NodeInfoWatcherTest.kt index 07b04868a0..897504d62d 100644 --- a/node/src/integration-test/kotlin/net/corda/node/services/network/NodeInfoWatcherTest.kt +++ b/node/src/integration-test/kotlin/net/corda/node/services/network/NodeInfoWatcherTest.kt @@ -6,15 +6,11 @@ import net.corda.cordform.CordformNode import net.corda.core.internal.createDirectories import net.corda.core.internal.div import net.corda.core.node.NodeInfo -import net.corda.core.node.services.KeyManagementService -import net.corda.node.services.identity.InMemoryIdentityService import net.corda.nodeapi.NodeInfoFilesCopier import net.corda.testing.ALICE import net.corda.testing.ALICE_KEY -import net.corda.testing.DEV_TRUST_ROOT import net.corda.testing.getTestPartyAndCertificate import net.corda.testing.internal.NodeBasedTest -import net.corda.testing.node.MockKeyManagementService import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.contentOf import org.junit.Before @@ -31,7 +27,6 @@ class NodeInfoWatcherTest : NodeBasedTest() { val nodeInfo = NodeInfo(listOf(), listOf(getTestPartyAndCertificate(ALICE)), 0, 0) } - private lateinit var keyManagementService: KeyManagementService private lateinit var nodeInfoPath: Path private val scheduler = TestScheduler() private val testSubscriber = TestSubscriber() @@ -41,8 +36,6 @@ class NodeInfoWatcherTest : NodeBasedTest() { @Before fun start() { - val identityService = InMemoryIdentityService(trustRoot = DEV_TRUST_ROOT) - keyManagementService = MockKeyManagementService(identityService, ALICE_KEY) nodeInfoWatcher = NodeInfoWatcher(tempFolder.root.toPath(), scheduler = scheduler) nodeInfoPath = tempFolder.root.toPath() / CordformNode.NODE_INFO_DIRECTORY } @@ -51,7 +44,7 @@ class NodeInfoWatcherTest : NodeBasedTest() { fun `save a NodeInfo`() { assertEquals(0, tempFolder.root.list().filter { it.startsWith(NodeInfoFilesCopier.NODE_INFO_FILE_NAME_PREFIX) }.size) - NodeInfoWatcher.saveToFile(tempFolder.root.toPath(), nodeInfo, keyManagementService) + NodeInfoWatcher.saveToFile(tempFolder.root.toPath(), nodeInfo, ALICE_KEY) val nodeInfoFiles = tempFolder.root.list().filter { it.startsWith(NodeInfoFilesCopier.NODE_INFO_FILE_NAME_PREFIX) } assertEquals(1, nodeInfoFiles.size) @@ -66,7 +59,7 @@ class NodeInfoWatcherTest : NodeBasedTest() { fun `save a NodeInfo to JimFs`() { val jimFs = Jimfs.newFileSystem(Configuration.unix()) val jimFolder = jimFs.getPath("/nodeInfo") - NodeInfoWatcher.saveToFile(jimFolder, nodeInfo, keyManagementService) + NodeInfoWatcher.saveToFile(jimFolder, nodeInfo, ALICE_KEY) } @Test @@ -135,6 +128,6 @@ class NodeInfoWatcherTest : NodeBasedTest() { // Write a nodeInfo under the right path. private fun createNodeInfoFileInPath(nodeInfo: NodeInfo) { - NodeInfoWatcher.saveToFile(nodeInfoPath, nodeInfo, keyManagementService) + NodeInfoWatcher.saveToFile(nodeInfoPath, nodeInfo, ALICE_KEY) } } diff --git a/node/src/integration-test/kotlin/net/corda/node/services/network/PersistentNetworkMapCacheTest.kt b/node/src/integration-test/kotlin/net/corda/node/services/network/PersistentNetworkMapCacheTest.kt index 30ac56d490..d4bdec79b7 100644 --- a/node/src/integration-test/kotlin/net/corda/node/services/network/PersistentNetworkMapCacheTest.kt +++ b/node/src/integration-test/kotlin/net/corda/node/services/network/PersistentNetworkMapCacheTest.kt @@ -8,7 +8,7 @@ import net.corda.node.internal.Node import net.corda.node.internal.StartedNode import net.corda.testing.ALICE import net.corda.testing.BOB -import net.corda.testing.DUMMY_NOTARY +import net.corda.testing.DUMMY_REGULATOR import net.corda.testing.chooseIdentity import net.corda.testing.internal.NodeBasedTest import org.junit.Before @@ -16,9 +16,9 @@ import org.junit.Test import kotlin.test.assertEquals class PersistentNetworkMapCacheTest : NodeBasedTest() { - private val partiesList = listOf(DUMMY_NOTARY, ALICE, BOB) + private val partiesList = listOf(DUMMY_REGULATOR, ALICE, BOB) private val addressesMap = HashMap() - private val infos: MutableSet = HashSet() + private val infos = HashSet() @Before fun start() { @@ -37,8 +37,8 @@ class PersistentNetworkMapCacheTest : NodeBasedTest() { alice.database.transaction { val res = netCache.getNodeByLegalIdentity(alice.info.chooseIdentity()) assertEquals(alice.info, res) - val res2 = netCache.getNodeByLegalName(DUMMY_NOTARY.name) - assertEquals(infos.singleOrNull { DUMMY_NOTARY.name in it.legalIdentitiesAndCerts.map { it.name } }, res2) + val res2 = netCache.getNodeByLegalName(DUMMY_REGULATOR.name) + assertEquals(infos.singleOrNull { DUMMY_REGULATOR.name in it.legalIdentities.map { it.name } }, res2) } } 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 46ac098355..49de548f16 100644 --- a/node/src/integration-test/kotlin/net/corda/node/services/statemachine/LargeTransactionsTest.kt +++ b/node/src/integration-test/kotlin/net/corda/node/services/statemachine/LargeTransactionsTest.kt @@ -8,11 +8,11 @@ import net.corda.core.messaging.startFlow import net.corda.core.transactions.TransactionBuilder import net.corda.testing.BOB import net.corda.testing.DUMMY_NOTARY -import net.corda.testing.aliceBobAndNotary import net.corda.testing.contracts.DummyContract import net.corda.testing.contracts.DummyState import net.corda.testing.driver.driver import net.corda.testing.dummyCommand +import net.corda.testing.notaryAliceAndBob import org.junit.Test import kotlin.test.assertEquals @@ -65,7 +65,7 @@ class LargeTransactionsTest { val bigFile3 = InputStreamAndHash.createInMemoryTestZip(1024 * 1024 * 3, 2) val bigFile4 = InputStreamAndHash.createInMemoryTestZip(1024 * 1024 * 3, 3) driver(startNodesInProcess = true, extraCordappPackagesToScan = listOf("net.corda.testing.contracts")) { - val (alice, _, _) = aliceBobAndNotary() + val (_, alice) = notaryAliceAndBob() alice.useRPC { val hash1 = it.uploadAttachment(bigFile1.inputStream) val hash2 = it.uploadAttachment(bigFile2.inputStream) diff --git a/node/src/integration-test/kotlin/net/corda/services/messaging/MQSecurityAsNodeTest.kt b/node/src/integration-test/kotlin/net/corda/services/messaging/MQSecurityAsNodeTest.kt index b795962f87..3f27c4e2b2 100644 --- a/node/src/integration-test/kotlin/net/corda/services/messaging/MQSecurityAsNodeTest.kt +++ b/node/src/integration-test/kotlin/net/corda/services/messaging/MQSecurityAsNodeTest.kt @@ -18,7 +18,6 @@ import org.assertj.core.api.Assertions.assertThatExceptionOfType import org.bouncycastle.asn1.x509.GeneralName import org.bouncycastle.asn1.x509.GeneralSubtree import org.bouncycastle.asn1.x509.NameConstraints -import org.bouncycastle.cert.X509CertificateHolder import org.junit.Test import java.nio.file.Files 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 00edc6d782..21e70426cc 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 @@ -23,7 +23,7 @@ import net.corda.nodeapi.User import net.corda.testing.DUMMY_NOTARY import net.corda.testing.chooseIdentity import net.corda.testing.driver.driver -import org.junit.Assume +import org.junit.Assume.assumeFalse import org.junit.Test import java.lang.management.ManagementFactory import javax.persistence.Column @@ -33,25 +33,24 @@ import kotlin.test.assertEquals import kotlin.test.assertNotNull class NodeStatePersistenceTests { - @Test fun `persistent state survives node restart`() { // Temporary disable this test when executed on Windows. It is known to be sporadically failing. // More investigation is needed to establish why. - Assume.assumeFalse(System.getProperty("os.name").toLowerCase().startsWith("win")) + assumeFalse(System.getProperty("os.name").toLowerCase().startsWith("win")) val user = User("mark", "dadada", setOf(startFlow(), invokeRpc("vaultQuery"))) val message = Message("Hello world!") driver(isDebug = true, startNodesInProcess = isQuasarAgentSpecified()) { - val (nodeName, notaryNodeHandle) = { - val notaryNodeHandle = startNotaryNode(DUMMY_NOTARY.name, validating = false).getOrThrow() + val nodeName = { + startNotaryNode(DUMMY_NOTARY.name, validating = false).getOrThrow() val nodeHandle = startNode(rpcUsers = listOf(user)).getOrThrow() val nodeName = nodeHandle.nodeInfo.chooseIdentity().name nodeHandle.rpcClientToNode().start(user.username, user.password).use { it.proxy.startFlow(::SendMessageFlow, message).returnValue.getOrThrow() } nodeHandle.stop() - nodeName to notaryNodeHandle + nodeName }() val nodeHandle = startNode(providedName = nodeName, rpcUsers = listOf(user)).getOrThrow() 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 b7ff38dc6f..5323cd6a82 100644 --- a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt +++ b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt @@ -7,6 +7,7 @@ import net.corda.confidential.SwapIdentitiesFlow import net.corda.confidential.SwapIdentitiesHandler import net.corda.core.CordaException import net.corda.core.concurrent.CordaFuture +import net.corda.core.crypto.SignedData import net.corda.core.flows.* import net.corda.core.identity.CordaX500Name import net.corda.core.identity.Party @@ -20,6 +21,7 @@ import net.corda.core.node.services.* import net.corda.core.serialization.SerializationWhitelist import net.corda.core.serialization.SerializeAsToken import net.corda.core.serialization.SingletonSerializeAsToken +import net.corda.core.serialization.deserialize import net.corda.core.transactions.SignedTransaction import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.debug @@ -42,7 +44,9 @@ import net.corda.node.services.events.ScheduledActivityObserver import net.corda.node.services.identity.PersistentIdentityService import net.corda.node.services.keys.PersistentKeyManagementService import net.corda.node.services.messaging.MessagingService -import net.corda.node.services.network.* +import net.corda.node.services.network.NetworkMapCacheImpl +import net.corda.node.services.network.NodeInfoWatcher +import net.corda.node.services.network.PersistentNetworkMapCache import net.corda.node.services.persistence.DBCheckpointStorage import net.corda.node.services.persistence.DBTransactionMappingStorage import net.corda.node.services.persistence.DBTransactionStorage @@ -82,18 +86,15 @@ import net.corda.core.crypto.generateKeyPair as cryptoGenerateKeyPair * Marked as SingletonSerializeAsToken to prevent the invisible reference to AbstractNode in the ServiceHub accidentally * sweeping up the Node into the Kryo checkpoint serialization via any flows holding a reference to ServiceHub. */ +// TODO Log warning if this node is a notary but not one of the ones specified in the network parameters, both for core and custom + // In theory the NodeInfo for the node should be passed in, instead, however currently this is constructed by the // AbstractNode. It should be possible to generate the NodeInfo outside of AbstractNode, so it can be passed in. -abstract class AbstractNode(config: NodeConfiguration, +abstract class AbstractNode(val configuration: NodeConfiguration, val platformClock: Clock, protected val versionInfo: VersionInfo, protected val cordappLoader: CordappLoader, - @VisibleForTesting val busyNodeLatch: ReusableLatch = ReusableLatch()) : SingletonSerializeAsToken() { - open val configuration = config.apply { - require(minimumPlatformVersion <= versionInfo.platformVersion) { - "minimumPlatformVersion cannot be greater than the node's own version" - } - } + private val busyNodeLatch: ReusableLatch = ReusableLatch()) : SingletonSerializeAsToken() { private class StartedNodeImpl( override val internals: N, @@ -116,14 +117,12 @@ abstract class AbstractNode(config: NodeConfiguration, // low-performance prototyping period. protected abstract val serverThread: AffinityExecutor + protected lateinit var networkParameters: NetworkParameters private val cordappServices = MutableClassToInstanceMap.create() private val flowFactories = ConcurrentHashMap>, InitiatedFlowFactory<*>>() - protected val partyKeys = mutableSetOf() protected val services: ServiceHubInternal get() = _services private lateinit var _services: ServiceHubInternalImpl - protected lateinit var legalIdentity: PartyAndCertificate - private lateinit var allIdentities: List protected lateinit var info: NodeInfo protected var myNotaryIdentity: PartyAndCertificate? = null protected lateinit var checkpointStorage: CheckpointStorage @@ -157,10 +156,6 @@ abstract class AbstractNode(config: NodeConfiguration, return SecureCordaRPCOps(services, smm, database, flowStarter) } - private fun saveOwnNodeInfo() { - NodeInfoWatcher.saveToFile(configuration.baseDirectory, info, services.keyManagementService) - } - private fun initCertificate() { if (configuration.devMode) { log.warn("Corda node is running in dev mode.") @@ -169,33 +164,34 @@ abstract class AbstractNode(config: NodeConfiguration, validateKeystore() } - private fun makeSchemaService() = NodeSchemaService(cordappLoader) open fun generateNodeInfo() { check(started == null) { "Node has already been started" } - initCertificate() log.info("Generating nodeInfo ...") - val schemaService = makeSchemaService() - initialiseDatabasePersistence(schemaService) { - val transactionStorage = makeTransactionStorage() - makeServices(schemaService, transactionStorage, StateLoaderImpl(transactionStorage)) - saveOwnNodeInfo() - } + initCertificate() + initNodeInfo() } open fun start(): StartedNode { check(started == null) { "Node has already been started" } - initCertificate() log.info("Node starting up ...") - val schemaService = makeSchemaService() + initCertificate() + val keyPairs = initNodeInfo() + readNetworkParameters() + val schemaService = NodeSchemaService(cordappLoader) // Do all of this in a database transaction so anything that might need a connection has one. val startedImpl = initialiseDatabasePersistence(schemaService) { val transactionStorage = makeTransactionStorage() val stateLoader = StateLoaderImpl(transactionStorage) - val services = makeServices(schemaService, transactionStorage, stateLoader) - saveOwnNodeInfo() + val services = makeServices(keyPairs, schemaService, transactionStorage, stateLoader) smm = makeStateMachineManager() val flowStarter = FlowStarterImpl(serverThread, smm) - val schedulerService = NodeSchedulerService(platformClock, this@AbstractNode.database, flowStarter, stateLoader, unfinishedSchedules = busyNodeLatch, serverThread = serverThread) + val schedulerService = NodeSchedulerService( + platformClock, + this@AbstractNode.database, + flowStarter, + stateLoader, + unfinishedSchedules = busyNodeLatch, + serverThread = serverThread) if (serverThread is ExecutorService) { runOnStop += { // We wait here, even though any in-flight messages should have been drained away because the @@ -232,6 +228,35 @@ abstract class AbstractNode(config: NodeConfiguration, } } + private fun initNodeInfo(): Set { + val (identity, identityKeyPair) = obtainIdentity(notaryConfig = null) + val keyPairs = mutableSetOf(identityKeyPair) + + myNotaryIdentity = configuration.notary?.let { + if (it.isClusterConfig) { + val (notaryIdentity, notaryIdentityKeyPair) = obtainIdentity(it) + keyPairs += notaryIdentityKeyPair + notaryIdentity + } else { + // In case of a single notary service myNotaryIdentity will be the node's single identity. + identity + } + } + + info = NodeInfo( + myAddresses(), + setOf(identity, myNotaryIdentity).filterNotNull(), + versionInfo.platformVersion, + platformClock.instant().toEpochMilli() + ) + + NodeInfoWatcher.saveToFile(configuration.baseDirectory, info, identityKeyPair) + + return keyPairs + } + + protected abstract fun myAddresses(): List + protected open fun makeStateMachineManager(): StateMachineManager { return StateMachineManagerImpl( services, @@ -458,23 +483,25 @@ abstract class AbstractNode(config: NodeConfiguration, * Builds node internal, advertised, and plugin services. * Returns a list of tokenizable services to be added to the serialisation context. */ - private fun makeServices(schemaService: SchemaService, transactionStorage: WritableTransactionStorage, stateLoader: StateLoader): MutableList { + private fun makeServices(keyPairs: Set, schemaService: SchemaService, transactionStorage: WritableTransactionStorage, stateLoader: StateLoader): MutableList { checkpointStorage = DBCheckpointStorage() val metrics = MetricRegistry() attachments = NodeAttachmentService(metrics) val cordappProvider = CordappProviderImpl(cordappLoader, attachments) - _services = ServiceHubInternalImpl(schemaService, transactionStorage, stateLoader, MonitoringService(metrics), cordappProvider) - legalIdentity = obtainIdentity(notaryConfig = null) - // TODO We keep only notary identity as additional legalIdentity if we run it on a node . Multiple identities need more design thinking. - myNotaryIdentity = getNotaryIdentity() - allIdentities = listOf(legalIdentity, myNotaryIdentity).filterNotNull() - network = makeMessagingService(legalIdentity) - val addresses = myAddresses() // TODO There is no support for multiple IP addresses yet. - info = NodeInfo(addresses, allIdentities, versionInfo.platformVersion, platformClock.instant().toEpochMilli()) - val networkMapCache = services.networkMapCache + val identityService = makeIdentityService() + val keyManagementService = makeKeyManagementService(identityService, keyPairs) + _services = ServiceHubInternalImpl( + identityService, + keyManagementService, + schemaService, + transactionStorage, + stateLoader, + MonitoringService(metrics), + cordappProvider) + network = makeMessagingService() val tokenizableServices = mutableListOf(attachments, network, services.vaultService, services.keyManagementService, services.identityService, platformClock, - services.auditService, services.monitoringService, networkMapCache, services.schemaService, + services.auditService, services.monitoringService, services.networkMapCache, services.schemaService, services.transactionVerifierService, services.validatedTransactions, services.contractUpgradeService, services, cordappProvider, this) makeNetworkServices(tokenizableServices) @@ -489,12 +516,6 @@ abstract class AbstractNode(config: NodeConfiguration, HibernateObserver.install(services.vaultService.rawUpdates, database.hibernateConfig) } - /** - * Obtain the node's notary identity if it's configured to be one. If part of a distributed notary then this will be - * the distributed identity shared across all the nodes of the cluster. - */ - protected fun getNotaryIdentity(): PartyAndCertificate? = configuration.notary?.let { obtainIdentity(it) } - @VisibleForTesting protected open fun acceptableLiveFiberCountOnStop(): Int = 0 @@ -549,9 +570,6 @@ abstract class AbstractNode(config: NodeConfiguration, } } - /** Return list of node's addresses. It's overridden in MockNetwork as we don't have real addresses for MockNodes. */ - protected abstract fun myAddresses(): List - open protected fun checkNetworkMapIsInitialized() { if (!services.networkMapCache.loadDBSuccess) { // TODO: There should be a consistent approach to configuration error exceptions. @@ -559,8 +577,15 @@ abstract class AbstractNode(config: NodeConfiguration, } } - protected open fun makeKeyManagementService(identityService: IdentityService): KeyManagementService { - return PersistentKeyManagementService(identityService, partyKeys) + protected open fun makeKeyManagementService(identityService: IdentityService, keyPairs: Set): KeyManagementService { + return PersistentKeyManagementService(identityService, keyPairs) + } + + private fun readNetworkParameters() { + val file = configuration.baseDirectory / "network-parameters" + networkParameters = file.readAll().deserialize>().verified() + log.info(networkParameters.toString()) + check(networkParameters.minimumPlatformVersion <= versionInfo.platformVersion) { "Node is too old for the network" } } private fun makeCoreNotaryService(notaryConfig: NotaryConfig): NotaryService { @@ -593,13 +618,13 @@ abstract class AbstractNode(config: NodeConfiguration, } } - protected open fun makeIdentityService(trustRoot: X509Certificate, - clientCa: CertificateAndKeyPair?, - legalIdentity: PartyAndCertificate): IdentityService { - val caCertificates: Array = listOf(legalIdentity.certificate, clientCa?.certificate?.cert) - .filterNotNull() - .toTypedArray() - return PersistentIdentityService(allIdentities, trustRoot = trustRoot, caCertificates = *caCertificates) + private fun makeIdentityService(): IdentityService { + val trustStore = KeyStoreWrapper(configuration.trustStoreFile, configuration.trustStorePassword) + val caKeyStore = KeyStoreWrapper(configuration.nodeKeystore, configuration.keyStorePassword) + val trustRoot = trustStore.getX509Certificate(X509Utilities.CORDA_ROOT_CA) + val clientCa = caKeyStore.certificateAndKeyPair(X509Utilities.CORDA_CLIENT_CA) + val caCertificates = arrayOf(info.legalIdentitiesAndCerts[0].certificate, clientCa.certificate.cert) + return PersistentIdentityService(info.legalIdentitiesAndCerts, trustRoot = trustRoot, caCertificates = *caCertificates) } protected abstract fun makeTransactionVerifierService(): TransactionVerifierService @@ -619,29 +644,23 @@ abstract class AbstractNode(config: NodeConfiguration, _started = null } - protected abstract fun makeMessagingService(legalIdentity: PartyAndCertificate): MessagingService + protected abstract fun makeMessagingService(): MessagingService protected abstract fun startMessagingService(rpcOps: RPCOps) - private fun obtainIdentity(notaryConfig: NotaryConfig?): PartyAndCertificate { + private fun obtainIdentity(notaryConfig: NotaryConfig?): Pair { val keyStore = KeyStoreWrapper(configuration.nodeKeystore, configuration.keyStorePassword) - val (id, singleName) = if (notaryConfig == null) { - // Node's main identity + val (id, singleName) = if (notaryConfig == null || !notaryConfig.isClusterConfig) { + // Node's main identity or if it's a single node notary Pair("identity", myLegalName) } else { val notaryId = notaryConfig.run { NotaryService.constructId(validating, raft != null, bftSMaRt != null, custom) } - if (notaryConfig.bftSMaRt == null && notaryConfig.raft == null) { - // Node's notary identity - Pair(notaryId, myLegalName.copy(commonName = notaryId)) - } else { - // The node is part of a distributed notary whose identity must already be generated beforehand - Pair(notaryId, null) - } + // The node is part of a distributed notary whose identity must already be generated beforehand. + Pair(notaryId, null) } - // TODO: Integrate with Key management service? val privateKeyAlias = "$id-private-key" @@ -653,7 +672,7 @@ abstract class AbstractNode(config: NodeConfiguration, keyStore.signAndSaveNewKeyPair(singleName, privateKeyAlias, generateKeyPair()) } - val (x509Cert, keys) = keyStore.certificateAndKeyPair(privateKeyAlias) + val (x509Cert, keyPair) = keyStore.certificateAndKeyPair(privateKeyAlias) // TODO: Use configuration to indicate composite key should be used instead of public key for the identity. val compositeKeyAlias = "$id-composite-key" @@ -679,8 +698,8 @@ abstract class AbstractNode(config: NodeConfiguration, throw ConfigurationException("The name '$singleName' for $id doesn't match what's in the key store: $subject") } - partyKeys += keys - return PartyAndCertificate(CertificateFactory.getInstance("X509").generateCertPath(certificates)) + val certPath = CertificateFactory.getInstance("X509").generateCertPath(certificates) + return Pair(PartyAndCertificate(certPath), keyPair) } protected open fun generateKeyPair() = cryptoGenerateKeyPair() @@ -689,6 +708,11 @@ abstract class AbstractNode(config: NodeConfiguration, } private inner class ServiceHubInternalImpl( + override val identityService: IdentityService, + // Place the long term identity key in the KMS. Eventually, this is likely going to be separated again because + // the KMS is meant for derived temporary keys used in transactions, and we're not supposed to sign things with + // the identity key. But the infrastructure to make that easy isn't here yet. + override val keyManagementService: KeyManagementService, override val schemaService: SchemaService, override val validatedTransactions: WritableTransactionStorage, private val stateLoader: StateLoader, @@ -699,22 +723,16 @@ abstract class AbstractNode(config: NodeConfiguration, override val stateMachineRecordedTransactionMapping = DBTransactionMappingStorage() override val auditService = DummyAuditService() override val transactionVerifierService by lazy { makeTransactionVerifierService() } - override val networkMapCache by lazy { NetworkMapCacheImpl(PersistentNetworkMapCache(this@AbstractNode.database, this@AbstractNode.configuration), identityService) } + override val networkMapCache by lazy { + NetworkMapCacheImpl( + PersistentNetworkMapCache( + this@AbstractNode.database, + this@AbstractNode.configuration, + networkParameters.notaries), + identityService) + } override val vaultService by lazy { makeVaultService(keyManagementService, stateLoader) } override val contractUpgradeService by lazy { ContractUpgradeServiceImpl() } - - // Place the long term identity key in the KMS. Eventually, this is likely going to be separated again because - // the KMS is meant for derived temporary keys used in transactions, and we're not supposed to sign things with - // the identity key. But the infrastructure to make that easy isn't here yet. - override val keyManagementService by lazy { makeKeyManagementService(identityService) } - override val identityService by lazy { - val trustStore = KeyStoreWrapper(configuration.trustStoreFile, configuration.trustStorePassword) - val caKeyStore = KeyStoreWrapper(configuration.nodeKeystore, configuration.keyStorePassword) - makeIdentityService( - trustStore.getX509Certificate(X509Utilities.CORDA_ROOT_CA), - caKeyStore.certificateAndKeyPair(X509Utilities.CORDA_CLIENT_CA), - legalIdentity) - } override val attachments: AttachmentStorage get() = this@AbstractNode.attachments override val networkService: MessagingService get() = network override val clock: Clock get() = platformClock diff --git a/node/src/main/kotlin/net/corda/node/internal/Node.kt b/node/src/main/kotlin/net/corda/node/internal/Node.kt index 4bee6df1f4..bef75a6966 100644 --- a/node/src/main/kotlin/net/corda/node/internal/Node.kt +++ b/node/src/main/kotlin/net/corda/node/internal/Node.kt @@ -4,7 +4,6 @@ import com.codahale.metrics.JmxReporter import net.corda.core.CordaException import net.corda.core.concurrent.CordaFuture import net.corda.core.identity.CordaX500Name -import net.corda.core.identity.PartyAndCertificate import net.corda.core.internal.concurrent.openFuture import net.corda.core.internal.concurrent.thenMatch import net.corda.core.internal.uncheckedCast @@ -45,7 +44,7 @@ import kotlin.system.exitProcess * * @param configuration This is typically loaded from a TypeSafe HOCON configuration file. */ -open class Node(override val configuration: NodeConfiguration, +open class Node(configuration: NodeConfiguration, versionInfo: VersionInfo, val initialiseSerialization: Boolean = true, cordappLoader: CordappLoader = makeCordappLoader(configuration) @@ -131,11 +130,11 @@ open class Node(override val configuration: NodeConfiguration, private lateinit var userService: RPCUserService - override fun makeMessagingService(legalIdentity: PartyAndCertificate): MessagingService { + override fun makeMessagingService(): MessagingService { userService = RPCUserServiceImpl(configuration.rpcUsers) val serverAddress = configuration.messagingServerAddress ?: makeLocalMessageBroker() - val advertisedAddress = configuration.messagingServerAddress ?: getAdvertisedAddress() + val advertisedAddress = info.addresses.single() printBasicNodeInfo("Incoming connection address", advertisedAddress.toString()) @@ -143,7 +142,7 @@ open class Node(override val configuration: NodeConfiguration, configuration, versionInfo, serverAddress, - legalIdentity.owningKey, + info.legalIdentities[0].owningKey, serverThread, database, services.monitoringService, @@ -157,14 +156,18 @@ open class Node(override val configuration: NodeConfiguration, } } + override fun myAddresses(): List { + return listOf(configuration.messagingServerAddress ?: getAdvertisedAddress()) + } + private fun getAdvertisedAddress(): NetworkHostAndPort { return with(configuration) { - val useHost = if (detectPublicIp) { + val host = if (detectPublicIp) { tryDetectIfNotPublicHost(p2pAddress.host) ?: p2pAddress.host } else { p2pAddress.host } - NetworkHostAndPort(useHost, p2pAddress.port) + NetworkHostAndPort(host, p2pAddress.port) } } @@ -196,11 +199,6 @@ open class Node(override val configuration: NodeConfiguration, (network as NodeMessagingClient).start(rpcOps, userService) } - override fun myAddresses(): List { - val address = network.myAddress as ArtemisMessagingComponent.ArtemisPeerAddress - return listOf(address.hostAndPort) - } - /** * If the node is persisting to an embedded H2 database, then expose this via TCP with a JDBC URL of the form: * jdbc:h2:tcp://:/node diff --git a/node/src/main/kotlin/net/corda/node/services/config/NodeConfiguration.kt b/node/src/main/kotlin/net/corda/node/services/config/NodeConfiguration.kt index 2b09e9dc33..e0d1cc1209 100644 --- a/node/src/main/kotlin/net/corda/node/services/config/NodeConfiguration.kt +++ b/node/src/main/kotlin/net/corda/node/services/config/NodeConfiguration.kt @@ -18,7 +18,6 @@ interface NodeConfiguration : NodeSSLConfiguration { // myLegalName should be only used in the initial network registration, we should use the name from the certificate instead of this. // TODO: Remove this so we don't accidentally use this identity in the code? val myLegalName: CordaX500Name - val minimumPlatformVersion: Int val emailAddress: String val exportJMXto: String val dataSourceProperties: Properties @@ -52,15 +51,17 @@ data class NotaryConfig(val validating: Boolean, "raft, bftSMaRt, and custom configs cannot be specified together" } } + val isClusterConfig: Boolean get() = raft != null || bftSMaRt != null } data class RaftConfig(val nodeAddress: NetworkHostAndPort, val clusterAddresses: List) /** @param exposeRaces for testing only, so its default is not in reference.conf but here. */ -data class BFTSMaRtConfiguration constructor(val replicaId: Int, - val clusterAddresses: List, - val debug: Boolean = false, - val exposeRaces: Boolean = false +data class BFTSMaRtConfiguration( + val replicaId: Int, + val clusterAddresses: List, + val debug: Boolean = false, + val exposeRaces: Boolean = false ) { init { require(replicaId >= 0) { "replicaId cannot be negative" } @@ -85,7 +86,6 @@ data class NodeConfigurationImpl( override val dataSourceProperties: Properties, override val database: Properties?, override val certificateSigningService: URL, - override val minimumPlatformVersion: Int = 1, override val rpcUsers: List, override val verifierType: VerifierType, // TODO typesafe config supports the notion of durations. Make use of that by mapping it to java.time.Duration. @@ -113,8 +113,6 @@ data class NodeConfigurationImpl( // This is a sanity feature do not remove. require(!useTestClock || devMode) { "Cannot use test clock outside of dev mode" } require(devModeOptions == null || devMode) { "Cannot use devModeOptions outside of dev mode" } - require(myLegalName.commonName == null) { "Common name must be null: $myLegalName" } - require(minimumPlatformVersion >= 1) { "minimumPlatformVersion cannot be less than 1" } } } diff --git a/node/src/main/kotlin/net/corda/node/services/network/NodeInfoWatcher.kt b/node/src/main/kotlin/net/corda/node/services/network/NodeInfoWatcher.kt index 4bd9c72cb4..32dfecee97 100644 --- a/node/src/main/kotlin/net/corda/node/services/network/NodeInfoWatcher.kt +++ b/node/src/main/kotlin/net/corda/node/services/network/NodeInfoWatcher.kt @@ -2,9 +2,9 @@ package net.corda.node.services.network import net.corda.cordform.CordformNode import net.corda.core.crypto.SignedData +import net.corda.core.crypto.sign import net.corda.core.internal.* import net.corda.core.node.NodeInfo -import net.corda.core.node.services.KeyManagementService import net.corda.core.serialization.deserialize import net.corda.core.serialization.serialize import net.corda.core.utilities.loggerFor @@ -15,6 +15,7 @@ import rx.Scheduler import rx.schedulers.Schedulers import java.io.IOException import java.nio.file.Path +import java.security.KeyPair import java.util.concurrent.TimeUnit import kotlin.streams.toList @@ -48,13 +49,13 @@ class NodeInfoWatcher(private val nodePath: Path, * * @param path the path where to write the file, if non-existent it will be created. * @param nodeInfo the NodeInfo to serialize. - * @param keyManager a KeyManagementService used to sign the NodeInfo data. + * @param signingKey used to sign the NodeInfo data. */ - fun saveToFile(path: Path, nodeInfo: NodeInfo, keyManager: KeyManagementService) { + fun saveToFile(path: Path, nodeInfo: NodeInfo, signingKey: KeyPair) { try { path.createDirectories() val serializedBytes = nodeInfo.serialize() - val regSig = keyManager.sign(serializedBytes.bytes, nodeInfo.legalIdentities.first().owningKey) + val regSig = signingKey.sign(serializedBytes.bytes) val signedData = SignedData(serializedBytes, regSig) signedData.serialize().open().copyTo( path / "${NodeInfoFilesCopier.NODE_INFO_FILE_NAME_PREFIX}${serializedBytes.hash}") diff --git a/node/src/main/kotlin/net/corda/node/services/network/PersistentNetworkMapCache.kt b/node/src/main/kotlin/net/corda/node/services/network/PersistentNetworkMapCache.kt index 528fe6a226..0af7cb2e1c 100644 --- a/node/src/main/kotlin/net/corda/node/services/network/PersistentNetworkMapCache.kt +++ b/node/src/main/kotlin/net/corda/node/services/network/PersistentNetworkMapCache.kt @@ -10,9 +10,9 @@ import net.corda.core.internal.bufferUntilSubscribed import net.corda.core.internal.concurrent.openFuture import net.corda.core.messaging.DataFeed import net.corda.core.node.NodeInfo +import net.corda.core.node.NotaryInfo import net.corda.core.node.services.IdentityService import net.corda.core.node.services.NetworkMapCache.MapChange -import net.corda.core.node.services.NotaryService import net.corda.core.node.services.PartyInfo import net.corda.core.schemas.NodeInfoSchemaV1 import net.corda.core.serialization.SingletonSerializeAsToken @@ -32,7 +32,10 @@ import java.util.* import javax.annotation.concurrent.ThreadSafe import kotlin.collections.HashMap -class NetworkMapCacheImpl(networkMapCacheBase: NetworkMapCacheBaseInternal, private val identityService: IdentityService) : NetworkMapCacheBaseInternal by networkMapCacheBase, NetworkMapCacheInternal { +class NetworkMapCacheImpl( + networkMapCacheBase: NetworkMapCacheBaseInternal, + private val identityService: IdentityService +) : NetworkMapCacheBaseInternal by networkMapCacheBase, NetworkMapCacheInternal { init { networkMapCacheBase.allNodes.forEach { it.legalIdentitiesAndCerts.forEach { identityService.verifyAndRegisterIdentity(it) } } networkMapCacheBase.changed.subscribe { mapChange -> @@ -57,13 +60,16 @@ class NetworkMapCacheImpl(networkMapCacheBase: NetworkMapCacheBaseInternal, priv * Extremely simple in-memory cache of the network map. */ @ThreadSafe -open class PersistentNetworkMapCache(private val database: CordaPersistence, configuration: NodeConfiguration) : SingletonSerializeAsToken(), NetworkMapCacheBaseInternal { +open class PersistentNetworkMapCache( + private val database: CordaPersistence, + val configuration: NodeConfiguration, + notaries: List +) : SingletonSerializeAsToken(), NetworkMapCacheBaseInternal { companion object { val logger = loggerFor() } - // TODO Small explanation, partyNodes and registeredNodes is left in memory as it was before, because it will be removed in - // next PR that gets rid of services. These maps are used only for queries by service. + // TODO Cleanup registered and party nodes protected val registeredNodes: MutableMap = Collections.synchronizedMap(HashMap()) protected val partyNodes: MutableList get() = registeredNodes.map { it.value }.toMutableList() private val _changed = PublishSubject.create() @@ -77,22 +83,9 @@ open class PersistentNetworkMapCache(private val database: CordaPersistence, con override val nodeReady: CordaFuture get() = _registrationFuture private var _loadDBSuccess: Boolean = false override val loadDBSuccess get() = _loadDBSuccess - // TODO From the NetworkMapService redesign doc: Remove the concept of network services. - // As a temporary hack, just assume for now that every network has a notary service named "Notary Service" that can be looked up in the map. - // This should eliminate the only required usage of services. - // It is ensured on node startup when constructing a notary that the name contains "notary". - override val notaryIdentities: List - get() { - return partyNodes - .flatMap { - // TODO: validate notary identity certificates before loading into network map cache. - // Notary certificates have to be signed by the doorman directly - it.legalIdentities - } - .filter { it.name.commonName?.startsWith(NotaryService.ID_PREFIX) ?: false } - .toSet() // Distinct, because of distributed service nodes - .sortedBy { it.name.toString() } - } + + override val notaryIdentities: List = notaries.map { it.identity } + private val validatingNotaries = notaries.mapNotNull { if (it.validating) it.identity else null } private val nodeInfoSerializer = NodeInfoWatcher(configuration.baseDirectory, configuration.additionalNodeInfoPollingFrequencyMsec) @@ -107,6 +100,8 @@ open class PersistentNetworkMapCache(private val database: CordaPersistence, con nodeInfoSerializer.nodeInfoUpdates().subscribe { node -> addNode(node) } } + override fun isValidatingNotary(party: Party): Boolean = party in validatingNotaries + override fun getPartyInfo(party: Party): PartyInfo? { val nodes = database.transaction { queryByIdentityKey(session, party.owningKey) } if (nodes.size == 1 && nodes[0].isLegalIdentity(party)) { @@ -286,7 +281,6 @@ open class PersistentNetworkMapCache(private val database: CordaPersistence, con return NodeInfoSchemaV1.PersistentNodeInfo( id = 0, addresses = nodeInfo.addresses.map { NodeInfoSchemaV1.DBHostAndPort.fromHostAndPort(it) }, - // TODO Another ugly hack with special first identity... legalIdentitiesAndCerts = nodeInfo.legalIdentitiesAndCerts.mapIndexed { idx, elem -> NodeInfoSchemaV1.DBPartyAndCertificate(elem, isMain = idx == 0) }, diff --git a/node/src/main/kotlin/net/corda/node/utilities/ServiceIdentityGenerator.kt b/node/src/main/kotlin/net/corda/node/utilities/ServiceIdentityGenerator.kt index e27a3cc8e4..2ff06f1d70 100644 --- a/node/src/main/kotlin/net/corda/node/utilities/ServiceIdentityGenerator.kt +++ b/node/src/main/kotlin/net/corda/node/utilities/ServiceIdentityGenerator.kt @@ -5,10 +5,10 @@ import net.corda.core.crypto.generateKeyPair import net.corda.core.identity.CordaX500Name import net.corda.core.identity.Party import net.corda.core.internal.cert +import net.corda.core.internal.createDirectories import net.corda.core.internal.div import net.corda.core.utilities.loggerFor import net.corda.core.utilities.trace -import java.nio.file.Files import java.nio.file.Path object ServiceIdentityGenerator { @@ -20,13 +20,12 @@ object ServiceIdentityGenerator { * This method should be called *before* any of the nodes are started. * * @param dirs List of node directories to place the generated identity and key pairs in. - * @param serviceId The service id of the distributed service. - * @param serviceName The legal name of the distributed service, with service id as CN. + * @param serviceName The legal name of the distributed service. * @param threshold The threshold for the generated group [CompositeKey]. */ - // TODO: This needs to write out to the key store, not just files on disk fun generateToDisk(dirs: List, serviceName: CordaX500Name, + serviceId: String, threshold: Int = 1): Party { log.trace { "Generating a group identity \"serviceName\" for nodes: ${dirs.joinToString()}" } val keyPairs = (1..dirs.size).map { generateKeyPair() } @@ -39,9 +38,8 @@ object ServiceIdentityGenerator { keyPairs.zip(dirs) { keyPair, dir -> val serviceKeyCert = X509Utilities.createCertificate(CertificateType.CLIENT_CA, issuer.certificate, issuer.keyPair, serviceName, keyPair.public) val compositeKeyCert = X509Utilities.createCertificate(CertificateType.CLIENT_CA, issuer.certificate, issuer.keyPair, serviceName, notaryKey) - val certPath = Files.createDirectories(dir / "certificates") / "distributedService.jks" + val certPath = (dir / "certificates").createDirectories() / "distributedService.jks" val keystore = loadOrCreateKeyStore(certPath, "cordacadevpass") - val serviceId = serviceName.commonName keystore.setCertificateEntry("$serviceId-composite-key", compositeKeyCert.cert) keystore.setKeyEntry("$serviceId-private-key", keyPair.private, "cordacadevkeypass".toCharArray(), arrayOf(serviceKeyCert.cert, issuer.certificate.cert, rootCert)) keystore.save(certPath, "cordacadevpass") diff --git a/node/src/test/kotlin/net/corda/node/CordaRPCOpsImplTest.kt b/node/src/test/kotlin/net/corda/node/CordaRPCOpsImplTest.kt index cae74c44ba..4f2b4cf970 100644 --- a/node/src/test/kotlin/net/corda/node/CordaRPCOpsImplTest.kt +++ b/node/src/test/kotlin/net/corda/node/CordaRPCOpsImplTest.kt @@ -55,22 +55,22 @@ class CordaRPCOpsImplTest { val testJar = "net/corda/node/testing/test.jar" } - lateinit var mockNet: MockNetwork - lateinit var aliceNode: StartedNode - lateinit var notaryNode: StartedNode - lateinit var notary: Party - lateinit var rpc: CordaRPCOps - lateinit var stateMachineUpdates: Observable - lateinit var transactions: Observable - lateinit var vaultTrackCash: Observable> + private lateinit var mockNet: MockNetwork + private lateinit var aliceNode: StartedNode + private lateinit var notaryNode: StartedNode + private lateinit var notary: Party + private lateinit var rpc: CordaRPCOps + private lateinit var stateMachineUpdates: Observable + private lateinit var transactions: Observable + private lateinit var vaultTrackCash: Observable> private val user = User("user", "pwd", permissions = emptySet()) @Before fun setup() { mockNet = MockNetwork(cordappPackages = listOf("net.corda.finance.contracts.asset")) - aliceNode = mockNet.createNode() notaryNode = mockNet.createNotaryNode(validating = false) + aliceNode = mockNet.createNode() rpc = SecureCordaRPCOps(aliceNode.services, aliceNode.smm, aliceNode.database, aliceNode.services) CURRENT_RPC_CONTEXT.set(RpcContext(user)) 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 451b1c2d9c..3158bb7887 100644 --- a/node/src/test/kotlin/net/corda/node/messaging/TwoPartyTradeFlowTests.kt +++ b/node/src/test/kotlin/net/corda/node/messaging/TwoPartyTradeFlowTests.kt @@ -615,18 +615,25 @@ class TwoPartyTradeFlowTests(val anonymous: Boolean) { notaryNode: StartedNode<*>, vararg extraSigningNodes: StartedNode<*>): Map { + val notaryParty = notaryNode.info.legalIdentities[0] val signed = wtxToSign.map { val id = it.id val sigs = mutableListOf() val nodeKey = node.info.chooseIdentity().owningKey - sigs.add(node.services.keyManagementService.sign(SignableData(id, SignatureMetadata(1, Crypto.findSignatureScheme(nodeKey).schemeNumberID)), nodeKey)) - sigs.add(notaryNode.services.keyManagementService.sign(SignableData(id, SignatureMetadata(1, - Crypto.findSignatureScheme(notaryNode.info.legalIdentities[1].owningKey).schemeNumberID)), notaryNode.info.legalIdentities[1].owningKey)) + sigs += node.services.keyManagementService.sign( + SignableData(id, SignatureMetadata(1, Crypto.findSignatureScheme(nodeKey).schemeNumberID)), + nodeKey + ) + sigs += notaryNode.services.keyManagementService.sign( + SignableData(id, SignatureMetadata(1, Crypto.findSignatureScheme(notaryParty.owningKey).schemeNumberID)), + notaryParty.owningKey + ) extraSigningNodes.forEach { currentNode -> - sigs.add(currentNode.services.keyManagementService.sign( - SignableData(id, SignatureMetadata(1, Crypto.findSignatureScheme(currentNode.info.chooseIdentity().owningKey).schemeNumberID)), + sigs += currentNode.services.keyManagementService.sign( + SignableData(id, SignatureMetadata( + 1, + Crypto.findSignatureScheme(currentNode.info.chooseIdentity().owningKey).schemeNumberID)), currentNode.info.chooseIdentity().owningKey) - ) } SignedTransaction(it, sigs) } 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 ee21427fdd..6310e9198d 100644 --- a/node/src/test/kotlin/net/corda/node/services/NotaryChangeTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/NotaryChangeTests.kt @@ -1,5 +1,7 @@ package net.corda.node.services +import com.nhaarman.mockito_kotlin.doReturn +import com.nhaarman.mockito_kotlin.whenever import net.corda.core.contracts.* import net.corda.core.crypto.generateKeyPair import net.corda.core.flows.NotaryChangeFlow @@ -13,8 +15,12 @@ import net.corda.core.transactions.WireTransaction import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.seconds import net.corda.node.internal.StartedNode -import net.corda.testing.* +import net.corda.node.services.config.NotaryConfig +import net.corda.testing.DUMMY_NOTARY +import net.corda.testing.chooseIdentity import net.corda.testing.contracts.DummyContract +import net.corda.testing.dummyCommand +import net.corda.testing.getTestPartyAndCertificate import net.corda.testing.node.MockNetwork import net.corda.testing.node.MockNodeParameters import org.assertj.core.api.Assertions.assertThatExceptionOfType @@ -27,24 +33,32 @@ import kotlin.test.assertEquals import kotlin.test.assertTrue class NotaryChangeTests { - lateinit var mockNet: MockNetwork - lateinit var oldNotaryNode: StartedNode - lateinit var newNotaryNode: StartedNode - lateinit var clientNodeA: StartedNode - lateinit var clientNodeB: StartedNode - lateinit var newNotaryParty: Party - lateinit var oldNotaryParty: Party + private lateinit var mockNet: MockNetwork + private lateinit var oldNotaryNode: StartedNode + private lateinit var clientNodeA: StartedNode + private lateinit var clientNodeB: StartedNode + private lateinit var newNotaryParty: Party + private lateinit var oldNotaryParty: Party @Before fun setUp() { mockNet = MockNetwork(cordappPackages = listOf("net.corda.testing.contracts")) - oldNotaryNode = mockNet.createNotaryNode(MockNodeParameters(legalName = DUMMY_NOTARY.name)) + val (oldNotaryNode, newNotaryNode) = listOf( + createUnstartedNotary(DUMMY_NOTARY.name), + createUnstartedNotary(DUMMY_NOTARY.name.copy(organisation = "Dummy Notary 2")) + ).map { it.start() } + this.oldNotaryNode = oldNotaryNode clientNodeA = mockNet.createNode() clientNodeB = mockNet.createNode() - newNotaryNode = mockNet.createNotaryNode(MockNodeParameters(legalName = DUMMY_NOTARY.name.copy(organisation = "Dummy Notary 2"))) - mockNet.runNetwork() // Clear network map registration messages - oldNotaryParty = newNotaryNode.services.networkMapCache.getNotary(DUMMY_NOTARY_SERVICE_NAME)!! - newNotaryParty = newNotaryNode.services.networkMapCache.getNotary(DUMMY_NOTARY_SERVICE_NAME.copy(organisation = "Dummy Notary 2"))!! + oldNotaryParty = newNotaryNode.services.networkMapCache.getNotary(DUMMY_NOTARY.name)!! + newNotaryParty = newNotaryNode.services.networkMapCache.getNotary(DUMMY_NOTARY.name.copy(organisation = "Dummy Notary 2"))!! + } + + private fun createUnstartedNotary(name: CordaX500Name): MockNetwork.MockNode { + return mockNet.createUnstartedNode(MockNodeParameters( + legalName = name, + configOverrides = { doReturn(NotaryConfig(validating = true)).whenever(it).notary } + )) } @After diff --git a/node/src/test/kotlin/net/corda/node/services/messaging/ArtemisMessagingTests.kt b/node/src/test/kotlin/net/corda/node/services/messaging/ArtemisMessagingTests.kt index d300c932dd..b19125f6e6 100644 --- a/node/src/test/kotlin/net/corda/node/services/messaging/ArtemisMessagingTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/messaging/ArtemisMessagingTests.kt @@ -42,6 +42,7 @@ class ArtemisMessagingTests { companion object { const val TOPIC = "platform.self" } + @Rule @JvmField val testSerialization = SerializationEnvironmentRule() @@ -50,21 +51,21 @@ class ArtemisMessagingTests { @JvmField val temporaryFolder = TemporaryFolder() - val serverPort = freePort() - val rpcPort = freePort() - val identity = generateKeyPair() + private val serverPort = freePort() + private val rpcPort = freePort() + private val identity = generateKeyPair() - lateinit var config: NodeConfiguration - lateinit var database: CordaPersistence - lateinit var userService: RPCUserService - lateinit var networkMapRegistrationFuture: CordaFuture + private lateinit var config: NodeConfiguration + private lateinit var database: CordaPersistence + private lateinit var userService: RPCUserService + private lateinit var networkMapRegistrationFuture: CordaFuture - var messagingClient: NodeMessagingClient? = null - var messagingServer: ArtemisMessagingServer? = null + private var messagingClient: NodeMessagingClient? = null + private var messagingServer: ArtemisMessagingServer? = null - lateinit var networkMapCache: NetworkMapCacheImpl + private lateinit var networkMapCache: NetworkMapCacheImpl - val rpcOps = object : RPCOps { + private val rpcOps = object : RPCOps { override val protocolVersion: Int get() = throw UnsupportedOperationException() } @@ -78,7 +79,7 @@ class ArtemisMessagingTests { LogHelper.setLevel(PersistentUniquenessProvider::class) database = configureDatabase(makeTestDataSourceProperties(), makeTestDatabaseProperties(), ::makeTestIdentityService) networkMapRegistrationFuture = doneFuture(Unit) - networkMapCache = NetworkMapCacheImpl(PersistentNetworkMapCache(database, config), rigorousMock()) + networkMapCache = NetworkMapCacheImpl(PersistentNetworkMapCache(database, config, emptyList()), rigorousMock()) } @After 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 6d478a8f03..de47655371 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 @@ -72,18 +72,14 @@ class FlowFrameworkTests { @Before fun start() { - mockNet = MockNetwork(servicePeerAllocationStrategy = RoundRobin(), cordappPackages = listOf("net.corda.finance.contracts", "net.corda.testing.contracts")) + mockNet = MockNetwork( + servicePeerAllocationStrategy = RoundRobin(), + cordappPackages = listOf("net.corda.finance.contracts", "net.corda.testing.contracts")) + val notary = mockNet.createNotaryNode() aliceNode = mockNet.createNode(MockNodeParameters(legalName = ALICE_NAME)) bobNode = mockNet.createNode(MockNodeParameters(legalName = BOB_NAME)) - mockNet.runNetwork() - - // We intentionally create our own notary and ignore the one provided by the network - // Note that these notaries don't operate correctly as they don't share their state. They are only used for testing - // service addressing. - val notary = mockNet.createNotaryNode() receivedSessionMessagesObservable().forEach { receivedSessionMessages += it } - mockNet.runNetwork() // Extract identities alice = aliceNode.info.singleIdentity() 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 210b62c4fa..7beb578048 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 @@ -27,10 +27,10 @@ class AttachmentDemoTest { invokeRpc(CordaRPCOps::wellKnownPartyFromX500Name), invokeRpc(CordaRPCOps::internalVerifiedTransactionsFeed) ))) - val (nodeA, nodeB) = listOf( + val (_, nodeA, nodeB) = listOf( + startNotaryNode(DUMMY_NOTARY.name, validating = false), startNode(providedName = DUMMY_BANK_A.name, rpcUsers = demoUser, maximumHeapSize = "1g"), - startNode(providedName = DUMMY_BANK_B.name, rpcUsers = demoUser, maximumHeapSize = "1g"), - startNotaryNode(DUMMY_NOTARY.name, validating = false)) + startNode(providedName = DUMMY_BANK_B.name, rpcUsers = demoUser, maximumHeapSize = "1g")) .map { it.getOrThrow() } startWebserver(nodeB).getOrThrow() diff --git a/samples/bank-of-corda-demo/src/integration-test/kotlin/net/corda/bank/BankOfCordaHttpAPITest.kt b/samples/bank-of-corda-demo/src/integration-test/kotlin/net/corda/bank/BankOfCordaHttpAPITest.kt index 8077105e41..91df6bed2c 100644 --- a/samples/bank-of-corda-demo/src/integration-test/kotlin/net/corda/bank/BankOfCordaHttpAPITest.kt +++ b/samples/bank-of-corda-demo/src/integration-test/kotlin/net/corda/bank/BankOfCordaHttpAPITest.kt @@ -2,23 +2,26 @@ package net.corda.bank import net.corda.bank.api.BankOfCordaClientApi import net.corda.bank.api.BankOfCordaWebApi.IssueRequestParams +import net.corda.core.internal.concurrent.transpose import net.corda.core.utilities.getOrThrow import net.corda.testing.BOC +import net.corda.testing.DUMMY_NOTARY import net.corda.testing.driver.driver -import net.corda.testing.notary import org.junit.Test import kotlin.test.assertTrue class BankOfCordaHttpAPITest { @Test fun `issuer flow via Http`() { - driver(extraCordappPackagesToScan = listOf("net.corda.finance"), dsl = { - val bigCorpNodeFuture = startNode(providedName = BIGCORP_LEGAL_NAME) - val nodeBankOfCordaFuture = startNotaryNode(BOC.name, validating = false) - val (nodeBankOfCorda) = listOf(nodeBankOfCordaFuture, bigCorpNodeFuture).map { it.getOrThrow() } - val nodeBankOfCordaApiAddr = startWebserver(nodeBankOfCorda).getOrThrow().listenAddress - val notaryName = notary().node.nodeInfo.legalIdentities[1].name - assertTrue(BankOfCordaClientApi(nodeBankOfCordaApiAddr).requestWebIssue(IssueRequestParams(1000, "USD", BIGCORP_LEGAL_NAME, "1", BOC.name, notaryName))) - }, isDebug = true) + driver(isDebug = true, extraCordappPackagesToScan = listOf("net.corda.finance")) { + val (_, bocNode) = listOf( + startNotaryNode(providedName = DUMMY_NOTARY.name), + startNode(providedName = BOC.name), + startNode(providedName = BIGCORP_LEGAL_NAME) + ).transpose().getOrThrow() + val bocApiAddress = startWebserver(bocNode).getOrThrow().listenAddress + val issueRequestParams = IssueRequestParams(1000, "USD", BIGCORP_LEGAL_NAME, "1", BOC.name, DUMMY_NOTARY.name) + assertTrue(BankOfCordaClientApi(bocApiAddress).requestWebIssue(issueRequestParams)) + } } } 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 83d3f0858f..0b77d4c865 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 @@ -28,9 +28,10 @@ class BankOfCordaRPCClientTest { val bocManager = User("bocManager", "password1", permissions = setOf( startFlow()) + commonPermissions) val bigCorpCFO = User("bigCorpCFO", "password2", permissions = emptySet() + commonPermissions) - val nodeBankOfCordaFuture = startNotaryNode(BOC.name, rpcUsers = listOf(bocManager), validating = false) - val nodeBigCorporationFuture = startNode(providedName = BIGCORP_LEGAL_NAME, rpcUsers = listOf(bigCorpCFO)) - val (nodeBankOfCorda, nodeBigCorporation) = listOf(nodeBankOfCordaFuture, nodeBigCorporationFuture).map { it.getOrThrow() } + val (nodeBankOfCorda, nodeBigCorporation) = listOf( + startNotaryNode(BOC.name, rpcUsers = listOf(bocManager), validating = false), + startNode(providedName = BIGCORP_LEGAL_NAME, rpcUsers = listOf(bigCorpCFO)) + ).map { it.getOrThrow() } // Bank of Corda RPC Client val bocClient = nodeBankOfCorda.rpcClientToNode() diff --git a/samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/BankOfCordaDriver.kt b/samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/BankOfCordaDriver.kt index d7ad085a9d..4faac3f3a9 100644 --- a/samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/BankOfCordaDriver.kt +++ b/samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/BankOfCordaDriver.kt @@ -57,7 +57,7 @@ private class BankOfCordaDriver { try { when (role) { Role.ISSUER -> { - driver(dsl = { + driver(isDebug = true, extraCordappPackagesToScan = listOf("net.corda.finance.contracts.asset")) { startNotaryNode(providedName = DUMMY_NOTARY.name, validating = true) val bankUser = User( BANK_USERNAME, @@ -79,7 +79,7 @@ private class BankOfCordaDriver { startNode(providedName = BIGCORP_LEGAL_NAME, rpcUsers = listOf(bigCorpUser)) startWebserver(bankOfCorda.get()) waitForAllNodesToFinish() - }, isDebug = true, extraCordappPackagesToScan = listOf("net.corda.finance.contracts.asset")) + } } else -> { val requestParams = IssueRequestParams(options.valueOf(quantity), options.valueOf(currency), BIGCORP_LEGAL_NAME, diff --git a/samples/irs-demo/cordapp/src/test/kotlin/net/corda/irs/api/NodeInterestRatesTest.kt b/samples/irs-demo/cordapp/src/test/kotlin/net/corda/irs/api/NodeInterestRatesTest.kt index 2b286ceaab..193eb8ec08 100644 --- a/samples/irs-demo/cordapp/src/test/kotlin/net/corda/irs/api/NodeInterestRatesTest.kt +++ b/samples/irs-demo/cordapp/src/test/kotlin/net/corda/irs/api/NodeInterestRatesTest.kt @@ -205,7 +205,7 @@ class NodeInterestRatesTest { @Test fun `network tearoff`() { val mockNet = MockNetwork(initialiseSerialization = false, cordappPackages = listOf("net.corda.finance.contracts", "net.corda.irs")) - val n1 = mockNet.createNotaryNode() + val aliceNode = mockNet.createPartyNode(ALICE.name) val oracleNode = mockNet.createNode().apply { internals.registerInitiatedFlow(NodeInterestRates.FixQueryHandler::class.java) internals.registerInitiatedFlow(NodeInterestRates.FixSignHandler::class.java) @@ -218,7 +218,7 @@ class NodeInterestRatesTest { val flow = FilteredRatesFlow(tx, oracleNode.info.chooseIdentity(), fixOf, BigDecimal("0.675"), BigDecimal("0.1")) LogHelper.setLevel("rates") mockNet.runNetwork() - val future = n1.services.startFlow(flow).resultFuture + val future = aliceNode.services.startFlow(flow).resultFuture mockNet.runNetwork() future.getOrThrow() // We should now have a valid fix of our tx from the oracle. diff --git a/samples/network-visualiser/src/main/kotlin/net/corda/netmap/simulation/IRSSimulation.kt b/samples/network-visualiser/src/main/kotlin/net/corda/netmap/simulation/IRSSimulation.kt index 59f6d525ee..300a173bf3 100644 --- a/samples/network-visualiser/src/main/kotlin/net/corda/netmap/simulation/IRSSimulation.kt +++ b/samples/network-visualiser/src/main/kotlin/net/corda/netmap/simulation/IRSSimulation.kt @@ -140,7 +140,7 @@ class IRSSimulation(networkSendManuallyPumped: Boolean, runAsync: Boolean, laten node1.internals.registerInitiatedFlow(FixingFlow.Fixer::class.java) node2.internals.registerInitiatedFlow(FixingFlow.Fixer::class.java) - val notaryId = notary.info.legalIdentities[1] + val notaryId = notary.info.legalIdentities[0] @InitiatingFlow class StartDealFlow(val otherParty: Party, diff --git a/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/BFTNotaryCordform.kt b/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/BFTNotaryCordform.kt index b3e295d1cb..cb0be14ee0 100644 --- a/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/BFTNotaryCordform.kt +++ b/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/BFTNotaryCordform.kt @@ -5,6 +5,7 @@ import net.corda.cordform.CordformDefinition import net.corda.cordform.CordformNode import net.corda.core.identity.CordaX500Name import net.corda.core.internal.div +import net.corda.core.node.services.NotaryService import net.corda.core.utilities.NetworkHostAndPort import net.corda.node.services.config.BFTSMaRtConfiguration import net.corda.node.services.config.NotaryConfig @@ -13,11 +14,7 @@ import net.corda.node.services.transactions.minCorrectReplicas import net.corda.node.utilities.ServiceIdentityGenerator import net.corda.testing.ALICE import net.corda.testing.BOB -import net.corda.testing.internal.demorun.name -import net.corda.testing.internal.demorun.node -import net.corda.testing.internal.demorun.notary -import net.corda.testing.internal.demorun.rpcUsers -import net.corda.testing.internal.demorun.runNodes +import net.corda.testing.internal.demorun.* fun main(args: Array) = BFTNotaryCordform().runNodes() @@ -66,6 +63,10 @@ class BFTNotaryCordform : CordformDefinition("build" / "notary-demo-nodes") { } override fun setup(context: CordformContext) { - ServiceIdentityGenerator.generateToDisk(notaryNames.map { context.baseDirectory(it.toString()) }, clusterName, minCorrectReplicas(clusterSize)) + ServiceIdentityGenerator.generateToDisk( + notaryNames.map { context.baseDirectory(it.toString()) }, + clusterName, + NotaryService.constructId(validating = false, bft = true), + minCorrectReplicas(clusterSize)) } } diff --git a/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/RaftNotaryCordform.kt b/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/RaftNotaryCordform.kt index ff128c6edf..677ec08a9a 100644 --- a/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/RaftNotaryCordform.kt +++ b/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/RaftNotaryCordform.kt @@ -5,6 +5,7 @@ import net.corda.cordform.CordformDefinition import net.corda.cordform.CordformNode import net.corda.core.identity.CordaX500Name import net.corda.core.internal.div +import net.corda.core.node.services.NotaryService import net.corda.core.utilities.NetworkHostAndPort import net.corda.node.services.config.NotaryConfig import net.corda.node.services.config.RaftConfig @@ -58,6 +59,9 @@ class RaftNotaryCordform : CordformDefinition("build" / "notary-demo-nodes") { } override fun setup(context: CordformContext) { - ServiceIdentityGenerator.generateToDisk(notaryNames.map { context.baseDirectory(it.toString()) }, clusterName) + ServiceIdentityGenerator.generateToDisk( + notaryNames.map { context.baseDirectory(it.toString()) }, + clusterName, + NotaryService.constructId(validating = true, raft = true)) } } 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 df4c1ec6db..51cbce94b3 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 @@ -30,12 +30,12 @@ class TraderDemoTest { startFlow(), all())) driver(startNodesInProcess = true, extraCordappPackagesToScan = listOf("net.corda.finance")) { - val (nodeA, nodeB, bankNode) = listOf( + val (_, nodeA, nodeB, bankNode) = listOf( + startNotaryNode(DUMMY_NOTARY.name, validating = false), 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)), - startNotaryNode(DUMMY_NOTARY.name, validating = false)) - .map { (it.getOrThrow() as NodeHandle.InProcess).node } + startNode(providedName = BOC.name, rpcUsers = listOf(bankUser)) + ).map { (it.getOrThrow() as NodeHandle.InProcess).node } nodeA.internals.registerInitiatedFlow(BuyerFlow::class.java) val (nodeARpc, nodeBRpc) = listOf(nodeA, nodeB).map { diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/DriverConstants.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/DriverConstants.kt index 73e36aba5e..5f872987dd 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/DriverConstants.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/DriverConstants.kt @@ -15,7 +15,7 @@ import net.corda.testing.driver.DriverDSLExposedInterface /** * A simple wrapper for objects provided by the integration test driver DSL. The fields are lazy so * node construction won't start until you access the members. You can get one of these from the - * [alice], [bob] and [aliceBobAndNotary] functions. + * [alice], [bob] and [notaryAliceAndBob] functions. */ class PredefinedTestNode internal constructor(party: Party, driver: DriverDSLExposedInterface, ifNotaryIsValidating: Boolean?) { val rpcUsers = listOf(User("admin", "admin", setOf("ALL"))) // TODO: Randomize? @@ -56,10 +56,10 @@ fun DriverDSLExposedInterface.notary(): PredefinedTestNode = PredefinedTestNode( * Returns plain, entirely stock nodes pre-configured with the [ALICE], [BOB] and [DUMMY_NOTARY] X.500 names in that * order. They have been started up in parallel and are now ready to use. */ -fun DriverDSLExposedInterface.aliceBobAndNotary(): List { +fun DriverDSLExposedInterface.notaryAliceAndBob(): List { + val notary = notary() val alice = alice() val bob = bob() - val notary = notary() - listOf(alice.nodeFuture, bob.nodeFuture, notary.nodeFuture).transpose().get() + listOf(notary.nodeFuture, alice.nodeFuture, bob.nodeFuture).transpose().get() return listOf(alice, bob, notary) } diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/NodeTestUtils.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/NodeTestUtils.kt index eac60d7e03..24ee8e0341 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/NodeTestUtils.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/NodeTestUtils.kt @@ -61,7 +61,6 @@ fun testNodeConfiguration( return rigorousMock().also { doReturn(baseDirectory).whenever(it).baseDirectory doReturn(myLegalName).whenever(it).myLegalName - doReturn(1).whenever(it).minimumPlatformVersion doReturn("cordacadevpass").whenever(it).keyStorePassword doReturn("trustpass").whenever(it).trustStorePassword doReturn(emptyList()).whenever(it).rpcUsers 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 127d010f31..2183cb8ac7 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 @@ -13,13 +13,13 @@ import net.corda.core.concurrent.CordaFuture import net.corda.core.concurrent.firstOf import net.corda.core.identity.CordaX500Name import net.corda.core.identity.Party -import net.corda.core.internal.ThreadBox +import net.corda.core.internal.* import net.corda.core.internal.concurrent.* -import net.corda.core.internal.div -import net.corda.core.internal.times import net.corda.core.messaging.CordaRPCOps import net.corda.core.node.NodeInfo +import net.corda.core.node.NotaryInfo import net.corda.core.node.services.NetworkMapCache +import net.corda.core.node.services.NotaryService import net.corda.core.toFuture import net.corda.core.utilities.* import net.corda.node.internal.Node @@ -36,6 +36,8 @@ import net.corda.nodeapi.internal.addShutdownHook import net.corda.testing.ALICE import net.corda.testing.BOB import net.corda.testing.DUMMY_BANK_A +import net.corda.testing.common.internal.NetworkParametersCopier +import net.corda.testing.common.internal.testNetworkParameters import net.corda.testing.initialiseTestSerialization import net.corda.testing.node.MockServices.Companion.MOCK_VERSION_INFO import okhttp3.OkHttpClient @@ -43,10 +45,10 @@ import okhttp3.Request import org.slf4j.Logger import rx.Observable import rx.observables.ConnectableObservable -import java.io.File import java.net.* import java.nio.file.Path import java.nio.file.Paths +import java.nio.file.StandardCopyOption.REPLACE_EXISTING import java.time.Duration import java.time.Instant import java.time.ZoneOffset.UTC @@ -59,6 +61,7 @@ import java.util.concurrent.TimeUnit.MILLISECONDS import java.util.concurrent.TimeUnit.SECONDS import java.util.concurrent.TimeoutException import java.util.concurrent.atomic.AtomicInteger +import kotlin.collections.ArrayList import kotlin.concurrent.thread @@ -69,7 +72,6 @@ import kotlin.concurrent.thread * * TODO this file is getting way too big, it should be split into several files. */ - private val log: Logger = loggerFor() private val DEFAULT_POLL_INTERVAL = 500.millis @@ -87,7 +89,7 @@ private val DRIVER_REQUIRED_PERMISSIONS = setOf( */ interface DriverDSLExposedInterface : CordformContext { /** - * Starts a [net.corda.node.internal.Node] in a separate process. + * Start a node. * * @param defaultParameters The default parameters for the node. Allows the node to be configured in builder style * when called from Java code. @@ -97,7 +99,7 @@ interface DriverDSLExposedInterface : CordformContext { * @param rpcUsers List of users who are authorised to use the RPC system. Defaults to empty list. * @param startInSameProcess Determines if the node should be started inside the same process the Driver is running * in. If null the Driver-level value will be used. - * @return The [NodeInfo] of the started up node retrieved from the network map service. + * @return A [CordaFuture] on the [NodeHandle] to the node. The future will complete when the node is available. */ fun startNode( defaultParameters: NodeParameters = NodeParameters(), @@ -113,7 +115,6 @@ interface DriverDSLExposedInterface : CordformContext { rpcUsers: List = emptyList(), verifierType: VerifierType = VerifierType.InMemory, customOverrides: Map = emptyMap(), - //TODO Switch the default value validating: Boolean = true): CordaFuture /** @@ -122,9 +123,7 @@ interface DriverDSLExposedInterface : CordformContext { * @param parameters The default parameters for the driver. * @return The value returned in the [dsl] closure. */ - fun startNode(parameters: NodeParameters): CordaFuture { - return startNode(defaultParameters = parameters) - } + fun startNode(parameters: NodeParameters): CordaFuture = startNode(defaultParameters = parameters) fun startNodes( nodes: List, @@ -323,7 +322,6 @@ fun driver( systemProperties: Map = defaultParameters.systemProperties, useTestClock: Boolean = defaultParameters.useTestClock, initialiseSerialization: Boolean = defaultParameters.initialiseSerialization, - startNodesInProcess: Boolean = defaultParameters.startNodesInProcess, extraCordappPackagesToScan: List = defaultParameters.extraCordappPackagesToScan, dsl: DriverDSLExposedInterface.() -> A @@ -466,7 +464,8 @@ fun getTimestampAsDirectoryName(): String { return DateTimeFormatter.ofPattern("yyyyMMddHHmmss").withZone(UTC).format(Instant.now()) } -class ListenProcessDeathException(hostAndPort: NetworkHostAndPort, listenProcess: Process) : CordaException("The process that was expected to listen on $hostAndPort has died with status: ${listenProcess.exitValue()}") +class ListenProcessDeathException(hostAndPort: NetworkHostAndPort, listenProcess: Process) : + CordaException("The process that was expected to listen on $hostAndPort has died with status: ${listenProcess.exitValue()}") /** * @throws ListenProcessDeathException if [listenProcess] dies before the check succeeds, i.e. the check can't succeed as intended. @@ -657,6 +656,7 @@ class DriverDSL( private val nodeInfoFilesCopier = NodeInfoFilesCopier() // Map from a nodes legal name to an observable emitting the number of nodes in its network map. private val countObservables = mutableMapOf>() + private var networkParameters: NetworkParametersCopier? = null class State { val processes = ArrayList>() @@ -749,6 +749,7 @@ class DriverDSL( verifierType: VerifierType, customOverrides: Map, validating: Boolean): CordaFuture { + createNetworkParameters(listOf(providedName), providedName, validating, "identity") val config = customOverrides + NotaryConfig(validating).toConfigMap() return startNode(providedName = providedName, rpcUsers = rpcUsers, verifierType = verifierType, customOverrides = config) } @@ -788,21 +789,28 @@ class DriverDSL( return config.toConfigMap() } - val nodeNames = (0 until clusterSize).map { CordaX500Name("Notary Service $it", "Zurich", "CH") } - val paths = nodeNames.map { baseDirectory(it) } - ServiceIdentityGenerator.generateToDisk(paths, notaryName) + require(clusterSize > 0) + + val nodeNames = (0 until clusterSize).map { notaryName.copy(organisation = "${notaryName.organisation}-$it") } + val notaryIdentity = createNetworkParameters( + nodeNames, + notaryName, + validating = true, + serviceId = NotaryService.constructId(validating = true, raft = true)) + val clusterAddress = portAllocation.nextHostAndPort() // Start the first node that will bootstrap the cluster val firstNotaryFuture = startNode( - providedName = nodeNames.first(), + providedName = nodeNames[0], rpcUsers = rpcUsers, verifierType = verifierType, customOverrides = notaryConfig(clusterAddress) + mapOf( - "database.serverNameTablePrefix" to if (nodeNames.isNotEmpty()) nodeNames.first().toString().replace(Regex("[^0-9A-Za-z]+"), "") else "" + "database.serverNameTablePrefix" to nodeNames[0].toString().replace(Regex("[^0-9A-Za-z]+"), "") ), startInSameProcess = startInSameProcess ) + // All other nodes will join the cluster val restNotaryFutures = nodeNames.drop(1).map { val nodeAddress = portAllocation.nextHostAndPort() @@ -816,13 +824,20 @@ class DriverDSL( } return firstNotaryFuture.flatMap { firstNotary -> - val notaryParty = firstNotary.nodeInfo.legalIdentities[1] // TODO For now the second identity is notary identity. - restNotaryFutures.transpose().map { restNotaries -> - Pair(notaryParty, listOf(firstNotary) + restNotaries) - } + restNotaryFutures.transpose().map { restNotaries -> Pair(notaryIdentity, listOf(firstNotary) + restNotaries) } } } + private fun createNetworkParameters(notaryNodeNames: List, notaryName: CordaX500Name, validating: Boolean, serviceId: String): Party { + check(networkParameters == null) { "Notaries must be started first" } + val identity = ServiceIdentityGenerator.generateToDisk( + notaryNodeNames.map { baseDirectory(it) }, + notaryName, + serviceId) + networkParameters = NetworkParametersCopier(testNetworkParameters(listOf(NotaryInfo(identity, validating)))) + return identity + } + private fun queryWebserver(handle: NodeHandle, process: Process): WebserverHandle { val protocol = if (handle.configuration.useHTTPS) "https://" else "http://" val url = URL("$protocol${handle.webAddress}/api/status") @@ -854,9 +869,8 @@ class DriverDSL( } fun baseDirectory(nodeName: CordaX500Name): Path { - val nodeDirectoryName = String(nodeName.organisation.filter { !it.isWhitespace() }.toCharArray()) + val nodeDirectoryName = nodeName.organisation.filter { !it.isWhitespace() } return driverDirectory / nodeDirectoryName - } override fun baseDirectory(nodeName: String): Path = baseDirectory(CordaX500Name.parse(nodeName)) @@ -887,7 +901,7 @@ class DriverDSL( private fun allNodesConnected(rpc: CordaRPCOps): CordaFuture { val (snapshot, updates) = rpc.networkMapFeed() val counterObservable = nodeCountObservable(snapshot.size, updates) - countObservables.put(rpc.nodeInfo().legalIdentities.first().name, counterObservable) + countObservables[rpc.nodeInfo().legalIdentities[0].name] = counterObservable /* TODO: this might not always be the exact number of nodes one has to wait for, * for example in the following sequence * 1 start 3 nodes in order, A, B, C. @@ -897,7 +911,7 @@ class DriverDSL( val requiredNodes = countObservables.size // This is an observable which yield the minimum number of nodes in each node network map. - val smallestSeenNetworkMapSize = Observable.combineLatest(countObservables.values.toList()) { args : Array -> + val smallestSeenNetworkMapSize = Observable.combineLatest(countObservables.values.toList()) { args: Array -> args.map { it as Int }.min() ?: 0 } val future = smallestSeenNetworkMapSize.filter { it >= requiredNodes }.toFuture() @@ -905,15 +919,23 @@ class DriverDSL( return future } - private fun startNodeInternal(config: Config, webAddress: NetworkHostAndPort, startInProcess: Boolean?, maximumHeapSize: String): CordaFuture { - val nodeConfiguration = config.parseAsNodeConfiguration() - nodeInfoFilesCopier.addConfig(nodeConfiguration.baseDirectory) + private fun startNodeInternal(config: Config, + webAddress: NetworkHostAndPort, + startInProcess: Boolean?, + maximumHeapSize: String): CordaFuture { + val configuration = config.parseAsNodeConfiguration() + val baseDirectory = configuration.baseDirectory.createDirectories() + if (networkParameters == null) { + networkParameters = NetworkParametersCopier(testNetworkParameters(emptyList())) + } + networkParameters!!.install(baseDirectory) + nodeInfoFilesCopier.addConfig(baseDirectory) val onNodeExit: () -> Unit = { - nodeInfoFilesCopier.removeConfig(nodeConfiguration.baseDirectory) - countObservables.remove(nodeConfiguration.myLegalName) + nodeInfoFilesCopier.removeConfig(baseDirectory) + countObservables.remove(configuration.myLegalName) } if (startInProcess ?: startNodesInProcess) { - val nodeAndThreadFuture = startInProcessNode(executorService, nodeConfiguration, config, cordappPackages) + val nodeAndThreadFuture = startInProcessNode(executorService, configuration, config, cordappPackages) shutdownManager.registerShutdown( nodeAndThreadFuture.map { (node, thread) -> { @@ -923,21 +945,21 @@ class DriverDSL( } ) return nodeAndThreadFuture.flatMap { (node, thread) -> - establishRpc(nodeConfiguration, openFuture()).flatMap { rpc -> + establishRpc(configuration, openFuture()).flatMap { rpc -> allNodesConnected(rpc).map { - NodeHandle.InProcess(rpc.nodeInfo(), rpc, nodeConfiguration, webAddress, node, thread, onNodeExit) + NodeHandle.InProcess(rpc.nodeInfo(), rpc, configuration, webAddress, node, thread, onNodeExit) } } } } else { val debugPort = if (isDebug) debugPortAllocation.nextPort() else null - val processFuture = startOutOfProcessNode(executorService, nodeConfiguration, config, quasarJarPath, debugPort, systemProperties, cordappPackages, maximumHeapSize) + val processFuture = startOutOfProcessNode(executorService, configuration, config, quasarJarPath, debugPort, systemProperties, cordappPackages, maximumHeapSize) registerProcess(processFuture) return processFuture.flatMap { process -> val processDeathFuture = poll(executorService, "process death") { if (process.isAlive) null else process } - establishRpc(nodeConfiguration, processDeathFuture).flatMap { rpc -> + establishRpc(configuration, processDeathFuture).flatMap { rpc -> // Call waitUntilNetworkReady in background in case RPC is failing over: val forked = executorService.fork { allNodesConnected(rpc) @@ -945,11 +967,11 @@ class DriverDSL( val networkMapFuture = forked.flatMap { it } firstOf(processDeathFuture, networkMapFuture) { if (it == processDeathFuture) { - throw ListenProcessDeathException(nodeConfiguration.p2pAddress, process) + throw ListenProcessDeathException(configuration.p2pAddress, process) } processDeathFuture.cancel(false) - log.info("Node handle is ready. NodeInfo: ${rpc.nodeInfo()}, WebAddress: ${webAddress}") - NodeHandle.OutOfProcess(rpc.nodeInfo(), rpc, nodeConfiguration, webAddress, debugPort, process, + log.info("Node handle is ready. NodeInfo: ${rpc.nodeInfo()}, WebAddress: $webAddress") + NodeHandle.OutOfProcess(rpc.nodeInfo(), rpc, configuration, webAddress, debugPort, process, onNodeExit) } } @@ -985,12 +1007,19 @@ class DriverDSL( // Write node.conf writeConfig(nodeConf.baseDirectory, "node.conf", config) // TODO pass the version in? - val node = Node(nodeConf, MOCK_VERSION_INFO, initialiseSerialization = false, cordappLoader = CordappLoader.createDefaultWithTestPackages(nodeConf, cordappPackages)).start() + val node = Node( + nodeConf, + MOCK_VERSION_INFO, + initialiseSerialization = false, + cordappLoader = CordappLoader.createDefaultWithTestPackages(nodeConf, cordappPackages)) + .start() val nodeThread = thread(name = nodeConf.myLegalName.organisation) { node.internals.run() } node to nodeThread - }.flatMap { nodeAndThread -> addressMustBeBoundFuture(executorService, nodeConf.p2pAddress).map { nodeAndThread } } + }.flatMap { + nodeAndThread -> addressMustBeBoundFuture(executorService, nodeConf.p2pAddress).map { nodeAndThread } + } } private fun startOutOfProcessNode( @@ -1004,7 +1033,7 @@ class DriverDSL( maximumHeapSize: String ): CordaFuture { val processFuture = executorService.fork { - log.info("Starting out-of-process Node ${nodeConf.myLegalName.organisation}, debug port is " + debugPort ?: "not enabled") + log.info("Starting out-of-process Node ${nodeConf.myLegalName.organisation}, debug port is " + (debugPort ?: "not enabled")) // Write node.conf writeConfig(nodeConf.baseDirectory, "node.conf", config) @@ -1015,7 +1044,13 @@ class DriverDSL( "java.io.tmpdir" to System.getProperty("java.io.tmpdir") // Inherit from parent process ) // See experimental/quasar-hook/README.md for how to generate. - val excludePattern = "x(antlr**;bftsmart**;ch**;co.paralleluniverse**;com.codahale**;com.esotericsoftware**;com.fasterxml**;com.google**;com.ibm**;com.intellij**;com.jcabi**;com.nhaarman**;com.opengamma**;com.typesafe**;com.zaxxer**;de.javakaffee**;groovy**;groovyjarjarantlr**;groovyjarjarasm**;io.atomix**;io.github**;io.netty**;jdk**;joptsimple**;junit**;kotlin**;net.bytebuddy**;net.i2p**;org.apache**;org.assertj**;org.bouncycastle**;org.codehaus**;org.crsh**;org.dom4j**;org.fusesource**;org.h2**;org.hamcrest**;org.hibernate**;org.jboss**;org.jcp**;org.joda**;org.junit**;org.mockito**;org.objectweb**;org.objenesis**;org.slf4j**;org.w3c**;org.xml**;org.yaml**;reflectasm**;rx**)" + val excludePattern = "x(antlr**;bftsmart**;ch**;co.paralleluniverse**;com.codahale**;com.esotericsoftware**;" + + "com.fasterxml**;com.google**;com.ibm**;com.intellij**;com.jcabi**;com.nhaarman**;com.opengamma**;" + + "com.typesafe**;com.zaxxer**;de.javakaffee**;groovy**;groovyjarjarantlr**;groovyjarjarasm**;io.atomix**;" + + "io.github**;io.netty**;jdk**;joptsimple**;junit**;kotlin**;net.bytebuddy**;net.i2p**;org.apache**;" + + "org.assertj**;org.bouncycastle**;org.codehaus**;org.crsh**;org.dom4j**;org.fusesource**;org.h2**;" + + "org.hamcrest**;org.hibernate**;org.jboss**;org.jcp**;org.joda**;org.junit**;org.mockito**;org.objectweb**;" + + "org.objenesis**;org.slf4j**;org.w3c**;org.xml**;org.yaml**;reflectasm**;rx**)" val extraJvmArguments = systemProperties.removeResolvedClasspath().map { "-D${it.key}=${it.value}" } + "-javaagent:$quasarJarPath=$excludePattern" val loggingLevel = if (debugPort == null) "INFO" else "DEBUG" @@ -1081,7 +1116,7 @@ class DriverDSL( } fun writeConfig(path: Path, filename: String, config: Config) { - path.toFile().mkdirs() - File("$path/$filename").writeText(config.root().render(ConfigRenderOptions.defaults())) + val configString = config.root().render(ConfigRenderOptions.defaults()) + configString.byteInputStream().copyTo(path / filename, REPLACE_EXISTING) } diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/internal/NodeBasedTest.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/internal/NodeBasedTest.kt index a3449d9417..3c0c0da6e0 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/internal/NodeBasedTest.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/internal/NodeBasedTest.kt @@ -16,11 +16,14 @@ import net.corda.node.services.config.parseAsNodeConfiguration import net.corda.node.services.config.plus import net.corda.nodeapi.User import net.corda.testing.SerializationEnvironmentRule +import net.corda.testing.common.internal.NetworkParametersCopier +import net.corda.testing.common.internal.testNetworkParameters import net.corda.testing.driver.addressMustNotBeBoundFuture import net.corda.testing.getFreeLocalPorts import net.corda.testing.node.MockServices import org.apache.logging.log4j.Level import org.junit.After +import org.junit.Before import org.junit.Rule import org.junit.rules.TemporaryFolder import java.nio.file.Path @@ -40,6 +43,7 @@ abstract class NodeBasedTest(private val cordappPackages: List = emptyLi @JvmField val tempFolder = TemporaryFolder() + private lateinit var defaultNetworkParameters: NetworkParametersCopier private val nodes = mutableListOf>() private val nodeInfos = mutableListOf() @@ -47,6 +51,11 @@ abstract class NodeBasedTest(private val cordappPackages: List = emptyLi System.setProperty("consoleLogLevel", Level.DEBUG.name().toLowerCase()) } + @Before + fun init() { + defaultNetworkParameters = NetworkParametersCopier(testNetworkParameters(emptyList())) + } + /** * Stops the network map node and all the nodes started by [startNode]. This is called automatically after each test * but can also be called manually within a test. @@ -86,6 +95,7 @@ abstract class NodeBasedTest(private val cordappPackages: List = emptyLi ) val parsedConfig = config.parseAsNodeConfiguration() + defaultNetworkParameters.install(baseDirectory) val node = Node( parsedConfig, MockServices.MOCK_VERSION_INFO.copy(platformVersion = platformVersion), diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/internal/demorun/DemoRunner.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/internal/demorun/DemoRunner.kt index cf452a9116..a38a5d78f1 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/internal/demorun/DemoRunner.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/internal/demorun/DemoRunner.kt @@ -15,6 +15,7 @@ fun CordformDefinition.clean() { /** * Creates and starts all nodes required for the demo. */ +// TODO add notaries to cordform! fun CordformDefinition.runNodes() = driver( isDebug = true, driverDirectory = driverDirectory, diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNetworkMapCache.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNetworkMapCache.kt index 71ee228f1b..6e10455fee 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNetworkMapCache.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNetworkMapCache.kt @@ -18,7 +18,10 @@ import java.math.BigInteger /** * Network map cache with no backing map service. */ -class MockNetworkMapCache(database: CordaPersistence, configuration: NodeConfiguration) : PersistentNetworkMapCache(database, configuration) { +class MockNetworkMapCache( + database: CordaPersistence, + configuration: NodeConfiguration +) : PersistentNetworkMapCache(database, configuration, emptyList()) { private companion object { val BANK_C = getTestPartyAndCertificate(CordaX500Name(organisation = "Bank C", locality = "London", country = "GB"), entropyToKeyPair(BigInteger.valueOf(1000)).public) val BANK_D = getTestPartyAndCertificate(CordaX500Name(organisation = "Bank D", locality = "London", country = "GB"), entropyToKeyPair(BigInteger.valueOf(2000)).public) diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNode.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNode.kt index 098efd5e38..321bb9c41a 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNode.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNode.kt @@ -7,13 +7,13 @@ import com.nhaarman.mockito_kotlin.whenever import net.corda.core.crypto.entropyToKeyPair import net.corda.core.crypto.random63BitValue import net.corda.core.identity.CordaX500Name -import net.corda.core.identity.PartyAndCertificate import net.corda.core.internal.createDirectories import net.corda.core.internal.createDirectory import net.corda.core.internal.uncheckedCast import net.corda.core.messaging.MessageRecipients import net.corda.core.messaging.RPCOps import net.corda.core.messaging.SingleMessageRecipient +import net.corda.core.node.NotaryInfo import net.corda.core.node.services.IdentityService import net.corda.core.node.services.KeyManagementService import net.corda.core.serialization.SerializationWhitelist @@ -34,14 +34,17 @@ import net.corda.node.services.transactions.BFTSMaRt import net.corda.node.services.transactions.InMemoryTransactionVerifierService import net.corda.node.utilities.AffinityExecutor import net.corda.node.utilities.AffinityExecutor.ServiceAffinityExecutor -import net.corda.nodeapi.internal.ServiceInfo +import net.corda.node.utilities.ServiceIdentityGenerator import net.corda.testing.DUMMY_NOTARY +import net.corda.testing.common.internal.NetworkParametersCopier +import net.corda.testing.common.internal.testNetworkParameters import net.corda.testing.initialiseTestSerialization import net.corda.testing.node.MockServices.Companion.MOCK_VERSION_INFO import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties import net.corda.testing.testNodeConfiguration import org.apache.activemq.artemis.utils.ReusableLatch import org.slf4j.Logger +import sun.plugin.dom.DOMObjectFactory.createNode import java.io.Closeable import java.math.BigInteger import java.nio.file.Path @@ -72,8 +75,6 @@ data class MockNetworkParameters( } /** - * @param notaryIdentity a set of service entries to use in place of the node's default service entries, - * for example where a node's service is part of a cluster. * @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. @@ -82,12 +83,10 @@ data class MockNetworkParameters( data class MockNodeParameters( val forcedID: Int? = null, val legalName: CordaX500Name? = null, - val notaryIdentity: Pair? = null, val entropyRoot: BigInteger = BigInteger.valueOf(random63BitValue()), val configOverrides: (NodeConfiguration) -> Any? = {}) { fun setForcedID(forcedID: Int?) = copy(forcedID = forcedID) fun setLegalName(legalName: CordaX500Name?) = copy(legalName = legalName) - fun setNotaryIdentity(notaryIdentity: Pair?) = copy(notaryIdentity = notaryIdentity) fun setEntropyRoot(entropyRoot: BigInteger) = copy(entropyRoot = entropyRoot) fun setConfigOverrides(configOverrides: (NodeConfiguration) -> Any?) = copy(configOverrides = configOverrides) } @@ -96,8 +95,8 @@ data class MockNodeArgs( val config: NodeConfiguration, val network: MockNetwork, val id: Int, - val notaryIdentity: Pair?, - val entropyRoot: BigInteger) + val entropyRoot: BigInteger +) /** * A mock node brings up a suite of in-memory services in a fast manner suitable for unit testing. @@ -129,10 +128,13 @@ class MockNetwork(defaultParameters: MockNetworkParameters = MockNetworkParamete val messagingNetwork = InMemoryMessagingNetwork(networkSendManuallyPumped, servicePeerAllocationStrategy, busyLatch) // A unique identifier for this network to segregate databases with the same nodeID but different networks. private val networkId = random63BitValue() - private val _nodes = mutableListOf() + private lateinit var networkParameters: NetworkParametersCopier + private var notaryInfos: MutableList? = ArrayList() + private val _nodes = ArrayList() + private val serializationEnv = initialiseTestSerialization(initialiseSerialization) + /** A read only view of the current set of executing nodes. */ val nodes: List get() = _nodes - private val serializationEnv = initialiseTestSerialization(initialiseSerialization) init { filesystem.getPath("/nodes").createDirectory() @@ -152,11 +154,11 @@ class MockNetwork(defaultParameters: MockNetworkParameters = MockNetworkParamete } override fun awaitTermination(timeout: Long, unit: TimeUnit): Boolean { - if (!isShutdown) { + return if (!isShutdown) { flush() - return true + true } else { - return super.awaitTermination(timeout, unit) + super.awaitTermination(timeout, unit) } } } @@ -166,44 +168,69 @@ class MockNetwork(defaultParameters: MockNetworkParameters = MockNetworkParamete TestClock(), MOCK_VERSION_INFO, CordappLoader.createDefaultWithTestPackages(args.config, args.network.cordappPackages), - args.network.busyLatch) { + args.network.busyLatch + ) { val mockNet = args.network val id = args.id - internal val notaryIdentity = args.notaryIdentity - val entropyRoot = args.entropyRoot + private val entropyRoot = args.entropyRoot var counter = entropyRoot + override val log: Logger = loggerFor() + override val serverThread: AffinityExecutor = - if (mockNet.threadPerNode) + if (mockNet.threadPerNode) { ServiceAffinityExecutor("Mock node $id thread", 1) - else { + } else { mockNet.sharedUserCount.incrementAndGet() mockNet.sharedServerThread } + override val started: StartedNode? get() = uncheckedCast(super.started) - override fun start(): StartedNode = uncheckedCast(super.start()) + + override fun start(): StartedNode { + installNetworkParameters() + val started: StartedNode = uncheckedCast(super.start()) + advertiseNodeToNetwork(started) + return started + } + + private fun installNetworkParameters() { + mockNet.notaryInfos?.let { + mockNet.networkParameters = NetworkParametersCopier(testNetworkParameters(it)) + } + mockNet.notaryInfos = null + mockNet.networkParameters.install(configuration.baseDirectory) + } + + private fun advertiseNodeToNetwork(newNode: StartedNode) { + mockNet.nodes + .mapNotNull { it.started } + .forEach { existingNode -> + newNode.services.networkMapCache.addNode(existingNode.info) + existingNode.services.networkMapCache.addNode(newNode.info) + } + } // We only need to override the messaging service here, as currently everything that hits disk does so // through the java.nio API which we are already mocking via Jimfs. - override fun makeMessagingService(legalIdentity: PartyAndCertificate): MessagingService { + override fun makeMessagingService(): MessagingService { require(id >= 0) { "Node ID must be zero or positive, was passed: " + id } return mockNet.messagingNetwork.createNodeWithID( !mockNet.threadPerNode, id, serverThread, - getNotaryIdentity(), + myNotaryIdentity, myLegalName, - database) - .start() - .getOrThrow() + database + ).start().getOrThrow() } fun setMessagingServiceSpy(messagingServiceSpy: MessagingServiceSpy) { network = messagingServiceSpy } - override fun makeKeyManagementService(identityService: IdentityService): KeyManagementService { - return E2ETestKeyManagementService(identityService, partyKeys + (notaryIdentity?.let { setOf(it.second) } ?: emptySet())) + override fun makeKeyManagementService(identityService: IdentityService, keyPairs: Set): KeyManagementService { + return E2ETestKeyManagementService(identityService, keyPairs) } override fun startMessagingService(rpcOps: RPCOps) { @@ -224,7 +251,7 @@ class MockNetwork(defaultParameters: MockNetworkParameters = MockNetworkParamete override fun makeTransactionVerifierService() = InMemoryTransactionVerifierService(1) - override fun myAddresses() = emptyList() + override fun myAddresses(): List = emptyList() // Allow unit tests to modify the serialization whitelist list before the node start, // so they don't have to ServiceLoad test whitelists into all unit tests. @@ -269,17 +296,21 @@ class MockNetwork(defaultParameters: MockNetworkParameters = MockNetworkParamete } } - fun createUnstartedNode(parameters: MockNodeParameters = MockNodeParameters()) = createUnstartedNode(parameters, defaultFactory) + fun createUnstartedNode(parameters: MockNodeParameters = MockNodeParameters()): MockNode { + return createUnstartedNode(parameters, defaultFactory) + } + fun createUnstartedNode(parameters: MockNodeParameters = MockNodeParameters(), nodeFactory: (MockNodeArgs) -> N): N { return createNodeImpl(parameters, nodeFactory, false) } - fun createNode(parameters: MockNodeParameters = MockNodeParameters()): StartedNode = createNode(parameters, defaultFactory) + fun createNode(parameters: MockNodeParameters = MockNodeParameters()): StartedNode { + return createNode(parameters, defaultFactory) + } + /** Like the other [createNode] but takes a [nodeFactory] and propagates its [MockNode] subtype. */ fun createNode(parameters: MockNodeParameters = MockNodeParameters(), nodeFactory: (MockNodeArgs) -> N): StartedNode { - val node: StartedNode = uncheckedCast(createNodeImpl(parameters, nodeFactory, true).started)!! - ensureAllNetworkMapCachesHaveAllNodeInfos() - return node + return uncheckedCast(createNodeImpl(parameters, nodeFactory, true).started)!! } private fun createNodeImpl(parameters: MockNodeParameters, nodeFactory: (MockNodeArgs) -> N, start: Boolean): N { @@ -290,13 +321,24 @@ class MockNetwork(defaultParameters: MockNetworkParameters = MockNetworkParamete doReturn(makeTestDataSourceProperties("node_${id}_net_$networkId")).whenever(it).dataSourceProperties parameters.configOverrides(it) } - return nodeFactory(MockNodeArgs(config, this, id, parameters.notaryIdentity, parameters.entropyRoot)).apply { - if (start) { - start() - ensureAllNetworkMapCachesHaveAllNodeInfos() + + val node = nodeFactory(MockNodeArgs(config, this, id, parameters.entropyRoot)) + _nodes += node + + config.notary?.let { notaryConfig -> + val notaryInfos = notaryInfos ?: throw IllegalStateException("Cannot add notaries once nodes have started") + if (!notaryConfig.isClusterConfig) { + // Create the node's main identity, which will also double as its notary identity + val identity = ServiceIdentityGenerator.generateToDisk(listOf(config.baseDirectory), config.myLegalName, "identity") + notaryInfos += NotaryInfo(identity, notaryConfig.validating) } - _nodes.add(this) } + + if (start) { + node.start() + } + + return node } fun baseDirectory(nodeId: Int): Path = filesystem.getPath("/nodes/$nodeId") @@ -309,7 +351,6 @@ class MockNetwork(defaultParameters: MockNetworkParameters = MockNetworkParamete */ @JvmOverloads fun runNetwork(rounds: Int = -1) { - ensureAllNetworkMapCachesHaveAllNodeInfos() check(!networkSendManuallyPumped) fun pumpAll() = messagingNetwork.endpoints.map { it.pumpReceive(false) } @@ -324,23 +365,26 @@ class MockNetwork(defaultParameters: MockNetworkParameters = MockNetworkParamete } @JvmOverloads - fun createNotaryNode(parameters: MockNodeParameters = MockNodeParameters(legalName = DUMMY_NOTARY.name), validating: Boolean = true): StartedNode { + fun createNotaryNode(parameters: MockNodeParameters = MockNodeParameters(legalName = DUMMY_NOTARY.name), + validating: Boolean = true): StartedNode { return createNotaryNode(parameters, validating, defaultFactory) } fun createNotaryNode(parameters: MockNodeParameters = MockNodeParameters(legalName = DUMMY_NOTARY.name), validating: Boolean = true, nodeFactory: (MockNodeArgs) -> N): StartedNode { - return createNode(parameters.copy(configOverrides = { - doReturn(NotaryConfig(validating)).whenever(it).notary - parameters.configOverrides(it) - }), nodeFactory) + return createNode( + parameters.copy(configOverrides = { + doReturn(NotaryConfig(validating)).whenever(it).notary + parameters.configOverrides(it) + }), + nodeFactory + ) } @JvmOverloads - fun createPartyNode(legalName: CordaX500Name? = null, - notaryIdentity: Pair? = null): StartedNode { - return createNode(MockNodeParameters(legalName = legalName, notaryIdentity = notaryIdentity)) + fun createPartyNode(legalName: CordaX500Name? = null): StartedNode { + return createNode(MockNodeParameters(legalName = legalName)) } @Suppress("unused") // This is used from the network visualiser tool. @@ -355,21 +399,9 @@ class MockNetwork(defaultParameters: MockNetworkParameters = MockNetworkParamete } } - private fun ensureAllNetworkMapCachesHaveAllNodeInfos() { - val infos = nodes.mapNotNull { it.started?.info } - nodes.filter { it.hasDBConnection() } - .mapNotNull { it.started?.services?.networkMapCache } - .forEach { - for (nodeInfo in infos) { - it.addNode(nodeInfo) - } - } - } - fun startNodes() { require(nodes.isNotEmpty()) nodes.forEach { it.started ?: it.start() } - ensureAllNetworkMapCachesHaveAllNodeInfos() } fun stopNodes() { diff --git a/testing/smoke-test-utils/src/main/kotlin/net/corda/smoketesting/NodeProcess.kt b/testing/smoke-test-utils/src/main/kotlin/net/corda/smoketesting/NodeProcess.kt index 4eb02c0d88..f2a9196eac 100644 --- a/testing/smoke-test-utils/src/main/kotlin/net/corda/smoketesting/NodeProcess.kt +++ b/testing/smoke-test-utils/src/main/kotlin/net/corda/smoketesting/NodeProcess.kt @@ -2,10 +2,14 @@ package net.corda.smoketesting import net.corda.client.rpc.CordaRPCClient import net.corda.client.rpc.CordaRPCConnection +import net.corda.client.rpc.internal.KryoClientSerializationScheme +import net.corda.core.internal.copyTo import net.corda.core.internal.createDirectories import net.corda.core.internal.div import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.loggerFor +import net.corda.testing.common.internal.NetworkParametersCopier +import net.corda.testing.common.internal.testNetworkParameters import java.nio.file.Path import java.nio.file.Paths import java.time.Instant @@ -45,9 +49,17 @@ class NodeProcess( private val buildDirectory: Path = Paths.get("build"), private val cordaJar: Path = Paths.get(this::class.java.getResource("/corda.jar").toURI()) ) { - companion object { + private companion object { val javaPath: Path = Paths.get(System.getProperty("java.home"), "bin", "java") val formatter: DateTimeFormatter = DateTimeFormatter.ofPattern("yyyyMMddHHmmss").withZone(systemDefault()) + val defaultNetworkParameters = run { + // TODO withTestSerialization is in test-utils, which we don't have access to + KryoClientSerializationScheme.initialiseSerialization() + // There are no notaries in the network parameters for smoke test nodes. If this is required then we would + // need to introduce the concept of a "network" which predefines the notaries, like the driver and MockNetwork + NetworkParametersCopier(testNetworkParameters(emptyList())) + } + init { try { Class.forName("net.corda.node.Corda") @@ -66,8 +78,8 @@ class NodeProcess( val nodeDir = baseDirectory(config).createDirectories() log.info("Node directory: {}", nodeDir) - val confFile = nodeDir.resolve("node.conf").toFile() - confFile.writeText(config.toText()) + config.toText().byteInputStream().copyTo(nodeDir / "node.conf") + defaultNetworkParameters.install(nodeDir) val process = startNode(nodeDir) val client = CordaRPCClient(NetworkHostAndPort("localhost", config.rpcPort)) diff --git a/testing/test-common/build.gradle b/testing/test-common/build.gradle index 05c6d36b20..dd2e2285b5 100644 --- a/testing/test-common/build.gradle +++ b/testing/test-common/build.gradle @@ -1,6 +1,10 @@ apply plugin: 'net.corda.plugins.publish-utils' apply plugin: 'com.jfrog.artifactory' +dependencies { + compile project(':core') +} + jar { baseName 'corda-test-common' } diff --git a/testing/test-common/src/main/kotlin/net/corda/testing/common/internal/NetworkParametersCopier.kt b/testing/test-common/src/main/kotlin/net/corda/testing/common/internal/NetworkParametersCopier.kt new file mode 100644 index 0000000000..25ba327a73 --- /dev/null +++ b/testing/test-common/src/main/kotlin/net/corda/testing/common/internal/NetworkParametersCopier.kt @@ -0,0 +1,32 @@ +package net.corda.testing.common.internal + +import net.corda.core.crypto.SignedData +import net.corda.core.crypto.entropyToKeyPair +import net.corda.core.crypto.sign +import net.corda.core.internal.copyTo +import net.corda.core.internal.div +import net.corda.core.node.NetworkParameters +import net.corda.core.serialization.serialize +import java.math.BigInteger +import java.nio.file.FileAlreadyExistsException +import java.nio.file.Path + +class NetworkParametersCopier(networkParameters: NetworkParameters) { + private companion object { + val DUMMY_MAP_KEY = entropyToKeyPair(BigInteger.valueOf(123)) + } + + private val serializedNetworkParameters = networkParameters.let { + val serialize = it.serialize() + val signature = DUMMY_MAP_KEY.sign(serialize) + SignedData(serialize, signature).serialize() + } + + fun install(dir: Path) { + try { + serializedNetworkParameters.open().copyTo(dir / "network-parameters") + } catch (e: FileAlreadyExistsException) { + // Leave the file untouched if it already exists + } + } +} \ No newline at end of file diff --git a/testing/test-common/src/main/kotlin/net/corda/testing/common/internal/ParametersUtilities.kt b/testing/test-common/src/main/kotlin/net/corda/testing/common/internal/ParametersUtilities.kt new file mode 100644 index 0000000000..da40ba6ac3 --- /dev/null +++ b/testing/test-common/src/main/kotlin/net/corda/testing/common/internal/ParametersUtilities.kt @@ -0,0 +1,18 @@ +package net.corda.testing.common.internal + +import net.corda.core.node.NetworkParameters +import net.corda.core.node.NotaryInfo +import net.corda.core.utilities.days +import java.time.Instant + +fun testNetworkParameters(notaries: List): NetworkParameters { + return NetworkParameters( + minimumPlatformVersion = 1, + notaries = notaries, + modifiedTime = Instant.now(), + eventHorizon = 10000.days, + maxMessageSize = 40000, + maxTransactionSize = 40000, + epoch = 1 + ) +} \ No newline at end of file diff --git a/testing/test-utils/build.gradle b/testing/test-utils/build.gradle index f2995b396a..c90b00304e 100644 --- a/testing/test-utils/build.gradle +++ b/testing/test-utils/build.gradle @@ -8,7 +8,6 @@ description 'Testing utilities for Corda' dependencies { compile project(':test-common') - compile project(':core') compile project(':node') compile project(':client:mock') diff --git a/testing/test-utils/src/main/kotlin/net/corda/testing/TestConstants.kt b/testing/test-utils/src/main/kotlin/net/corda/testing/TestConstants.kt index 5aa2750e4b..d4b2290b40 100644 --- a/testing/test-utils/src/main/kotlin/net/corda/testing/TestConstants.kt +++ b/testing/test-utils/src/main/kotlin/net/corda/testing/TestConstants.kt @@ -30,7 +30,6 @@ val DUMMY_NOTARY_KEY: KeyPair by lazy { entropyToKeyPair(BigInteger.valueOf(20)) /** Dummy notary identity for tests and simulations */ val DUMMY_NOTARY_IDENTITY: PartyAndCertificate get() = getTestPartyAndCertificate(DUMMY_NOTARY) val DUMMY_NOTARY: Party get() = Party(CordaX500Name(organisation = "Notary Service", locality = "Zurich", country = "CH"), DUMMY_NOTARY_KEY.public) -val DUMMY_NOTARY_SERVICE_NAME: CordaX500Name = DUMMY_NOTARY.name.copy(commonName = "corda.notary.validating") val DUMMY_BANK_A_KEY: KeyPair by lazy { entropyToKeyPair(BigInteger.valueOf(40)) } /** Dummy bank identity for tests and simulations */ diff --git a/tools/explorer/src/main/kotlin/net/corda/explorer/views/Network.kt b/tools/explorer/src/main/kotlin/net/corda/explorer/views/Network.kt index de6a41aded..8970e7b1e1 100644 --- a/tools/explorer/src/main/kotlin/net/corda/explorer/views/Network.kt +++ b/tools/explorer/src/main/kotlin/net/corda/explorer/views/Network.kt @@ -36,7 +36,6 @@ import net.corda.explorer.model.CordaView import net.corda.finance.utils.CityDatabase import net.corda.finance.utils.ScreenCoordinate import net.corda.finance.utils.WorldMapLocation -import net.corda.nodeapi.internal.ServiceType import tornadofx.* class Network : CordaView() { @@ -45,6 +44,7 @@ class Network : CordaView() { // Inject data. private val myIdentity by observableValue(NetworkIdentityModel::myIdentity) private val notaries by observableList(NetworkIdentityModel::notaryNodes) + private val notaryIdentities by observableList(NetworkIdentityModel::notaries) private val peers by observableList(NetworkIdentityModel::parties) private val transactions by observableList(TransactionDataModel::partiallyResolvedTransactions) var centralPeer: String? = null @@ -103,7 +103,7 @@ class Network : CordaView() { hgap = 5.0 vgap = 5.0 for (identity in identities) { - val isNotary = identity.name.commonName?.let { ServiceType.parse(it).isNotary() } == true + val isNotary = identity.party in notaryIdentities row("${if (isNotary) "Notary " else ""}Public Key :") { copyableLabel(SimpleObjectProperty(identity.owningKey.toBase58String())) } diff --git a/tools/loadtest/src/main/kotlin/net/corda/loadtest/tests/NotaryTest.kt b/tools/loadtest/src/main/kotlin/net/corda/loadtest/tests/NotaryTest.kt index b96f6f4f40..88b517b9db 100644 --- a/tools/loadtest/src/main/kotlin/net/corda/loadtest/tests/NotaryTest.kt +++ b/tools/loadtest/src/main/kotlin/net/corda/loadtest/tests/NotaryTest.kt @@ -24,7 +24,7 @@ val dummyNotarisationTest = LoadTest( val issuerServices = MockServices(DUMMY_CASH_ISSUER_KEY) val generateTx = Generator.pickOne(simpleNodes).flatMap { node -> Generator.int().map { - val issueBuilder = DummyContract.generateInitial(it, notary.info.legalIdentities[1], DUMMY_CASH_ISSUER) // TODO notary choice + val issueBuilder = DummyContract.generateInitial(it, notary.info.legalIdentities[0], DUMMY_CASH_ISSUER) // TODO notary choice val issueTx = issuerServices.signInitialTransaction(issueBuilder) val asset = issueTx.tx.outRef(0) val moveBuilder = DummyContract.move(asset, DUMMY_CASH_ISSUER.party) 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 279d34f6b2..7184c381b7 100644 --- a/verifier/src/integration-test/kotlin/net/corda/verifier/VerifierDriver.kt +++ b/verifier/src/integration-test/kotlin/net/corda/verifier/VerifierDriver.kt @@ -6,6 +6,7 @@ import net.corda.core.concurrent.CordaFuture import net.corda.core.crypto.random63BitValue import net.corda.core.identity.CordaX500Name import net.corda.core.internal.concurrent.* +import net.corda.core.internal.createDirectories import net.corda.core.internal.div import net.corda.core.transactions.LedgerTransaction import net.corda.core.utilities.NetworkHostAndPort @@ -248,7 +249,7 @@ data class VerifierDriverDSL( val jdwpPort = if (driverDSL.isDebug) driverDSL.debugPortAllocation.nextPort() else null val processFuture = driverDSL.executorService.fork { val verifierName = CordaX500Name(organisation = "Verifier$id", locality = "London", country = "GB") - val baseDirectory = driverDSL.driverDirectory / verifierName.organisation + val baseDirectory = (driverDSL.driverDirectory / verifierName.organisation).createDirectories() val config = createConfiguration(baseDirectory, address) val configFilename = "verifier.conf" writeConfig(baseDirectory, configFilename, config) 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 2ff5b34e62..9072bdbe52 100644 --- a/verifier/src/integration-test/kotlin/net/corda/verifier/VerifierTests.kt +++ b/verifier/src/integration-test/kotlin/net/corda/verifier/VerifierTests.kt @@ -7,6 +7,7 @@ import net.corda.core.messaging.startFlow import net.corda.core.transactions.LedgerTransaction import net.corda.core.transactions.WireTransaction import net.corda.core.utilities.OpaqueBytes +import net.corda.core.utilities.getOrThrow import net.corda.finance.DOLLARS import net.corda.finance.flows.CashIssueFlow import net.corda.finance.flows.CashPaymentFlow @@ -14,7 +15,6 @@ import net.corda.node.services.config.VerifierType import net.corda.testing.ALICE import net.corda.testing.ALICE_NAME import net.corda.testing.DUMMY_NOTARY -import net.corda.testing.DUMMY_NOTARY_SERVICE_NAME import org.junit.Test import java.util.* import java.util.concurrent.atomic.AtomicInteger @@ -129,12 +129,12 @@ class VerifierTests { @Test fun `single verifier works with a node`() { verifierDriver(extraCordappPackagesToScan = listOf("net.corda.finance.contracts")) { - val aliceFuture = startNode(providedName = ALICE.name) - val notaryFuture = startNotaryNode(DUMMY_NOTARY.name, verifierType = VerifierType.OutOfProcess) - val aliceNode = aliceFuture.get() - val notaryNode = notaryFuture.get() + val (notaryNode, aliceNode) = listOf( + startNotaryNode(DUMMY_NOTARY.name, verifierType = VerifierType.OutOfProcess), + startNode(providedName = ALICE.name) + ).transpose().getOrThrow() val alice = aliceNode.rpc.wellKnownPartyFromX500Name(ALICE_NAME)!! - val notary = notaryNode.rpc.notaryPartyFromX500Name(DUMMY_NOTARY_SERVICE_NAME)!! + val notary = notaryNode.rpc.notaryPartyFromX500Name(DUMMY_NOTARY.name)!! startVerifier(notaryNode) aliceNode.rpc.startFlow(::CashIssueFlow, 10.DOLLARS, OpaqueBytes.of(0), notary).returnValue.get() notaryNode.waitUntilNumberOfVerifiers(1) From 22453204ed209b5c29336fa3b1119aefd2fd8eb0 Mon Sep 17 00:00:00 2001 From: Patrick Kuo Date: Fri, 3 Nov 2017 11:30:29 +0000 Subject: [PATCH 18/24] move node info schema to internal package (#1976) --- .ci/api-current.txt | 60 ------------------- .../{ => internal}/schemas/NodeInfoSchema.kt | 3 +- docs/source/changelog.rst | 3 + .../network/PersistentNetworkMapCache.kt | 2 +- .../node/services/schema/NodeSchemaService.kt | 6 +- 5 files changed, 11 insertions(+), 63 deletions(-) rename core/src/main/kotlin/net/corda/core/{ => internal}/schemas/NodeInfoSchema.kt (98%) diff --git a/.ci/api-current.txt b/.ci/api-current.txt index 5fcde86cee..03b214c40d 100644 --- a/.ci/api-current.txt +++ b/.ci/api-current.txt @@ -2344,66 +2344,6 @@ public class net.corda.core.schemas.MappedSchema extends java.lang.Object public final int getVersion() @org.jetbrains.annotations.NotNull public String toString() ## -public final class net.corda.core.schemas.NodeInfoSchema extends java.lang.Object - public static final net.corda.core.schemas.NodeInfoSchema INSTANCE -## -public final class net.corda.core.schemas.NodeInfoSchemaV1 extends net.corda.core.schemas.MappedSchema - public static final net.corda.core.schemas.NodeInfoSchemaV1 INSTANCE -## -@javax.persistence.Entity public static final class net.corda.core.schemas.NodeInfoSchemaV1$DBHostAndPort extends java.lang.Object - public () - public (net.corda.core.schemas.NodeInfoSchemaV1$PKHostAndPort) - @org.jetbrains.annotations.NotNull public final net.corda.core.schemas.NodeInfoSchemaV1$DBHostAndPort copy(net.corda.core.schemas.NodeInfoSchemaV1$PKHostAndPort) - public boolean equals(Object) - public int hashCode() - @org.jetbrains.annotations.NotNull public final net.corda.core.utilities.NetworkHostAndPort toHostAndPort() - public String toString() - public static final net.corda.core.schemas.NodeInfoSchemaV1$DBHostAndPort$Companion Companion -## -public static final class net.corda.core.schemas.NodeInfoSchemaV1$DBHostAndPort$Companion extends java.lang.Object - @org.jetbrains.annotations.NotNull public final net.corda.core.schemas.NodeInfoSchemaV1$DBHostAndPort fromHostAndPort(net.corda.core.utilities.NetworkHostAndPort) -## -@javax.persistence.Entity @javax.persistence.Table public static final class net.corda.core.schemas.NodeInfoSchemaV1$DBPartyAndCertificate extends java.lang.Object - public () - public (String, String, byte[], boolean, Set) - public (net.corda.core.identity.PartyAndCertificate, boolean) - @org.jetbrains.annotations.NotNull public final String component1() - @org.jetbrains.annotations.NotNull public final String component2() - @org.jetbrains.annotations.NotNull public final byte[] component3() - public final boolean component4() - @org.jetbrains.annotations.NotNull public final net.corda.core.schemas.NodeInfoSchemaV1$DBPartyAndCertificate copy(String, String, byte[], boolean, Set) - public boolean equals(Object) - @org.jetbrains.annotations.NotNull public final String getName() - @org.jetbrains.annotations.NotNull public final String getOwningKeyHash() - @org.jetbrains.annotations.NotNull public final byte[] getPartyCertBinary() - public int hashCode() - public final boolean isMain() - @org.jetbrains.annotations.NotNull public final net.corda.core.identity.PartyAndCertificate toLegalIdentityAndCert() - public String toString() -## -@javax.persistence.Embeddable public static final class net.corda.core.schemas.NodeInfoSchemaV1$PKHostAndPort extends java.lang.Object implements java.io.Serializable - public () - public (String, Integer) - @org.jetbrains.annotations.Nullable public final String component1() - @org.jetbrains.annotations.Nullable public final Integer component2() - @org.jetbrains.annotations.NotNull public final net.corda.core.schemas.NodeInfoSchemaV1$PKHostAndPort copy(String, Integer) - public boolean equals(Object) - @org.jetbrains.annotations.Nullable public final String getHost() - @org.jetbrains.annotations.Nullable public final Integer getPort() - public int hashCode() - public String toString() -## -@javax.persistence.Entity @javax.persistence.Table public static final class net.corda.core.schemas.NodeInfoSchemaV1$PersistentNodeInfo extends java.lang.Object - public () - public (int, List, List, int, long) - @org.jetbrains.annotations.NotNull public final List getAddresses() - public final int getId() - @org.jetbrains.annotations.NotNull public final List getLegalIdentitiesAndCerts() - public final int getPlatformVersion() - public final long getSerial() - public final void setId(int) - @org.jetbrains.annotations.NotNull public final net.corda.core.node.NodeInfo toNodeInfo() -## @javax.persistence.MappedSuperclass @net.corda.core.serialization.CordaSerializable public class net.corda.core.schemas.PersistentState extends java.lang.Object implements net.corda.core.schemas.StatePersistable public () public (net.corda.core.schemas.PersistentStateRef) diff --git a/core/src/main/kotlin/net/corda/core/schemas/NodeInfoSchema.kt b/core/src/main/kotlin/net/corda/core/internal/schemas/NodeInfoSchema.kt similarity index 98% rename from core/src/main/kotlin/net/corda/core/schemas/NodeInfoSchema.kt rename to core/src/main/kotlin/net/corda/core/internal/schemas/NodeInfoSchema.kt index 80c0d842ae..219ee2cbae 100644 --- a/core/src/main/kotlin/net/corda/core/schemas/NodeInfoSchema.kt +++ b/core/src/main/kotlin/net/corda/core/internal/schemas/NodeInfoSchema.kt @@ -1,8 +1,9 @@ -package net.corda.core.schemas +package net.corda.core.internal.schemas import net.corda.core.crypto.toStringShort import net.corda.core.identity.PartyAndCertificate import net.corda.core.node.NodeInfo +import net.corda.core.schemas.MappedSchema import net.corda.core.serialization.SerializationDefaults import net.corda.core.serialization.deserialize import net.corda.core.serialization.serialize diff --git a/docs/source/changelog.rst b/docs/source/changelog.rst index 184a572810..dae68708fe 100644 --- a/docs/source/changelog.rst +++ b/docs/source/changelog.rst @@ -72,6 +72,9 @@ UNRELEASED * The ``ReceiveTransactionFlow`` can now be told to record the transaction at the same time as receiving it. Using this feature, better support for observer/regulator nodes has been added. See :doc:`tutorial-observer-nodes`. +* Moved ``NodeInfoSchema`` to internal package as the node info's database schema is not part of the public API. This is + needed to allow new ``node_info_hash`` column to be added for the network map redesign work. + .. _changelog_v1: Release 1.0 diff --git a/node/src/main/kotlin/net/corda/node/services/network/PersistentNetworkMapCache.kt b/node/src/main/kotlin/net/corda/node/services/network/PersistentNetworkMapCache.kt index 0af7cb2e1c..349b9e1185 100644 --- a/node/src/main/kotlin/net/corda/node/services/network/PersistentNetworkMapCache.kt +++ b/node/src/main/kotlin/net/corda/node/services/network/PersistentNetworkMapCache.kt @@ -8,13 +8,13 @@ import net.corda.core.identity.Party import net.corda.core.identity.PartyAndCertificate import net.corda.core.internal.bufferUntilSubscribed import net.corda.core.internal.concurrent.openFuture +import net.corda.core.internal.schemas.NodeInfoSchemaV1 import net.corda.core.messaging.DataFeed import net.corda.core.node.NodeInfo import net.corda.core.node.NotaryInfo import net.corda.core.node.services.IdentityService import net.corda.core.node.services.NetworkMapCache.MapChange import net.corda.core.node.services.PartyInfo -import net.corda.core.schemas.NodeInfoSchemaV1 import net.corda.core.serialization.SingletonSerializeAsToken import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.loggerFor diff --git a/node/src/main/kotlin/net/corda/node/services/schema/NodeSchemaService.kt b/node/src/main/kotlin/net/corda/node/services/schema/NodeSchemaService.kt index ea02e3d45f..00cb1b5f79 100644 --- a/node/src/main/kotlin/net/corda/node/services/schema/NodeSchemaService.kt +++ b/node/src/main/kotlin/net/corda/node/services/schema/NodeSchemaService.kt @@ -3,7 +3,11 @@ package net.corda.node.services.schema import net.corda.core.contracts.ContractState import net.corda.core.contracts.FungibleAsset import net.corda.core.contracts.LinearState -import net.corda.core.schemas.* +import net.corda.core.internal.schemas.NodeInfoSchemaV1 +import net.corda.core.schemas.CommonSchemaV1 +import net.corda.core.schemas.MappedSchema +import net.corda.core.schemas.PersistentState +import net.corda.core.schemas.QueryableState import net.corda.core.serialization.SingletonSerializeAsToken import net.corda.node.internal.cordapp.CordappLoader import net.corda.node.services.api.SchemaService From 9be37c2b88ca3f0a55ef2de8e81dd43fe0e7aa76 Mon Sep 17 00:00:00 2001 From: Alberto Arri <30873160+al-r3@users.noreply.github.com> Date: Fri, 3 Nov 2017 14:27:46 +0000 Subject: [PATCH 19/24] Remove useless call to waitUntilNetworkReady within driver started nodes (#1998) Remove usages of waitUntilNetworkReady in conjunction with driver --- .../kotlin/net/corda/docs/IntegrationTestingTutorial.kt | 8 ++------ .../src/main/kotlin/net/corda/docs/ClientRpcTutorial.kt | 4 +--- .../net/corda/node/services/AttachmentLoadingTests.kt | 1 - .../kotlin/net/corda/bank/BankOfCordaRPCClientTest.kt | 5 +---- .../src/main/kotlin/net/corda/testing/driver/Driver.kt | 2 +- .../main/kotlin/net/corda/explorer/ExplorerSimulation.kt | 4 ---- 6 files changed, 5 insertions(+), 19 deletions(-) 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 be3ab27dc4..c9be1c2353 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 @@ -11,8 +11,8 @@ import net.corda.finance.DOLLARS import net.corda.finance.contracts.asset.Cash import net.corda.finance.flows.CashIssueFlow import net.corda.finance.flows.CashPaymentFlow -import net.corda.node.services.Permissions.Companion.startFlow import net.corda.node.services.Permissions.Companion.invokeRpc +import net.corda.node.services.Permissions.Companion.startFlow import net.corda.nodeapi.User import net.corda.testing.* import net.corda.testing.driver.driver @@ -28,14 +28,12 @@ class IntegrationTestingTutorial { val aliceUser = User("aliceUser", "testPassword1", permissions = setOf( startFlow(), startFlow(), - invokeRpc(CordaRPCOps::waitUntilNetworkReady), invokeRpc("vaultTrackBy"), invokeRpc(CordaRPCOps::notaryIdentities), invokeRpc(CordaRPCOps::networkMapFeed) )) val bobUser = User("bobUser", "testPassword2", permissions = setOf( startFlow(), - invokeRpc(CordaRPCOps::waitUntilNetworkReady), invokeRpc("vaultTrackBy"), invokeRpc(CordaRPCOps::networkMapFeed) )) @@ -44,6 +42,7 @@ class IntegrationTestingTutorial { startNode(providedName = ALICE.name, rpcUsers = listOf(aliceUser)), startNode(providedName = BOB.name, rpcUsers = listOf(bobUser)) ).transpose().getOrThrow() + // END 1 // START 2 @@ -52,9 +51,6 @@ class IntegrationTestingTutorial { val bobClient = bob.rpcClientToNode() val bobProxy = bobClient.start("bobUser", "testPassword2").proxy - - aliceProxy.waitUntilNetworkReady().getOrThrow() - bobProxy.waitUntilNetworkReady().getOrThrow() // END 2 // START 3 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 1cfb712ec8..b391d4cab7 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 @@ -8,14 +8,13 @@ import net.corda.core.serialization.CordaSerializable import net.corda.core.serialization.SerializationWhitelist import net.corda.core.transactions.SignedTransaction import net.corda.core.utilities.OpaqueBytes -import net.corda.core.utilities.getOrThrow import net.corda.finance.USD import net.corda.finance.contracts.asset.Cash import net.corda.finance.flows.CashExitFlow import net.corda.finance.flows.CashIssueFlow import net.corda.finance.flows.CashPaymentFlow -import net.corda.node.services.Permissions.Companion.startFlow import net.corda.node.services.Permissions.Companion.invokeRpc +import net.corda.node.services.Permissions.Companion.startFlow import net.corda.nodeapi.User import net.corda.testing.ALICE import net.corda.testing.DUMMY_NOTARY @@ -56,7 +55,6 @@ fun main(args: Array) { // START 2 val client = node.rpcClientToNode() val proxy = client.start("user", "password").proxy - proxy.waitUntilNetworkReady().getOrThrow() thread { generateTransactions(proxy) diff --git a/node/src/integration-test/kotlin/net/corda/node/services/AttachmentLoadingTests.kt b/node/src/integration-test/kotlin/net/corda/node/services/AttachmentLoadingTests.kt index 9f6ad6a4d3..738cd7488e 100644 --- a/node/src/integration-test/kotlin/net/corda/node/services/AttachmentLoadingTests.kt +++ b/node/src/integration-test/kotlin/net/corda/node/services/AttachmentLoadingTests.kt @@ -68,7 +68,6 @@ class AttachmentLoadingTests { startNode(providedName = bankAName, rpcUsers = listOf(adminUser)), startNode(providedName = bankBName, rpcUsers = listOf(adminUser)) ).transpose().getOrThrow() // Wait for all nodes to start up. - nodes.forEach { it.rpc.waitUntilNetworkReady().getOrThrow() } return nodes } 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 0b77d4c865..c1a9d461fd 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 @@ -8,8 +8,8 @@ import net.corda.core.utilities.getOrThrow import net.corda.finance.DOLLARS import net.corda.finance.contracts.asset.Cash import net.corda.finance.flows.CashIssueAndPaymentFlow -import net.corda.node.services.Permissions.Companion.startFlow import net.corda.node.services.Permissions.Companion.invokeRpc +import net.corda.node.services.Permissions.Companion.startFlow import net.corda.nodeapi.User import net.corda.testing.* import net.corda.testing.driver.driver @@ -20,7 +20,6 @@ class BankOfCordaRPCClientTest { fun `issuer flow via RPC`() { val commonPermissions = setOf( invokeRpc("vaultTrackByCriteria"), - invokeRpc(CordaRPCOps::waitUntilNetworkReady), invokeRpc(CordaRPCOps::wellKnownPartyFromX500Name), invokeRpc(CordaRPCOps::notaryIdentities) ) @@ -40,8 +39,6 @@ class BankOfCordaRPCClientTest { // Big Corporation RPC Client val bigCorpClient = nodeBigCorporation.rpcClientToNode() val bigCorpProxy = bigCorpClient.start("bigCorpCFO", "password2").proxy - bocProxy.waitUntilNetworkReady().getOrThrow() - bigCorpProxy.waitUntilNetworkReady().getOrThrow() // Register for Bank of Corda Vault updates val criteria = QueryCriteria.VaultQueryCriteria(status = Vault.StateStatus.ALL) 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 2183cb8ac7..86f8669777 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 @@ -960,7 +960,7 @@ class DriverDSL( if (process.isAlive) null else process } establishRpc(configuration, processDeathFuture).flatMap { rpc -> - // Call waitUntilNetworkReady in background in case RPC is failing over: + // Check for all nodes to have all other nodes in background in case RPC is failing over: val forked = executorService.fork { allNodesConnected(rpc) } 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 d1913e2c01..869df2ab64 100644 --- a/tools/explorer/src/main/kotlin/net/corda/explorer/ExplorerSimulation.kt +++ b/tools/explorer/src/main/kotlin/net/corda/explorer/ExplorerSimulation.kt @@ -127,10 +127,6 @@ class ExplorerSimulation(val options: OptionSet) { bobNode.nodeInfo.legalIdentities.first() to bobRPC, issuerNodeGBP.nodeInfo.legalIdentities.first() to issuerRPCGBP, issuerNodeUSD.nodeInfo.legalIdentities.first() to issuerRPCUSD)) - - listOf(aliceRPC, bobRPC, issuerRPCGBP, issuerRPCUSD).map { - it.waitUntilNetworkReady().getOrThrow() - } } private fun startSimulation(eventGenerator: EventGenerator, maxIterations: Int) { From 3bb018a5cef14ec8da1026b80f1a788aaaa749c1 Mon Sep 17 00:00:00 2001 From: Shams Asari Date: Sun, 5 Nov 2017 12:34:42 +0000 Subject: [PATCH 20/24] Removed the ability to manually start notary nodes from the driver and MockNetwork. Instead by default a single notary is automatically started. This can be customised at creation time of the driver and MockNetwork. This more accurately models the concept of network parameters in a CZ. Also added helper methods to retrieve this default notary. --- .../corda/client/jfx/NodeMonitorModelTest.kt | 73 +++-- .../confidential/IdentitySyncFlowTests.kt | 23 +- .../confidential/SwapIdentitiesFlowTests.kt | 29 +- .../core/node/services/NetworkMapCache.kt | 2 +- .../net/corda/core/flows/FlowsInJavaTest.java | 2 - .../net/corda/core/flows/AttachmentTests.kt | 4 +- .../core/flows/CollectSignaturesFlowTests.kt | 19 +- .../core/flows/ContractUpgradeFlowTest.kt | 3 +- .../net/corda/core/flows/FinalityFlowTests.kt | 3 +- .../corda/core/flows/ReceiveAllFlowTests.kt | 4 +- .../internal/ResolveTransactionsFlowTest.kt | 20 +- .../corda/docs/IntegrationTestingTutorial.kt | 3 +- .../net/corda/docs/ClientRpcTutorial.kt | 4 +- .../mocknetwork/TutorialMockNetwork.kt | 2 - .../net/corda/docs/CustomVaultQueryTest.kt | 16 +- .../docs/FxTransactionBuildTutorialTest.kt | 12 +- .../WorkflowTransactionBuildTutorialTest.kt | 15 +- .../contracts/asset/CashSelectionH2Test.kt | 41 ++- .../corda/finance/flows/CashExitFlowTests.kt | 10 +- .../corda/finance/flows/CashIssueFlowTests.kt | 7 +- .../finance/flows/CashPaymentFlowTests.kt | 16 +- .../net/corda/node/NodePerformanceTests.kt | 25 +- .../node/services/AttachmentLoadingTests.kt | 46 +-- .../node/services/DistributedServiceTests.kt | 78 +++--- .../node/services/RaftNotaryServiceTests.kt | 15 +- .../statemachine/LargeTransactionsTest.kt | 4 +- .../services/messaging/P2PMessagingTest.kt | 20 +- .../test/node/NodeStatePersistenceTests.kt | 4 +- .../network/PersistentNetworkMapCache.kt | 3 +- .../net/corda/node/CordaRPCOpsImplTest.kt | 3 - .../corda/node/internal/CordaServiceTest.kt | 6 +- .../node/messaging/TwoPartyTradeFlowTests.kt | 17 +- .../corda/node/services/NotaryChangeTests.kt | 34 +-- .../services/events/ScheduledFlowTests.kt | 2 - .../services/network/NetworkMapCacheTest.kt | 30 +- .../services/schema/NodeSchemaServiceTest.kt | 11 +- .../statemachine/FlowFrameworkTests.kt | 6 +- .../transactions/NotaryServiceTests.kt | 22 +- .../ValidatingNotaryServiceTests.kt | 22 +- .../attachmentdemo/AttachmentDemoTest.kt | 8 +- .../kotlin/net/corda/attachmentdemo/Main.kt | 2 - .../net/corda/bank/BankOfCordaHttpAPITest.kt | 11 +- .../corda/bank/BankOfCordaRPCClientTest.kt | 9 +- .../net/corda/bank/BankOfCordaDriver.kt | 4 +- .../src/test/kotlin/net/corda/irs/Main.kt | 13 +- .../kotlin/net/corda/irs/IRSDemoTest.kt | 22 +- .../net/corda/test/spring/SpringDriver.kt | 6 +- .../corda/netmap/simulation/IRSSimulation.kt | 5 +- .../net/corda/netmap/simulation/Simulation.kt | 4 +- .../net/corda/vega/SimmValuationTest.kt | 5 +- .../src/test/kotlin/net/corda/vega/Main.kt | 15 +- .../net/corda/traderdemo/TraderDemoTest.kt | 8 +- .../test/kotlin/net/corda/traderdemo/Main.kt | 2 - .../corda/testing/FlowStackSnapshotTest.kt | 1 - .../net/corda/testing/driver/DriverTests.kt | 19 +- .../net/corda/testing/DriverConstants.kt | 33 +-- .../kotlin/net/corda/testing/RPCDriver.kt | 5 +- .../kotlin/net/corda/testing/driver/Driver.kt | 263 ++++++++++-------- .../kotlin/net/corda/testing/node/MockNode.kt | 120 ++++---- .../net/corda/testing/node/NotarySpec.kt | 23 ++ .../net/corda/explorer/ExplorerSimulation.kt | 24 +- .../net/corda/verifier/VerifierDriver.kt | 5 +- .../net/corda/verifier/VerifierTests.kt | 15 +- .../corda/webserver/WebserverDriverTests.kt | 2 +- 64 files changed, 637 insertions(+), 643 deletions(-) create mode 100644 testing/node-driver/src/main/kotlin/net/corda/testing/node/NotarySpec.kt 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 c6af5cc734..238d6fd05a 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 @@ -11,7 +11,6 @@ import net.corda.core.flows.StateMachineRunId import net.corda.core.identity.CordaX500Name import net.corda.core.identity.Party import net.corda.core.internal.bufferUntilSubscribed -import net.corda.core.internal.concurrent.transpose import net.corda.core.messaging.CordaRPCOps import net.corda.core.messaging.StateMachineTransactionMapping import net.corda.core.messaging.StateMachineUpdate @@ -50,44 +49,44 @@ class NodeMonitorModelTest { private lateinit var vaultUpdates: Observable> private lateinit var networkMapUpdates: Observable private lateinit var newNode: (CordaX500Name) -> NodeInfo - private fun setup(runTest: () -> Unit) = driver(extraCordappPackagesToScan = listOf("net.corda.finance")) { - val cashUser = User("user1", "test", permissions = setOf( - startFlow(), - startFlow(), - startFlow(), - invokeRpc(CordaRPCOps::notaryIdentities), - invokeRpc("vaultTrackBy"), - invokeRpc("vaultQueryBy"), - invokeRpc(CordaRPCOps::internalVerifiedTransactionsFeed), - invokeRpc(CordaRPCOps::stateMachineRecordedTransactionMappingFeed), - invokeRpc(CordaRPCOps::stateMachinesFeed), - invokeRpc(CordaRPCOps::networkMapFeed)) - ) - val (notaryHandle, aliceNodeHandle) = listOf( - startNotaryNode(DUMMY_NOTARY.name, validating = false), - startNode(providedName = ALICE.name, rpcUsers = listOf(cashUser)) - ).transpose().getOrThrow() - aliceNode = aliceNodeHandle.nodeInfo - newNode = { nodeName -> startNode(providedName = nodeName).getOrThrow().nodeInfo } - val monitor = NodeMonitorModel() - stateMachineTransactionMapping = monitor.stateMachineTransactionMapping.bufferUntilSubscribed() - stateMachineUpdates = monitor.stateMachineUpdates.bufferUntilSubscribed() - progressTracking = monitor.progressTracking.bufferUntilSubscribed() - transactions = monitor.transactions.bufferUntilSubscribed() - vaultUpdates = monitor.vaultUpdates.bufferUntilSubscribed() - networkMapUpdates = monitor.networkMap.bufferUntilSubscribed() - monitor.register(aliceNodeHandle.configuration.rpcAddress!!, cashUser.username, cashUser.password) - rpc = monitor.proxyObservable.value!! - notaryParty = notaryHandle.nodeInfo.legalIdentities[0] + private fun setup(runTest: () -> Unit) { + driver(extraCordappPackagesToScan = listOf("net.corda.finance")) { + val cashUser = User("user1", "test", permissions = setOf( + startFlow(), + startFlow(), + startFlow(), + invokeRpc(CordaRPCOps::notaryIdentities), + invokeRpc("vaultTrackBy"), + invokeRpc("vaultQueryBy"), + invokeRpc(CordaRPCOps::internalVerifiedTransactionsFeed), + invokeRpc(CordaRPCOps::stateMachineRecordedTransactionMappingFeed), + invokeRpc(CordaRPCOps::stateMachinesFeed), + invokeRpc(CordaRPCOps::networkMapFeed)) + ) + val aliceNodeHandle = startNode(providedName = ALICE.name, rpcUsers = listOf(cashUser)).getOrThrow() + aliceNode = aliceNodeHandle.nodeInfo + newNode = { nodeName -> startNode(providedName = nodeName).getOrThrow().nodeInfo } + val monitor = NodeMonitorModel() + stateMachineTransactionMapping = monitor.stateMachineTransactionMapping.bufferUntilSubscribed() + stateMachineUpdates = monitor.stateMachineUpdates.bufferUntilSubscribed() + progressTracking = monitor.progressTracking.bufferUntilSubscribed() + transactions = monitor.transactions.bufferUntilSubscribed() + vaultUpdates = monitor.vaultUpdates.bufferUntilSubscribed() + networkMapUpdates = monitor.networkMap.bufferUntilSubscribed() - val bobNodeHandle = startNode(providedName = BOB.name, rpcUsers = listOf(cashUser)).getOrThrow() - bobNode = bobNodeHandle.nodeInfo - val monitorBob = NodeMonitorModel() - stateMachineUpdatesBob = monitorBob.stateMachineUpdates.bufferUntilSubscribed() - monitorBob.register(bobNodeHandle.configuration.rpcAddress!!, cashUser.username, cashUser.password) - rpcBob = monitorBob.proxyObservable.value!! - runTest() + monitor.register(aliceNodeHandle.configuration.rpcAddress!!, cashUser.username, cashUser.password) + rpc = monitor.proxyObservable.value!! + notaryParty = defaultNotaryIdentity + + val bobNodeHandle = startNode(providedName = BOB.name, rpcUsers = listOf(cashUser)).getOrThrow() + bobNode = bobNodeHandle.nodeInfo + val monitorBob = NodeMonitorModel() + stateMachineUpdatesBob = monitorBob.stateMachineUpdates.bufferUntilSubscribed() + monitorBob.register(bobNodeHandle.configuration.rpcAddress!!, cashUser.username, cashUser.password) + rpcBob = monitorBob.proxyObservable.value!! + runTest() + } } @Test diff --git a/confidential-identities/src/test/kotlin/net/corda/confidential/IdentitySyncFlowTests.kt b/confidential-identities/src/test/kotlin/net/corda/confidential/IdentitySyncFlowTests.kt index b760817afe..798a954708 100644 --- a/confidential-identities/src/test/kotlin/net/corda/confidential/IdentitySyncFlowTests.kt +++ b/confidential-identities/src/test/kotlin/net/corda/confidential/IdentitySyncFlowTests.kt @@ -14,8 +14,11 @@ import net.corda.finance.DOLLARS import net.corda.finance.contracts.asset.Cash import net.corda.finance.flows.CashIssueAndPaymentFlow import net.corda.finance.flows.CashPaymentFlow -import net.corda.testing.* +import net.corda.testing.ALICE_NAME +import net.corda.testing.BOB_NAME +import net.corda.testing.CHARLIE_NAME import net.corda.testing.node.MockNetwork +import net.corda.testing.singleIdentity import org.junit.After import org.junit.Before import org.junit.Test @@ -24,12 +27,16 @@ import kotlin.test.assertNotNull import kotlin.test.assertNull class IdentitySyncFlowTests { - lateinit var mockNet: MockNetwork + private lateinit var mockNet: MockNetwork @Before fun before() { // We run this in parallel threads to help catch any race conditions that may exist. - mockNet = MockNetwork(networkSendManuallyPumped = false, threadPerNode = true, cordappPackages = listOf("net.corda.finance.contracts.asset")) + mockNet = MockNetwork( + networkSendManuallyPumped = false, + threadPerNode = true, + cordappPackages = listOf("net.corda.finance.contracts.asset") + ) } @After @@ -40,12 +47,11 @@ class IdentitySyncFlowTests { @Test fun `sync confidential identities`() { // Set up values we'll need - val notaryNode = mockNet.createNotaryNode() val aliceNode = mockNet.createPartyNode(ALICE_NAME) val bobNode = mockNet.createPartyNode(BOB_NAME) val alice: Party = aliceNode.info.singleIdentity() val bob: Party = bobNode.info.singleIdentity() - val notary = notaryNode.services.getDefaultNotary() + val notary = mockNet.defaultNotaryIdentity bobNode.internals.registerInitiatedFlow(Receive::class.java) // Alice issues then pays some cash to a new confidential identity that Bob doesn't know about @@ -70,14 +76,13 @@ class IdentitySyncFlowTests { @Test fun `don't offer other's identities confidential identities`() { // Set up values we'll need - val notaryNode = mockNet.createNotaryNode() val aliceNode = mockNet.createPartyNode(ALICE_NAME) val bobNode = mockNet.createPartyNode(BOB_NAME) val charlieNode = mockNet.createPartyNode(CHARLIE_NAME) val alice: Party = aliceNode.info.singleIdentity() val bob: Party = bobNode.info.singleIdentity() val charlie: Party = charlieNode.info.singleIdentity() - val notary = notaryNode.services.getDefaultNotary() + val notary = mockNet.defaultNotaryIdentity bobNode.internals.registerInitiatedFlow(Receive::class.java) // Charlie issues then pays some cash to a new confidential identity @@ -105,7 +110,7 @@ class IdentitySyncFlowTests { * Very lightweight wrapping flow to trigger the counterparty flow that receives the identities. */ @InitiatingFlow - class Initiator(val otherSide: Party, val tx: WireTransaction): FlowLogic() { + class Initiator(private val otherSide: Party, private val tx: WireTransaction): FlowLogic() { @Suspendable override fun call(): Boolean { val session = initiateFlow(otherSide) @@ -116,7 +121,7 @@ class IdentitySyncFlowTests { } @InitiatedBy(IdentitySyncFlowTests.Initiator::class) - class Receive(val otherSideSession: FlowSession): FlowLogic() { + class Receive(private val otherSideSession: FlowSession): FlowLogic() { @Suspendable override fun call() { subFlow(IdentitySyncFlow.Receive(otherSideSession)) diff --git a/confidential-identities/src/test/kotlin/net/corda/confidential/SwapIdentitiesFlowTests.kt b/confidential-identities/src/test/kotlin/net/corda/confidential/SwapIdentitiesFlowTests.kt index d323b38b30..6fc8270b2d 100644 --- a/confidential-identities/src/test/kotlin/net/corda/confidential/SwapIdentitiesFlowTests.kt +++ b/confidential-identities/src/test/kotlin/net/corda/confidential/SwapIdentitiesFlowTests.kt @@ -6,17 +6,22 @@ import net.corda.core.identity.Party import net.corda.core.utilities.getOrThrow import net.corda.testing.* import net.corda.testing.node.MockNetwork +import org.junit.Before import org.junit.Test import kotlin.test.* class SwapIdentitiesFlowTests { + private lateinit var mockNet: MockNetwork + + @Before + fun setup() { + // We run this in parallel threads to help catch any race conditions that may exist. + mockNet = MockNetwork(networkSendManuallyPumped = false, threadPerNode = true) + } + @Test fun `issue key`() { - // We run this in parallel threads to help catch any race conditions that may exist. - val mockNet = MockNetwork(threadPerNode = true) - // Set up values we'll need - mockNet.createNotaryNode() val aliceNode = mockNet.createPartyNode(ALICE.name) val bobNode = mockNet.createPartyNode(BOB.name) val alice = aliceNode.info.singleIdentity() @@ -52,19 +57,16 @@ class SwapIdentitiesFlowTests { */ @Test fun `verifies identity name`() { - // We run this in parallel threads to help catch any race conditions that may exist. - val mockNet = MockNetwork(threadPerNode = true) - // Set up values we'll need - val notaryNode = mockNet.createNotaryNode() val aliceNode = mockNet.createPartyNode(ALICE.name) val bobNode = mockNet.createPartyNode(BOB.name) + val charlieNode = mockNet.createPartyNode(CHARLIE.name) val bob: Party = bobNode.services.myInfo.singleIdentity() - val notBob = notaryNode.database.transaction { - notaryNode.services.keyManagementService.freshKeyAndCert(notaryNode.services.myInfo.chooseIdentityAndCert(), false) + val notBob = charlieNode.database.transaction { + charlieNode.services.keyManagementService.freshKeyAndCert(charlieNode.services.myInfo.chooseIdentityAndCert(), false) } val sigData = SwapIdentitiesFlow.buildDataToSign(notBob) - val signature = notaryNode.services.keyManagementService.sign(sigData, notBob.owningKey) + val signature = charlieNode.services.keyManagementService.sign(sigData, notBob.owningKey) assertFailsWith("Certificate subject must match counterparty's well known identity.") { SwapIdentitiesFlow.validateAndRegisterIdentity(aliceNode.services.identityService, bob, notBob, signature.withoutKey()) } @@ -77,11 +79,8 @@ class SwapIdentitiesFlowTests { */ @Test fun `verifies signature`() { - // We run this in parallel threads to help catch any race conditions that may exist. - val mockNet = MockNetwork(threadPerNode = true) - // Set up values we'll need - val notaryNode = mockNet.createNotaryNode() + val notaryNode = mockNet.defaultNotaryNode val aliceNode = mockNet.createPartyNode(ALICE.name) val bobNode = mockNet.createPartyNode(BOB.name) val bob: Party = bobNode.services.myInfo.singleIdentity() diff --git a/core/src/main/kotlin/net/corda/core/node/services/NetworkMapCache.kt b/core/src/main/kotlin/net/corda/core/node/services/NetworkMapCache.kt index d1bb9fd503..69ab1a7307 100644 --- a/core/src/main/kotlin/net/corda/core/node/services/NetworkMapCache.kt +++ b/core/src/main/kotlin/net/corda/core/node/services/NetworkMapCache.kt @@ -120,7 +120,7 @@ interface NetworkMapCacheBase { /** * Returns true if and only if the given [Party] is validating notary. For every party that is a validating notary, - * [isNotary] is only true. + * [isNotary] is also true. * @see isNotary */ fun isValidatingNotary(party: Party): Boolean 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 6e836dbaba..ab03890365 100644 --- a/core/src/test/java/net/corda/core/flows/FlowsInJavaTest.java +++ b/core/src/test/java/net/corda/core/flows/FlowsInJavaTest.java @@ -18,14 +18,12 @@ import static org.assertj.core.api.AssertionsForClassTypes.assertThat; import static org.junit.Assert.fail; public class FlowsInJavaTest { - private final MockNetwork mockNet = new MockNetwork(); private StartedNode aliceNode; private StartedNode bobNode; @Before public void setUp() throws Exception { - mockNet.createNotaryNode(); aliceNode = mockNet.createPartyNode(TestConstants.getALICE().getName()); bobNode = mockNet.createPartyNode(TestConstants.getBOB().getName()); mockNet.runNetwork(); 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 1824be00a9..f634c2fd8b 100644 --- a/core/src/test/kotlin/net/corda/core/flows/AttachmentTests.kt +++ b/core/src/test/kotlin/net/corda/core/flows/AttachmentTests.kt @@ -110,11 +110,11 @@ class AttachmentTests { @Test fun maliciousResponse() { // Make a node that doesn't do sanity checking at load time. - val aliceNode = mockNet.createNotaryNode(MockNodeParameters(legalName = ALICE.name), nodeFactory = { args -> + val aliceNode = mockNet.createNode(MockNodeParameters(legalName = ALICE.name), nodeFactory = { args -> object : MockNetwork.MockNode(args) { override fun start() = super.start().apply { attachments.checkAttachmentsOnLoad = false } } - }, validating = false) + }) val bobNode = mockNet.createNode(MockNodeParameters(legalName = BOB.name)) mockNet.runNetwork() val alice = aliceNode.services.myInfo.identityFromX500Name(ALICE_NAME) 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 9ecea5940b..75e14ecff0 100644 --- a/core/src/test/kotlin/net/corda/core/flows/CollectSignaturesFlowTests.kt +++ b/core/src/test/kotlin/net/corda/core/flows/CollectSignaturesFlowTests.kt @@ -27,19 +27,18 @@ class CollectSignaturesFlowTests { private val cordappPackages = listOf("net.corda.testing.contracts") } - lateinit var mockNet: MockNetwork - lateinit var aliceNode: StartedNode - lateinit var bobNode: StartedNode - lateinit var charlieNode: StartedNode - lateinit var alice: Party - lateinit var bob: Party - lateinit var charlie: Party - lateinit var notary: Party + private lateinit var mockNet: MockNetwork + private lateinit var aliceNode: StartedNode + private lateinit var bobNode: StartedNode + private lateinit var charlieNode: StartedNode + private lateinit var alice: Party + private lateinit var bob: Party + private lateinit var charlie: Party + private lateinit var notary: Party @Before fun setup() { mockNet = MockNetwork(cordappPackages = cordappPackages) - val notaryNode = mockNet.createNotaryNode() aliceNode = mockNet.createPartyNode(ALICE.name) bobNode = mockNet.createPartyNode(BOB.name) charlieNode = mockNet.createPartyNode(CHARLIE.name) @@ -47,7 +46,7 @@ class CollectSignaturesFlowTests { alice = aliceNode.info.singleIdentity() bob = bobNode.info.singleIdentity() charlie = charlieNode.info.singleIdentity() - notary = notaryNode.services.getDefaultNotary() + notary = mockNet.defaultNotaryIdentity } @After 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 52ad1049c4..e2ec9b68d5 100644 --- a/core/src/test/kotlin/net/corda/core/flows/ContractUpgradeFlowTest.kt +++ b/core/src/test/kotlin/net/corda/core/flows/ContractUpgradeFlowTest.kt @@ -41,14 +41,13 @@ class ContractUpgradeFlowTest { @Before fun setup() { mockNet = MockNetwork(cordappPackages = listOf("net.corda.testing.contracts", "net.corda.finance.contracts.asset", "net.corda.core.flows")) - val notaryNode = mockNet.createNotaryNode() aliceNode = mockNet.createPartyNode(ALICE.name) bobNode = mockNet.createPartyNode(BOB.name) // Process registration mockNet.runNetwork() - notary = notaryNode.services.getDefaultNotary() + notary = mockNet.defaultNotaryIdentity } @After diff --git a/core/src/test/kotlin/net/corda/core/flows/FinalityFlowTests.kt b/core/src/test/kotlin/net/corda/core/flows/FinalityFlowTests.kt index 1bc496db6c..167c6a968f 100644 --- a/core/src/test/kotlin/net/corda/core/flows/FinalityFlowTests.kt +++ b/core/src/test/kotlin/net/corda/core/flows/FinalityFlowTests.kt @@ -26,7 +26,6 @@ class FinalityFlowTests { @Before fun setup() { mockNet = MockNetwork(cordappPackages = listOf("net.corda.finance.contracts.asset")) - val notaryNode = mockNet.createNotaryNode() val aliceNode = mockNet.createPartyNode(ALICE_NAME) val bobNode = mockNet.createPartyNode(BOB_NAME) mockNet.runNetwork() @@ -34,7 +33,7 @@ class FinalityFlowTests { bobServices = bobNode.services alice = aliceNode.info.singleIdentity() bob = bobNode.info.singleIdentity() - notary = notaryNode.services.getDefaultNotary() + notary = aliceServices.getDefaultNotary() } @After diff --git a/core/src/test/kotlin/net/corda/core/flows/ReceiveAllFlowTests.kt b/core/src/test/kotlin/net/corda/core/flows/ReceiveAllFlowTests.kt index cfadcf4bf5..9b1ee8161c 100644 --- a/core/src/test/kotlin/net/corda/core/flows/ReceiveAllFlowTests.kt +++ b/core/src/test/kotlin/net/corda/core/flows/ReceiveAllFlowTests.kt @@ -13,7 +13,7 @@ import org.junit.Test class ReceiveMultipleFlowTests { @Test fun `receive all messages in parallel using map style`() { - network(3) { nodes, _ -> + network(3) { nodes -> val doubleValue = 5.0 nodes[1].registerAnswer(AlgorithmDefinition::class, doubleValue) val stringValue = "Thriller" @@ -30,7 +30,7 @@ class ReceiveMultipleFlowTests { @Test fun `receive all messages in parallel using list style`() { - network(3) { nodes, _ -> + network(3) { nodes -> val value1 = 5.0 nodes[1].registerAnswer(ParallelAlgorithmList::class, value1) val value2 = 6.0 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 e7f43c3096..e5e67fff0b 100644 --- a/core/src/test/kotlin/net/corda/core/internal/ResolveTransactionsFlowTest.kt +++ b/core/src/test/kotlin/net/corda/core/internal/ResolveTransactionsFlowTest.kt @@ -8,7 +8,9 @@ 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.* +import net.corda.testing.MEGA_CORP +import net.corda.testing.MINI_CORP +import net.corda.testing.chooseIdentity import net.corda.testing.contracts.DummyContract import net.corda.testing.getDefaultNotary import net.corda.testing.node.MockNetwork @@ -26,18 +28,18 @@ import kotlin.test.assertNull // DOCSTART 3 class ResolveTransactionsFlowTest { - lateinit var mockNet: MockNetwork - lateinit var notaryNode: StartedNode - lateinit var megaCorpNode: StartedNode - lateinit var miniCorpNode: StartedNode - lateinit var megaCorp: Party - lateinit var miniCorp: Party - lateinit var notary: Party + private lateinit var mockNet: MockNetwork + private lateinit var notaryNode: StartedNode + private lateinit var megaCorpNode: StartedNode + private lateinit var miniCorpNode: StartedNode + private lateinit var megaCorp: Party + private lateinit var miniCorp: Party + private lateinit var notary: Party @Before fun setup() { mockNet = MockNetwork(cordappPackages = listOf("net.corda.testing.contracts")) - notaryNode = mockNet.createNotaryNode() + notaryNode = mockNet.defaultNotaryNode megaCorpNode = mockNet.createPartyNode(MEGA_CORP.name) miniCorpNode = mockNet.createPartyNode(MINI_CORP.name) megaCorpNode.internals.registerInitiatedFlow(TestResponseFlow::class.java) 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 c9be1c2353..9b192038bf 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 @@ -37,8 +37,7 @@ class IntegrationTestingTutorial { invokeRpc("vaultTrackBy"), invokeRpc(CordaRPCOps::networkMapFeed) )) - val (_, alice, bob) = listOf( - startNotaryNode(DUMMY_NOTARY.name), + val (alice, bob) = listOf( startNode(providedName = ALICE.name, rpcUsers = listOf(aliceUser)), startNode(providedName = BOB.name, rpcUsers = listOf(bobUser)) ).transpose().getOrThrow() 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 b391d4cab7..1f20376fbd 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 @@ -1,3 +1,5 @@ +@file:Suppress("unused") + package net.corda.docs import net.corda.core.contracts.Amount @@ -17,7 +19,6 @@ import net.corda.node.services.Permissions.Companion.invokeRpc import net.corda.node.services.Permissions.Companion.startFlow import net.corda.nodeapi.User import net.corda.testing.ALICE -import net.corda.testing.DUMMY_NOTARY import net.corda.testing.driver.driver import org.graphstream.graph.Edge import org.graphstream.graph.Node @@ -48,7 +49,6 @@ fun main(args: Array) { invokeRpc(CordaRPCOps::nodeInfo) )) driver(driverDirectory = baseDirectory, extraCordappPackagesToScan = listOf("net.corda.finance")) { - startNotaryNode(DUMMY_NOTARY.name) val node = startNode(providedName = ALICE.name, rpcUsers = listOf(user)).get() // END 1 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 9e86b57aab..56d33e5676 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 @@ -55,7 +55,6 @@ class TutorialMockNetwork { } lateinit private var mockNet: MockNetwork - lateinit private var notary: StartedNode lateinit private var nodeA: StartedNode lateinit private var nodeB: StartedNode @@ -66,7 +65,6 @@ class TutorialMockNetwork { @Before fun setUp() { mockNet = MockNetwork() - notary = mockNet.createNotaryNode() nodeA = mockNet.createPartyNode() nodeB = mockNet.createPartyNode() 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 a4639256f3..0973b3541b 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 @@ -10,7 +10,8 @@ import net.corda.finance.contracts.getCashBalances import net.corda.finance.flows.CashIssueFlow import net.corda.finance.schemas.CashSchemaV1 import net.corda.node.internal.StartedNode -import net.corda.testing.* +import net.corda.testing.chooseIdentity +import net.corda.testing.getDefaultNotary import net.corda.testing.node.MockNetwork import org.junit.After import org.junit.Assert @@ -19,23 +20,20 @@ import org.junit.Test import java.util.* class CustomVaultQueryTest { - - lateinit var mockNet: MockNetwork - lateinit var nodeA: StartedNode - lateinit var nodeB: StartedNode - lateinit var notary: Party + private lateinit var mockNet: MockNetwork + private lateinit var nodeA: StartedNode + private lateinit var nodeB: StartedNode + private lateinit var notary: Party @Before fun setup() { - mockNet = MockNetwork( - threadPerNode = true, + mockNet = MockNetwork(threadPerNode = true, cordappPackages = listOf( "net.corda.finance.contracts.asset", CashSchemaV1::class.packageName, "net.corda.docs" ) ) - mockNet.createNotaryNode() nodeA = mockNet.createPartyNode() nodeB = mockNet.createPartyNode() nodeA.internals.registerInitiatedFlow(TopupIssuerFlow.TopupIssuer::class.java) 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 5e31ac39ba..cb2a8f29eb 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 @@ -10,7 +10,8 @@ import net.corda.finance.contracts.getCashBalances import net.corda.finance.flows.CashIssueFlow import net.corda.finance.schemas.CashSchemaV1 import net.corda.node.internal.StartedNode -import net.corda.testing.* +import net.corda.testing.chooseIdentity +import net.corda.testing.getDefaultNotary import net.corda.testing.node.MockNetwork import org.junit.After import org.junit.Before @@ -18,15 +19,14 @@ import org.junit.Test import kotlin.test.assertEquals class FxTransactionBuildTutorialTest { - lateinit var mockNet: MockNetwork - lateinit var nodeA: StartedNode - lateinit var nodeB: StartedNode - lateinit var notary: Party + private lateinit var mockNet: MockNetwork + private lateinit var nodeA: StartedNode + private lateinit var nodeB: StartedNode + private lateinit var notary: Party @Before fun setup() { mockNet = MockNetwork(threadPerNode = true, cordappPackages = listOf("net.corda.finance.contracts.asset", CashSchemaV1::class.packageName)) - mockNet.createNotaryNode() nodeA = mockNet.createPartyNode() nodeB = mockNet.createPartyNode() nodeB.internals.registerInitiatedFlow(ForeignExchangeRemoteFlow::class.java) diff --git a/docs/source/example-code/src/test/kotlin/net/corda/docs/WorkflowTransactionBuildTutorialTest.kt b/docs/source/example-code/src/test/kotlin/net/corda/docs/WorkflowTransactionBuildTutorialTest.kt index 9f98082730..1cbd83a282 100644 --- a/docs/source/example-code/src/test/kotlin/net/corda/docs/WorkflowTransactionBuildTutorialTest.kt +++ b/docs/source/example-code/src/test/kotlin/net/corda/docs/WorkflowTransactionBuildTutorialTest.kt @@ -10,7 +10,8 @@ import net.corda.core.node.services.vault.QueryCriteria import net.corda.core.toFuture import net.corda.core.utilities.getOrThrow import net.corda.node.services.api.StartedNodeServices -import net.corda.testing.* +import net.corda.testing.ALICE_NAME +import net.corda.testing.BOB_NAME import net.corda.testing.node.MockNetwork import org.junit.After import org.junit.Before @@ -18,11 +19,11 @@ import org.junit.Test import kotlin.test.assertEquals class WorkflowTransactionBuildTutorialTest { - lateinit var mockNet: MockNetwork - lateinit var aliceServices: StartedNodeServices - lateinit var bobServices: StartedNodeServices - lateinit var alice: Party - lateinit var bob: Party + private lateinit var mockNet: MockNetwork + private lateinit var aliceServices: StartedNodeServices + private lateinit var bobServices: StartedNodeServices + private lateinit var alice: Party + private lateinit var bob: Party // Helper method to locate the latest Vault version of a LinearState private inline fun ServiceHub.latest(ref: UniqueIdentifier): StateAndRef { @@ -33,8 +34,6 @@ class WorkflowTransactionBuildTutorialTest { @Before fun setup() { mockNet = MockNetwork(threadPerNode = true, cordappPackages = listOf("net.corda.docs")) - // While we don't use the notary, we need there to be one on the network - mockNet.createNotaryNode() val aliceNode = mockNet.createPartyNode(ALICE_NAME) val bobNode = mockNet.createPartyNode(BOB_NAME) aliceNode.internals.registerInitiatedFlow(RecordCompletionFlow::class.java) diff --git a/finance/src/test/kotlin/net/corda/finance/contracts/asset/CashSelectionH2Test.kt b/finance/src/test/kotlin/net/corda/finance/contracts/asset/CashSelectionH2Test.kt index 2a69ac0631..5dcde25664 100644 --- a/finance/src/test/kotlin/net/corda/finance/contracts/asset/CashSelectionH2Test.kt +++ b/finance/src/test/kotlin/net/corda/finance/contracts/asset/CashSelectionH2Test.kt @@ -1,41 +1,38 @@ package net.corda.finance.contracts.asset -import net.corda.core.internal.packageName import net.corda.core.utilities.getOrThrow import net.corda.finance.DOLLARS import net.corda.finance.flows.CashException import net.corda.finance.flows.CashPaymentFlow -import net.corda.finance.schemas.CashSchemaV1 -import net.corda.testing.chooseIdentity import net.corda.testing.node.MockNetwork import net.corda.testing.node.MockNodeParameters import org.assertj.core.api.Assertions.assertThatThrownBy +import org.junit.After import org.junit.Test - class CashSelectionH2Test { + private val mockNet = MockNetwork(threadPerNode = true, cordappPackages = listOf("net.corda.finance")) + + @After + fun cleanUp() { + mockNet.stopNodes() + } @Test fun `check does not hold connection over retries`() { - val mockNet = MockNetwork(threadPerNode = true, cordappPackages = listOf("net.corda.finance.contracts.asset", CashSchemaV1::class.packageName)) - try { - val notaryNode = mockNet.createNotaryNode() - val bankA = mockNet.createNode(MockNodeParameters(configOverrides = { existingConfig -> - // Tweak connections to be minimal to make this easier (1 results in a hung node during start up, so use 2 connections). - existingConfig.dataSourceProperties.setProperty("maximumPoolSize", "2") - })) - mockNet.startNodes() + val bankA = mockNet.createNode(MockNodeParameters(configOverrides = { + // Tweak connections to be minimal to make this easier (1 results in a hung node during start up, so use 2 connections). + it.dataSourceProperties.setProperty("maximumPoolSize", "2") + })) + val notary = mockNet.defaultNotaryIdentity - // Start more cash spends than we have connections. If spend leaks a connection on retry, we will run out of connections. - val flow1 = bankA.services.startFlow(CashPaymentFlow(amount = 100.DOLLARS, anonymous = false, recipient = notaryNode.info.chooseIdentity())) - val flow2 = bankA.services.startFlow(CashPaymentFlow(amount = 100.DOLLARS, anonymous = false, recipient = notaryNode.info.chooseIdentity())) - val flow3 = bankA.services.startFlow(CashPaymentFlow(amount = 100.DOLLARS, anonymous = false, recipient = notaryNode.info.chooseIdentity())) + // Start more cash spends than we have connections. If spend leaks a connection on retry, we will run out of connections. + val flow1 = bankA.services.startFlow(CashPaymentFlow(amount = 100.DOLLARS, anonymous = false, recipient = notary)) + val flow2 = bankA.services.startFlow(CashPaymentFlow(amount = 100.DOLLARS, anonymous = false, recipient = notary)) + val flow3 = bankA.services.startFlow(CashPaymentFlow(amount = 100.DOLLARS, anonymous = false, recipient = notary)) - assertThatThrownBy { flow1.resultFuture.getOrThrow() }.isInstanceOf(CashException::class.java) - assertThatThrownBy { flow2.resultFuture.getOrThrow() }.isInstanceOf(CashException::class.java) - assertThatThrownBy { flow3.resultFuture.getOrThrow() }.isInstanceOf(CashException::class.java) - } finally { - mockNet.stopNodes() - } + assertThatThrownBy { flow1.resultFuture.getOrThrow() }.isInstanceOf(CashException::class.java) + assertThatThrownBy { flow2.resultFuture.getOrThrow() }.isInstanceOf(CashException::class.java) + assertThatThrownBy { flow3.resultFuture.getOrThrow() }.isInstanceOf(CashException::class.java) } } \ No newline at end of file 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 94b3f83658..6483229909 100644 --- a/finance/src/test/kotlin/net/corda/finance/flows/CashExitFlowTests.kt +++ b/finance/src/test/kotlin/net/corda/finance/flows/CashExitFlowTests.kt @@ -7,7 +7,9 @@ import net.corda.finance.DOLLARS import net.corda.finance.`issued by` import net.corda.finance.contracts.asset.Cash import net.corda.node.internal.StartedNode -import net.corda.testing.* +import net.corda.testing.BOC +import net.corda.testing.chooseIdentity +import net.corda.testing.getDefaultNotary import net.corda.testing.node.InMemoryMessagingNetwork.ServicePeerAllocationStrategy.RoundRobin import net.corda.testing.node.MockNetwork import net.corda.testing.node.MockNetwork.MockNode @@ -23,15 +25,13 @@ class CashExitFlowTests { private val ref = OpaqueBytes.of(0x01) private lateinit var bankOfCordaNode: StartedNode private lateinit var bankOfCorda: Party - private lateinit var notaryNode: StartedNode private lateinit var notary: Party @Before fun start() { - mockNet = MockNetwork(servicePeerAllocationStrategy = RoundRobin(), cordappPackages = listOf("net.corda.finance.contracts.asset")) - notaryNode = mockNet.createNotaryNode() + mockNet = MockNetwork(servicePeerAllocationStrategy = RoundRobin(), + cordappPackages = listOf("net.corda.finance.contracts.asset")) bankOfCordaNode = mockNet.createPartyNode(BOC.name) - notary = notaryNode.services.getDefaultNotary() bankOfCorda = bankOfCordaNode.info.chooseIdentity() mockNet.runNetwork() 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 911a014d5f..552ebe0ebd 100644 --- a/finance/src/test/kotlin/net/corda/finance/flows/CashIssueFlowTests.kt +++ b/finance/src/test/kotlin/net/corda/finance/flows/CashIssueFlowTests.kt @@ -7,9 +7,8 @@ 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.chooseIdentity -import net.corda.testing.getDefaultNotary import net.corda.testing.BOC +import net.corda.testing.chooseIdentity import net.corda.testing.node.InMemoryMessagingNetwork.ServicePeerAllocationStrategy.RoundRobin import net.corda.testing.node.MockNetwork import net.corda.testing.node.MockNetwork.MockNode @@ -23,16 +22,14 @@ class CashIssueFlowTests { private lateinit var mockNet: MockNetwork private lateinit var bankOfCordaNode: StartedNode private lateinit var bankOfCorda: Party - private lateinit var notaryNode: StartedNode private lateinit var notary: Party @Before fun start() { mockNet = MockNetwork(servicePeerAllocationStrategy = RoundRobin(), cordappPackages = listOf("net.corda.finance.contracts.asset")) - notaryNode = mockNet.createNotaryNode() bankOfCordaNode = mockNet.createPartyNode(BOC.name) bankOfCorda = bankOfCordaNode.info.chooseIdentity() - notary = notaryNode.services.getDefaultNotary() + notary = mockNet.defaultNotaryIdentity mockNet.runNetwork() } 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 0c5b1e5efa..c515c886ed 100644 --- a/finance/src/test/kotlin/net/corda/finance/flows/CashPaymentFlowTests.kt +++ b/finance/src/test/kotlin/net/corda/finance/flows/CashPaymentFlowTests.kt @@ -26,18 +26,16 @@ class CashPaymentFlowTests { private val ref = OpaqueBytes.of(0x01) private lateinit var bankOfCordaNode: StartedNode private lateinit var bankOfCorda: Party - private lateinit var notaryNode: StartedNode - private lateinit var notary: Party + private lateinit var aliceNode: StartedNode @Before fun start() { mockNet = MockNetwork(servicePeerAllocationStrategy = RoundRobin(), cordappPackages = listOf("net.corda.finance.contracts.asset")) - notaryNode = mockNet.createNotaryNode() bankOfCordaNode = mockNet.createPartyNode(BOC.name) + aliceNode = mockNet.createPartyNode(ALICE.name) bankOfCorda = bankOfCordaNode.info.chooseIdentity() - notary = notaryNode.services.getDefaultNotary() - val future = bankOfCordaNode.services.startFlow(CashIssueFlow(initialBalance, ref, notary)).resultFuture mockNet.runNetwork() + val future = bankOfCordaNode.services.startFlow(CashIssueFlow(initialBalance, ref, mockNet.defaultNotaryIdentity)).resultFuture future.getOrThrow() } @@ -48,7 +46,7 @@ class CashPaymentFlowTests { @Test fun `pay some cash`() { - val payTo = notaryNode.info.chooseIdentity() + val payTo = aliceNode.info.chooseIdentity() val expectedPayment = 500.DOLLARS val expectedChange = 1500.DOLLARS @@ -56,7 +54,7 @@ class CashPaymentFlowTests { // Register for vault updates val criteria = QueryCriteria.VaultQueryCriteria(status = Vault.StateStatus.ALL) val (_, vaultUpdatesBoc) = bankOfCordaNode.services.vaultService.trackBy(criteria) - val (_, vaultUpdatesBankClient) = notaryNode.services.vaultService.trackBy(criteria) + val (_, vaultUpdatesBankClient) = aliceNode.services.vaultService.trackBy(criteria) val future = bankOfCordaNode.services.startFlow(CashPaymentFlow(expectedPayment, payTo)).resultFuture @@ -88,7 +86,7 @@ class CashPaymentFlowTests { @Test fun `pay more than we have`() { - val payTo = notaryNode.info.chooseIdentity() + val payTo = aliceNode.info.chooseIdentity() val expected = 4000.DOLLARS val future = bankOfCordaNode.services.startFlow(CashPaymentFlow(expected, payTo)).resultFuture @@ -100,7 +98,7 @@ class CashPaymentFlowTests { @Test fun `pay zero cash`() { - val payTo = notaryNode.info.chooseIdentity() + val payTo = aliceNode.info.chooseIdentity() val expected = 0.DOLLARS val future = bankOfCordaNode.services.startFlow(CashPaymentFlow(expected, payTo)).resultFuture 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 eac55b5544..abf4b1a06b 100644 --- a/node/src/integration-test/kotlin/net/corda/node/NodePerformanceTests.kt +++ b/node/src/integration-test/kotlin/net/corda/node/NodePerformanceTests.kt @@ -15,9 +15,9 @@ import net.corda.finance.flows.CashPaymentFlow import net.corda.node.services.Permissions.Companion.startFlow import net.corda.nodeapi.User import net.corda.testing.DUMMY_NOTARY -import net.corda.testing.chooseIdentity import net.corda.testing.driver.NodeHandle import net.corda.testing.driver.driver +import net.corda.testing.node.NotarySpec import net.corda.testing.performance.div import net.corda.testing.performance.startPublishingFixedRateInjector import net.corda.testing.performance.startReporter @@ -102,26 +102,25 @@ class NodePerformanceTests { @Test fun `self pay rate`() { - driver(startNodesInProcess = true, extraCordappPackagesToScan = listOf("net.corda.finance")) { - val a = startNotaryNode( - DUMMY_NOTARY.name, - rpcUsers = listOf(User("A", "A", setOf(startFlow(), startFlow()))) - ).getOrThrow() - a as NodeHandle.InProcess - val metricRegistry = startReporter(shutdownManager, a.node.services.monitoringService.metrics) - a.rpcClientToNode().use("A", "A") { connection -> - val notary = connection.proxy.notaryIdentities().first() + val user = User("A", "A", setOf(startFlow(), startFlow())) + driver( + 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(shutdownManager, notary.node.services.monitoringService.metrics) + notary.rpcClientToNode().use("A", "A") { connection -> println("ISSUING") val doneFutures = (1..100).toList().parallelStream().map { - connection.proxy.startFlow(::CashIssueFlow, 1.DOLLARS, OpaqueBytes.of(0), notary).returnValue + connection.proxy.startFlow(::CashIssueFlow, 1.DOLLARS, OpaqueBytes.of(0), defaultNotaryIdentity).returnValue }.toList() doneFutures.transpose().get() println("STARTING PAYMENT") startPublishingFixedRateInjector(metricRegistry, 8, 5.minutes, 100L / TimeUnit.SECONDS) { - connection.proxy.startFlow(::CashPaymentFlow, 1.DOLLARS, a.nodeInfo.chooseIdentity()).returnValue.get() + connection.proxy.startFlow(::CashPaymentFlow, 1.DOLLARS, defaultNotaryIdentity).returnValue.get() } } - } } } \ No newline at end of file diff --git a/node/src/integration-test/kotlin/net/corda/node/services/AttachmentLoadingTests.kt b/node/src/integration-test/kotlin/net/corda/node/services/AttachmentLoadingTests.kt index 738cd7488e..0b233b9fb3 100644 --- a/node/src/integration-test/kotlin/net/corda/node/services/AttachmentLoadingTests.kt +++ b/node/src/integration-test/kotlin/net/corda/node/services/AttachmentLoadingTests.kt @@ -13,20 +13,16 @@ import net.corda.core.internal.div import net.corda.core.internal.toLedgerTransaction import net.corda.core.serialization.SerializationFactory import net.corda.core.transactions.TransactionBuilder -import net.corda.core.utilities.OpaqueBytes import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.loggerFor -import net.corda.core.utilities.seconds import net.corda.node.internal.cordapp.CordappLoader import net.corda.node.internal.cordapp.CordappProviderImpl -import net.corda.nodeapi.User import net.corda.testing.DUMMY_BANK_A import net.corda.testing.DUMMY_NOTARY import net.corda.testing.SerializationEnvironmentRule import net.corda.testing.driver.DriverDSLExposedInterface import net.corda.testing.driver.NodeHandle import net.corda.testing.driver.driver -import net.corda.testing.eventually import net.corda.testing.node.MockServices import org.junit.Assert.assertEquals import org.junit.Before @@ -56,19 +52,15 @@ class AttachmentLoadingTests { val bankAName = CordaX500Name("BankA", "Zurich", "CH") val bankBName = CordaX500Name("BankB", "Zurich", "CH") - val notaryName = CordaX500Name("Notary", "Zurich", "CH") val flowInitiatorClass: Class> = Class.forName("net.corda.finance.contracts.isolated.IsolatedDummyFlow\$Initiator", true, URLClassLoader(arrayOf(isolatedJAR))) .asSubclass(FlowLogic::class.java) - private fun DriverDSLExposedInterface.createNotaryAndTwoNodes(): List { - val adminUser = User("admin", "admin", permissions = setOf("ALL")) - val nodes = listOf( - startNotaryNode(providedName = notaryName, rpcUsers = listOf(adminUser), validating = false), - startNode(providedName = bankAName, rpcUsers = listOf(adminUser)), - startNode(providedName = bankBName, rpcUsers = listOf(adminUser)) - ).transpose().getOrThrow() // Wait for all nodes to start up. - return nodes + private fun DriverDSLExposedInterface.createTwoNodes(): List { + return listOf( + startNode(providedName = bankAName), + startNode(providedName = bankBName) + ).transpose().getOrThrow() } private fun DriverDSLExposedInterface.installIsolatedCordappTo(nodeName: CordaX500Name) { @@ -81,15 +73,6 @@ class AttachmentLoadingTests { } } } - - // Due to cluster instability after nodes been started it may take some time to all the nodes to become available - // *and* discover each other to reliably communicate. Hence, eventual nature of the test. - // TODO: Remove this method and usages of it once NetworkMap service been re-worked - private fun eventuallyPassingTest(block: () -> Unit) { - eventually(30.seconds) { - block() - } - } } private lateinit var services: Services @@ -105,9 +88,8 @@ class AttachmentLoadingTests { val contractClass = appClassLoader.loadClass(ISOLATED_CONTRACT_ID).asSubclass(Contract::class.java) val generateInitialMethod = contractClass.getDeclaredMethod("generateInitial", PartyAndReference::class.java, Integer.TYPE, Party::class.java) val contract = contractClass.newInstance() - val txBuilder = generateInitialMethod.invoke(contract, PartyAndReference(DUMMY_BANK_A, OpaqueBytes(kotlin.ByteArray(1))), 1, DUMMY_NOTARY) as TransactionBuilder - val context = SerializationFactory.defaultFactory.defaultContext - .withClassLoader(appClassLoader) + val txBuilder = generateInitialMethod.invoke(contract, DUMMY_BANK_A.ref(1), 1, DUMMY_NOTARY) as TransactionBuilder + val context = SerializationFactory.defaultFactory.defaultContext.withClassLoader(appClassLoader) val ledgerTx = txBuilder.toLedgerTransaction(services, context) contract.verify(ledgerTx) @@ -121,11 +103,9 @@ class AttachmentLoadingTests { fun `test that attachments retrieved over the network are not used for code`() { driver(initialiseSerialization = false) { installIsolatedCordappTo(bankAName) - val (_, bankA, bankB) = createNotaryAndTwoNodes() - eventuallyPassingTest { - assertFailsWith("Party C=CH,L=Zurich,O=BankB rejected session request: Don't know net.corda.finance.contracts.isolated.IsolatedDummyFlow\$Initiator") { - bankA.rpc.startFlowDynamic(flowInitiatorClass, bankB.nodeInfo.legalIdentities.first()).returnValue.getOrThrow() - } + val (bankA, bankB) = createTwoNodes() + assertFailsWith("Party C=CH,L=Zurich,O=BankB rejected session request: Don't know net.corda.finance.contracts.isolated.IsolatedDummyFlow\$Initiator") { + bankA.rpc.startFlowDynamic(flowInitiatorClass, bankB.nodeInfo.legalIdentities.first()).returnValue.getOrThrow() } } } @@ -135,10 +115,8 @@ class AttachmentLoadingTests { driver(initialiseSerialization = false) { installIsolatedCordappTo(bankAName) installIsolatedCordappTo(bankBName) - val (_, bankA, bankB) = createNotaryAndTwoNodes() - eventuallyPassingTest { - bankA.rpc.startFlowDynamic(flowInitiatorClass, bankB.nodeInfo.legalIdentities.first()).returnValue.getOrThrow() - } + val (bankA, bankB) = createTwoNodes() + bankA.rpc.startFlowDynamic(flowInitiatorClass, bankB.nodeInfo.legalIdentities.first()).returnValue.getOrThrow() } } } 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 bc41e797cd..15a8914e35 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 @@ -11,60 +11,64 @@ import net.corda.core.utilities.getOrThrow import net.corda.finance.POUNDS import net.corda.finance.flows.CashIssueFlow import net.corda.finance.flows.CashPaymentFlow -import net.corda.node.services.Permissions.Companion.startFlow import net.corda.node.services.Permissions.Companion.invokeRpc -import net.corda.node.services.transactions.RaftValidatingNotaryService +import net.corda.node.services.Permissions.Companion.startFlow import net.corda.nodeapi.User import net.corda.testing.* import net.corda.testing.driver.NodeHandle import net.corda.testing.driver.driver +import net.corda.testing.node.ClusterSpec +import net.corda.testing.node.NotarySpec +import org.assertj.core.api.Assertions.assertThat import org.junit.Test import rx.Observable import java.util.* -import kotlin.test.assertEquals class DistributedServiceTests { - lateinit var alice: NodeHandle - lateinit var notaries: List - lateinit var aliceProxy: CordaRPCOps - lateinit var raftNotaryIdentity: Party - lateinit var notaryStateMachines: Observable> - private fun setup(runTest: () -> Unit) = driver(extraCordappPackagesToScan = listOf("net.corda.finance.contracts")) { - // Start Alice and 3 notaries in a RAFT cluster - val clusterSize = 3 + private lateinit var alice: NodeHandle + private lateinit var notaryNodes: List + private lateinit var aliceProxy: CordaRPCOps + private lateinit var raftNotaryIdentity: Party + private lateinit var notaryStateMachines: Observable> + + private fun setup(testBlock: () -> Unit) { val testUser = User("test", "test", permissions = setOf( startFlow(), startFlow(), invokeRpc(CordaRPCOps::nodeInfo), invokeRpc(CordaRPCOps::stateMachinesFeed)) ) - val notariesFuture = startNotaryCluster( - DUMMY_NOTARY.name.copy(commonName = RaftValidatingNotaryService.id), - rpcUsers = listOf(testUser), - clusterSize = clusterSize - ) - val aliceFuture = startNode(providedName = ALICE.name, rpcUsers = listOf(testUser)) - alice = aliceFuture.get() - val (notaryIdentity, notaryNodes) = notariesFuture.get() - raftNotaryIdentity = notaryIdentity - notaries = notaryNodes.map { it as NodeHandle.OutOfProcess } + driver( + extraCordappPackagesToScan = listOf("net.corda.finance.contracts"), + notarySpecs = listOf(NotarySpec(DUMMY_NOTARY.name, rpcUsers = listOf(testUser), cluster = ClusterSpec.Raft(clusterSize = 3)))) + { + alice = startNode(providedName = ALICE.name, rpcUsers = listOf(testUser)).getOrThrow() + raftNotaryIdentity = defaultNotaryIdentity + notaryNodes = defaultNotaryHandle.nodeHandles.getOrThrow().map { it as NodeHandle.OutOfProcess } - assertEquals(notaries.size, clusterSize) - // Check that each notary has different identity as a node. - assertEquals(notaries.size, notaries.map { it.nodeInfo.chooseIdentity() }.toSet().size) - // Connect to Alice and the notaries - fun connectRpc(node: NodeHandle): CordaRPCOps { - val client = node.rpcClientToNode() - return client.start("test", "test").proxy + assertThat(notaryNodes).hasSize(3) + + for (notaryNode in notaryNodes) { + assertThat(notaryNode.nodeInfo.legalIdentities).contains(raftNotaryIdentity) + } + + // Check that each notary has different identity as a node. + assertThat(notaryNodes.flatMap { it.nodeInfo.legalIdentities - raftNotaryIdentity }.toSet()).hasSameSizeAs(notaryNodes) + + // Connect to Alice and the notaries + fun connectRpc(node: NodeHandle): CordaRPCOps { + val client = node.rpcClientToNode() + return client.start("test", "test").proxy + } + aliceProxy = connectRpc(alice) + val rpcClientsToNotaries = notaryNodes.map(::connectRpc) + notaryStateMachines = Observable.from(rpcClientsToNotaries.map { proxy -> + proxy.stateMachinesFeed().updates.map { Pair(proxy.nodeInfo().chooseIdentity(), it) } + }).flatMap { it.onErrorResumeNext(Observable.empty()) }.bufferUntilSubscribed() + + testBlock() } - aliceProxy = connectRpc(alice) - val rpcClientsToNotaries = notaries.map(::connectRpc) - notaryStateMachines = Observable.from(rpcClientsToNotaries.map { proxy -> - proxy.stateMachinesFeed().updates.map { Pair(proxy.nodeInfo().chooseIdentity(), it) } - }).flatMap { it.onErrorResumeNext(Observable.empty()) }.bufferUntilSubscribed() - - runTest() } // TODO Use a dummy distributed service rather than a Raft Notary Service as this test is only about Artemis' ability @@ -106,8 +110,8 @@ class DistributedServiceTests { paySelf(5.POUNDS) } - // Now kill a notary - with(notaries[0].process) { + // Now kill a notary node + with(notaryNodes[0].process) { destroy() waitFor() } 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 745b763e9e..b9e95db5bd 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 @@ -18,6 +18,8 @@ import net.corda.testing.contracts.DummyContract import net.corda.testing.driver.NodeHandle import net.corda.testing.driver.driver import net.corda.testing.dummyCommand +import net.corda.testing.node.ClusterSpec +import net.corda.testing.node.NotarySpec import org.junit.Test import java.util.* import kotlin.test.assertEquals @@ -28,13 +30,16 @@ class RaftNotaryServiceTests { @Test fun `detect double spend`() { - driver(startNodesInProcess = true, extraCordappPackagesToScan = listOf("net.corda.testing.contracts")) { - val (notaryParty) = startNotaryCluster(notaryName, 3).getOrThrow() + driver( + startNodesInProcess = true, + 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 inputState = issueState(bankA, notaryParty) + val inputState = issueState(bankA, defaultNotaryIdentity) - val firstTxBuilder = TransactionBuilder(notaryParty) + val firstTxBuilder = TransactionBuilder(defaultNotaryIdentity) .addInputState(inputState) .addCommand(dummyCommand(bankA.services.myInfo.chooseIdentity().owningKey)) val firstSpendTx = bankA.services.signInitialTransaction(firstTxBuilder) @@ -42,7 +47,7 @@ class RaftNotaryServiceTests { val firstSpend = bankA.services.startFlow(NotaryFlow.Client(firstSpendTx)) firstSpend.resultFuture.getOrThrow() - val secondSpendBuilder = TransactionBuilder(notaryParty).withItems(inputState).run { + val secondSpendBuilder = TransactionBuilder(defaultNotaryIdentity).withItems(inputState).run { val dummyState = DummyContract.SingleOwnerState(0, bankA.info.chooseIdentity()) addOutputState(dummyState, DummyContract.PROGRAM_ID) addCommand(dummyCommand(bankA.services.myInfo.chooseIdentity().owningKey)) 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 49de548f16..9f0212d2cb 100644 --- a/node/src/integration-test/kotlin/net/corda/node/services/statemachine/LargeTransactionsTest.kt +++ b/node/src/integration-test/kotlin/net/corda/node/services/statemachine/LargeTransactionsTest.kt @@ -8,11 +8,11 @@ import net.corda.core.messaging.startFlow import net.corda.core.transactions.TransactionBuilder import net.corda.testing.BOB import net.corda.testing.DUMMY_NOTARY +import net.corda.testing.aliceAndBob import net.corda.testing.contracts.DummyContract import net.corda.testing.contracts.DummyState import net.corda.testing.driver.driver import net.corda.testing.dummyCommand -import net.corda.testing.notaryAliceAndBob import org.junit.Test import kotlin.test.assertEquals @@ -65,7 +65,7 @@ class LargeTransactionsTest { val bigFile3 = InputStreamAndHash.createInMemoryTestZip(1024 * 1024 * 3, 2) val bigFile4 = InputStreamAndHash.createInMemoryTestZip(1024 * 1024 * 3, 3) driver(startNodesInProcess = true, extraCordappPackagesToScan = listOf("net.corda.testing.contracts")) { - val (_, alice) = notaryAliceAndBob() + val (alice, _) = aliceAndBob() alice.useRPC { val hash1 = it.uploadAttachment(bigFile1.inputStream) val hash2 = it.uploadAttachment(bigFile2.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 c43185bdeb..6d480bdf87 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 @@ -21,6 +21,8 @@ import net.corda.testing.chooseIdentity import net.corda.testing.driver.DriverDSLExposedInterface import net.corda.testing.driver.NodeHandle import net.corda.testing.driver.driver +import net.corda.testing.node.ClusterSpec +import net.corda.testing.node.NotarySpec import org.assertj.core.api.Assertions.assertThat import org.junit.Test import java.util.* @@ -35,16 +37,14 @@ class P2PMessagingTest { @Test fun `communicating with a distributed service which we're part of`() { - driver(startNodesInProcess = true) { - val distributedService = startDistributedService() + startDriverWithDistributedService { distributedService -> assertAllNodesAreUsed(distributedService, DISTRIBUTED_SERVICE_NAME, distributedService[0]) } } @Test fun `distributed service requests are retried if one of the nodes in the cluster goes down without sending a response`() { - driver(startNodesInProcess = true) { - val distributedServiceNodes = startDistributedService() + startDriverWithDistributedService { distributedServiceNodes -> val alice = startAlice() val serviceAddress = alice.services.networkMapCache.run { val notaryParty = notaryIdentities.randomOrNull()!! @@ -77,8 +77,7 @@ class P2PMessagingTest { @Test fun `distributed service request retries are persisted across client node restarts`() { - driver(startNodesInProcess = true) { - val distributedServiceNodes = startDistributedService() + startDriverWithDistributedService { distributedServiceNodes -> val alice = startAlice() val serviceAddress = alice.services.networkMapCache.run { val notaryParty = notaryIdentities.randomOrNull()!! @@ -117,11 +116,10 @@ class P2PMessagingTest { } } - private fun DriverDSLExposedInterface.startDistributedService(): List> { - return startNotaryCluster(DISTRIBUTED_SERVICE_NAME, 2) - .getOrThrow() - .second - .map { (it as NodeHandle.InProcess).node } + private fun startDriverWithDistributedService(dsl: DriverDSLExposedInterface.(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 }) + } } private fun DriverDSLExposedInterface.startAlice(): StartedNode { 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 21e70426cc..c2355f7723 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 @@ -17,10 +17,9 @@ import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.TransactionBuilder import net.corda.core.utilities.ProgressTracker import net.corda.core.utilities.getOrThrow -import net.corda.node.services.Permissions.Companion.startFlow import net.corda.node.services.Permissions.Companion.invokeRpc +import net.corda.node.services.Permissions.Companion.startFlow import net.corda.nodeapi.User -import net.corda.testing.DUMMY_NOTARY import net.corda.testing.chooseIdentity import net.corda.testing.driver.driver import org.junit.Assume.assumeFalse @@ -43,7 +42,6 @@ class NodeStatePersistenceTests { val message = Message("Hello world!") driver(isDebug = true, startNodesInProcess = isQuasarAgentSpecified()) { val nodeName = { - startNotaryNode(DUMMY_NOTARY.name, validating = false).getOrThrow() val nodeHandle = startNode(rpcUsers = listOf(user)).getOrThrow() val nodeName = nodeHandle.nodeInfo.chooseIdentity().name nodeHandle.rpcClientToNode().start(user.username, user.password).use { diff --git a/node/src/main/kotlin/net/corda/node/services/network/PersistentNetworkMapCache.kt b/node/src/main/kotlin/net/corda/node/services/network/PersistentNetworkMapCache.kt index 349b9e1185..33f98f7322 100644 --- a/node/src/main/kotlin/net/corda/node/services/network/PersistentNetworkMapCache.kt +++ b/node/src/main/kotlin/net/corda/node/services/network/PersistentNetworkMapCache.kt @@ -31,6 +31,7 @@ import java.security.PublicKey import java.util.* import javax.annotation.concurrent.ThreadSafe import kotlin.collections.HashMap +import kotlin.collections.HashSet class NetworkMapCacheImpl( networkMapCacheBase: NetworkMapCacheBaseInternal, @@ -85,7 +86,7 @@ open class PersistentNetworkMapCache( override val loadDBSuccess get() = _loadDBSuccess override val notaryIdentities: List = notaries.map { it.identity } - private val validatingNotaries = notaries.mapNotNull { if (it.validating) it.identity else null } + private val validatingNotaries = notaries.mapNotNullTo(HashSet()) { if (it.validating) it.identity else null } private val nodeInfoSerializer = NodeInfoWatcher(configuration.baseDirectory, configuration.additionalNodeInfoPollingFrequencyMsec) diff --git a/node/src/test/kotlin/net/corda/node/CordaRPCOpsImplTest.kt b/node/src/test/kotlin/net/corda/node/CordaRPCOpsImplTest.kt index 4f2b4cf970..552d16a484 100644 --- a/node/src/test/kotlin/net/corda/node/CordaRPCOpsImplTest.kt +++ b/node/src/test/kotlin/net/corda/node/CordaRPCOpsImplTest.kt @@ -50,14 +50,12 @@ import kotlin.test.assertNull import kotlin.test.assertTrue class CordaRPCOpsImplTest { - private companion object { val testJar = "net/corda/node/testing/test.jar" } private lateinit var mockNet: MockNetwork private lateinit var aliceNode: StartedNode - private lateinit var notaryNode: StartedNode private lateinit var notary: Party private lateinit var rpc: CordaRPCOps private lateinit var stateMachineUpdates: Observable @@ -69,7 +67,6 @@ class CordaRPCOpsImplTest { @Before fun setup() { mockNet = MockNetwork(cordappPackages = listOf("net.corda.finance.contracts.asset")) - notaryNode = mockNet.createNotaryNode(validating = false) aliceNode = mockNet.createNode() rpc = SecureCordaRPCOps(aliceNode.services, aliceNode.smm, aliceNode.database, aliceNode.services) CURRENT_RPC_CONTEXT.set(RpcContext(user)) 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 14242aecb3..7e5f99d32a 100644 --- a/node/src/test/kotlin/net/corda/node/internal/CordaServiceTest.kt +++ b/node/src/test/kotlin/net/corda/node/internal/CordaServiceTest.kt @@ -74,14 +74,12 @@ class TestCordaService2(val appServiceHub: AppServiceHub): SingletonSerializeAsT class LegacyCordaService(@Suppress("UNUSED_PARAMETER") simpleServiceHub: ServiceHub) : SingletonSerializeAsToken() class CordaServiceTest { - lateinit var mockNet: MockNetwork - lateinit var notaryNode: StartedNode - lateinit var nodeA: StartedNode + private lateinit var mockNet: MockNetwork + private lateinit var nodeA: StartedNode @Before fun start() { mockNet = MockNetwork(threadPerNode = true, cordappPackages = listOf("net.corda.node.internal","net.corda.finance")) - notaryNode = mockNet.createNotaryNode() nodeA = mockNet.createNode() mockNet.startNodes() } 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 3158bb7887..00e3e4d641 100644 --- a/node/src/test/kotlin/net/corda/node/messaging/TwoPartyTradeFlowTests.kt +++ b/node/src/test/kotlin/net/corda/node/messaging/TwoPartyTradeFlowTests.kt @@ -63,7 +63,7 @@ import kotlin.test.assertTrue * We assume that Alice and Bob already found each other via some market, and have agreed the details already. */ @RunWith(Parameterized::class) -class TwoPartyTradeFlowTests(val anonymous: Boolean) { +class TwoPartyTradeFlowTests(private val anonymous: Boolean) { companion object { private val cordappPackages = listOf("net.corda.finance.contracts") @JvmStatic @@ -93,7 +93,7 @@ class TwoPartyTradeFlowTests(val anonymous: Boolean) { // allow interruption half way through. mockNet = MockNetwork(threadPerNode = true, cordappPackages = cordappPackages) ledger(MockServices(cordappPackages), initialiseSerialization = false) { - val notaryNode = mockNet.createNotaryNode() + val notaryNode = mockNet.defaultNotaryNode val aliceNode = mockNet.createPartyNode(ALICE_NAME) val bobNode = mockNet.createPartyNode(BOB_NAME) val bankNode = mockNet.createPartyNode(BOC_NAME) @@ -143,7 +143,7 @@ class TwoPartyTradeFlowTests(val anonymous: Boolean) { fun `trade cash for commercial paper fails using soft locking`() { mockNet = MockNetwork(threadPerNode = true, cordappPackages = cordappPackages) ledger(MockServices(cordappPackages), initialiseSerialization = false) { - val notaryNode = mockNet.createNotaryNode() + val notaryNode = mockNet.defaultNotaryNode val aliceNode = mockNet.createPartyNode(ALICE_NAME) val bobNode = mockNet.createPartyNode(BOB_NAME) val bankNode = mockNet.createPartyNode(BOC_NAME) @@ -199,7 +199,7 @@ class TwoPartyTradeFlowTests(val anonymous: Boolean) { fun `shutdown and restore`() { mockNet = MockNetwork(cordappPackages = cordappPackages) ledger(MockServices(cordappPackages), initialiseSerialization = false) { - val notaryNode = mockNet.createNotaryNode() + val notaryNode = mockNet.defaultNotaryNode val aliceNode = mockNet.createPartyNode(ALICE_NAME) var bobNode = mockNet.createPartyNode(BOB_NAME) val bankNode = mockNet.createPartyNode(BOC_NAME) @@ -292,7 +292,8 @@ class TwoPartyTradeFlowTests(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) { @@ -307,7 +308,7 @@ class TwoPartyTradeFlowTests(val anonymous: Boolean) { @Test fun `check dependencies of sale asset are resolved`() { mockNet = MockNetwork(cordappPackages = cordappPackages) - val notaryNode = mockNet.createNotaryNode() + val notaryNode = mockNet.defaultNotaryNode val aliceNode = makeNodeWithTracking(ALICE_NAME) val bobNode = makeNodeWithTracking(BOB_NAME) val bankNode = makeNodeWithTracking(BOC_NAME) @@ -413,7 +414,7 @@ class TwoPartyTradeFlowTests(val anonymous: Boolean) { @Test fun `track works`() { mockNet = MockNetwork(cordappPackages = cordappPackages) - val notaryNode = mockNet.createNotaryNode() + val notaryNode = mockNet.defaultNotaryNode val aliceNode = makeNodeWithTracking(ALICE_NAME) val bobNode = makeNodeWithTracking(BOB_NAME) val bankNode = makeNodeWithTracking(BOC_NAME) @@ -568,7 +569,7 @@ class TwoPartyTradeFlowTests(val anonymous: Boolean) { aliceError: Boolean, expectedMessageSubstring: String ) { - val notaryNode = mockNet.createNotaryNode() + val notaryNode = mockNet.defaultNotaryNode val aliceNode = mockNet.createPartyNode(ALICE_NAME) val bobNode = mockNet.createPartyNode(BOB_NAME) val bankNode = mockNet.createPartyNode(BOC_NAME) 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 6310e9198d..5f18b85b10 100644 --- a/node/src/test/kotlin/net/corda/node/services/NotaryChangeTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/NotaryChangeTests.kt @@ -1,7 +1,5 @@ package net.corda.node.services -import com.nhaarman.mockito_kotlin.doReturn -import com.nhaarman.mockito_kotlin.whenever import net.corda.core.contracts.* import net.corda.core.crypto.generateKeyPair import net.corda.core.flows.NotaryChangeFlow @@ -15,14 +13,10 @@ 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.node.services.config.NotaryConfig -import net.corda.testing.DUMMY_NOTARY -import net.corda.testing.chooseIdentity +import net.corda.testing.* import net.corda.testing.contracts.DummyContract -import net.corda.testing.dummyCommand -import net.corda.testing.getTestPartyAndCertificate import net.corda.testing.node.MockNetwork -import net.corda.testing.node.MockNodeParameters +import net.corda.testing.node.MockNetwork.NotarySpec import org.assertj.core.api.Assertions.assertThatExceptionOfType import org.junit.After import org.junit.Before @@ -42,23 +36,17 @@ class NotaryChangeTests { @Before fun setUp() { - mockNet = MockNetwork(cordappPackages = listOf("net.corda.testing.contracts")) - val (oldNotaryNode, newNotaryNode) = listOf( - createUnstartedNotary(DUMMY_NOTARY.name), - createUnstartedNotary(DUMMY_NOTARY.name.copy(organisation = "Dummy Notary 2")) - ).map { it.start() } - this.oldNotaryNode = oldNotaryNode + val oldNotaryName = DUMMY_REGULATOR.name + mockNet = MockNetwork( + notarySpecs = listOf(NotarySpec(DUMMY_NOTARY.name), NotarySpec(oldNotaryName)), + cordappPackages = listOf("net.corda.testing.contracts") + ) clientNodeA = mockNet.createNode() clientNodeB = mockNet.createNode() - oldNotaryParty = newNotaryNode.services.networkMapCache.getNotary(DUMMY_NOTARY.name)!! - newNotaryParty = newNotaryNode.services.networkMapCache.getNotary(DUMMY_NOTARY.name.copy(organisation = "Dummy Notary 2"))!! - } - - private fun createUnstartedNotary(name: CordaX500Name): MockNetwork.MockNode { - return mockNet.createUnstartedNode(MockNodeParameters( - legalName = name, - configOverrides = { doReturn(NotaryConfig(validating = true)).whenever(it).notary } - )) + oldNotaryNode = mockNet.notaryNodes[1] + mockNet.runNetwork() // Clear network map registration messages + newNotaryParty = clientNodeA.services.networkMapCache.getNotary(DUMMY_NOTARY.name)!! + oldNotaryParty = clientNodeA.services.networkMapCache.getNotary(oldNotaryName)!! } @After 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 db4b9dde89..a8b924fe53 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 @@ -35,7 +35,6 @@ class ScheduledFlowTests { } lateinit var mockNet: MockNetwork - lateinit var notaryNode: StartedNode lateinit var nodeA: StartedNode lateinit var nodeB: StartedNode @@ -94,7 +93,6 @@ class ScheduledFlowTests { @Before fun setup() { mockNet = MockNetwork(threadPerNode = true, cordappPackages = listOf("net.corda.testing.contracts")) - notaryNode = mockNet.createNotaryNode() val a = mockNet.createUnstartedNode() val b = mockNet.createUnstartedNode() diff --git a/node/src/test/kotlin/net/corda/node/services/network/NetworkMapCacheTest.kt b/node/src/test/kotlin/net/corda/node/services/network/NetworkMapCacheTest.kt index b493466645..cb4be70ef7 100644 --- a/node/src/test/kotlin/net/corda/node/services/network/NetworkMapCacheTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/network/NetworkMapCacheTest.kt @@ -38,13 +38,13 @@ class NetworkMapCacheTest { @Test fun `getNodeByLegalIdentity`() { - val notaryNode = mockNet.createNotaryNode() val aliceNode = mockNet.createPartyNode(ALICE.name) - val notaryCache: NetworkMapCache = notaryNode.services.networkMapCache + val bobNode = mockNet.createPartyNode(BOB.name) + val bobCache: NetworkMapCache = bobNode.services.networkMapCache val expected = aliceNode.info mockNet.runNetwork() - val actual = notaryNode.database.transaction { notaryCache.getNodeByLegalIdentity(aliceNode.info.chooseIdentity()) } + val actual = bobNode.database.transaction { bobCache.getNodeByLegalIdentity(aliceNode.info.chooseIdentity()) } assertEquals(expected, actual) // TODO: Should have a test case with anonymous lookup @@ -52,30 +52,30 @@ class NetworkMapCacheTest { @Test fun `getPeerByLegalName`() { - val notaryNode = mockNet.createNotaryNode() val aliceNode = mockNet.createPartyNode(ALICE.name) - val notaryCache: NetworkMapCache = notaryNode.services.networkMapCache + val bobNode = mockNet.createPartyNode(BOB.name) + val bobCache: NetworkMapCache = bobNode.services.networkMapCache val expected = aliceNode.info.legalIdentities.single() mockNet.runNetwork() - val actual = notaryNode.database.transaction { notaryCache.getPeerByLegalName(ALICE.name) } + val actual = bobNode.database.transaction { bobCache.getPeerByLegalName(ALICE.name) } assertEquals(expected, actual) } @Test fun `remove node from cache`() { - val notaryNode = mockNet.createNotaryNode() val aliceNode = mockNet.createPartyNode(ALICE.name) - val notaryLegalIdentity = notaryNode.info.chooseIdentity() + val bobNode = mockNet.createPartyNode(BOB.name) + val bobLegalIdentity = bobNode.info.chooseIdentity() val alice = aliceNode.info.chooseIdentity() - val notaryCache = notaryNode.services.networkMapCache + val bobCache = bobNode.services.networkMapCache mockNet.runNetwork() - notaryNode.database.transaction { - assertThat(notaryCache.getNodeByLegalIdentity(alice) != null) - notaryCache.removeNode(aliceNode.info) - assertThat(notaryCache.getNodeByLegalIdentity(alice) == null) - assertThat(notaryCache.getNodeByLegalIdentity(notaryLegalIdentity) != null) - assertThat(notaryCache.getNodeByLegalName(alice.name) == null) + bobNode.database.transaction { + assertThat(bobCache.getNodeByLegalIdentity(alice) != null) + bobCache.removeNode(aliceNode.info) + assertThat(bobCache.getNodeByLegalIdentity(alice) == null) + assertThat(bobCache.getNodeByLegalIdentity(bobLegalIdentity) != null) + assertThat(bobCache.getNodeByLegalName(alice.name) == null) } } } 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 be168e8b98..b17b91928f 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 @@ -43,9 +43,7 @@ class NodeSchemaServiceTest { @Test fun `auto scanning of custom schemas for testing with Driver`() { driver(startNodesInProcess = true) { - val node = startNode() - val nodeHandle = node.getOrThrow() - val result = nodeHandle.rpc.startFlow(::MappedSchemasFlow) + val result = defaultNotaryNode.getOrThrow().rpc.startFlow(::MappedSchemasFlow) val mappedSchemas = result.returnValue.getOrThrow() assertTrue(mappedSchemas.contains(TestSchema.name)) } @@ -54,11 +52,12 @@ class NodeSchemaServiceTest { @Test fun `custom schemas are loaded eagerly`() { val expected = setOf("PARENTS", "CHILDREN") - assertEquals>(expected, driver { - (startNode(startInSameProcess = true).getOrThrow() as NodeHandle.InProcess).node.database.transaction { + val tables = driver(startNodesInProcess = true) { + (defaultNotaryNode.getOrThrow() as NodeHandle.InProcess).node.database.transaction { session.createNativeQuery("SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES").list() } - }.toMutableSet().apply { retainAll(expected) }) + } + assertEquals>(expected, tables.toMutableSet().apply { retainAll(expected) }) } @StartableByRPC 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 de47655371..291d58a317 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 @@ -74,8 +74,8 @@ class FlowFrameworkTests { fun start() { mockNet = MockNetwork( servicePeerAllocationStrategy = RoundRobin(), - cordappPackages = listOf("net.corda.finance.contracts", "net.corda.testing.contracts")) - val notary = mockNet.createNotaryNode() + cordappPackages = listOf("net.corda.finance.contracts", "net.corda.testing.contracts") + ) aliceNode = mockNet.createNode(MockNodeParameters(legalName = ALICE_NAME)) bobNode = mockNet.createNode(MockNodeParameters(legalName = BOB_NAME)) @@ -84,7 +84,7 @@ class FlowFrameworkTests { // Extract identities alice = aliceNode.info.singleIdentity() bob = bobNode.info.singleIdentity() - notaryIdentity = notary.services.getDefaultNotary() + notaryIdentity = aliceNode.services.getDefaultNotary() } @After diff --git a/node/src/test/kotlin/net/corda/node/services/transactions/NotaryServiceTests.kt b/node/src/test/kotlin/net/corda/node/services/transactions/NotaryServiceTests.kt index 05bf4ecdca..a17b9c68c9 100644 --- a/node/src/test/kotlin/net/corda/node/services/transactions/NotaryServiceTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/transactions/NotaryServiceTests.kt @@ -14,10 +14,13 @@ import net.corda.core.transactions.TransactionBuilder import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.seconds import net.corda.node.services.api.StartedNodeServices -import net.corda.testing.* +import net.corda.testing.ALICE_NAME import net.corda.testing.contracts.DummyContract +import net.corda.testing.dummyCommand +import net.corda.testing.getDefaultNotary import net.corda.testing.node.MockNetwork import net.corda.testing.node.MockNodeParameters +import net.corda.testing.singleIdentity import org.assertj.core.api.Assertions.assertThat import org.junit.After import org.junit.Before @@ -28,20 +31,19 @@ import kotlin.test.assertEquals import kotlin.test.assertFailsWith class NotaryServiceTests { - lateinit var mockNet: MockNetwork - lateinit var notaryServices: StartedNodeServices - lateinit var aliceServices: StartedNodeServices - lateinit var notary: Party - lateinit var alice: Party + private lateinit var mockNet: MockNetwork + private lateinit var notaryServices: StartedNodeServices + private lateinit var aliceServices: StartedNodeServices + private lateinit var notary: Party + private lateinit var alice: Party @Before fun setup() { mockNet = MockNetwork(cordappPackages = listOf("net.corda.testing.contracts")) - val notaryNode = mockNet.createNotaryNode(validating = false) aliceServices = mockNet.createNode(MockNodeParameters(legalName = ALICE_NAME)).services mockNet.runNetwork() // Clear network map registration messages - notaryServices = notaryNode.services - notary = notaryServices.getDefaultNotary() + notaryServices = mockNet.defaultNotaryNode.services //TODO get rid of that + notary = aliceServices.getDefaultNotary() alice = aliceServices.myInfo.singleIdentity() } @@ -155,7 +157,7 @@ class NotaryServiceTests { return future } - fun issueState(services: ServiceHub, identity: Party): StateAndRef<*> { + private fun issueState(services: ServiceHub, identity: Party): StateAndRef<*> { val tx = DummyContract.generateInitial(Random().nextInt(), notary, identity.ref(0)) val signedByNode = services.signInitialTransaction(tx) val stx = notaryServices.addSignature(signedByNode, notary.owningKey) diff --git a/node/src/test/kotlin/net/corda/node/services/transactions/ValidatingNotaryServiceTests.kt b/node/src/test/kotlin/net/corda/node/services/transactions/ValidatingNotaryServiceTests.kt index 4fe3f68a20..112348d74c 100644 --- a/node/src/test/kotlin/net/corda/node/services/transactions/ValidatingNotaryServiceTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/transactions/ValidatingNotaryServiceTests.kt @@ -15,10 +15,13 @@ import net.corda.core.transactions.TransactionBuilder import net.corda.core.utilities.getOrThrow import net.corda.node.services.api.StartedNodeServices import net.corda.node.services.issueInvalidState -import net.corda.testing.* +import net.corda.testing.ALICE_NAME +import net.corda.testing.MEGA_CORP_KEY import net.corda.testing.contracts.DummyContract +import net.corda.testing.dummyCommand import net.corda.testing.node.MockNetwork import net.corda.testing.node.MockNodeParameters +import net.corda.testing.singleIdentity import org.assertj.core.api.Assertions.assertThat import org.junit.After import org.junit.Before @@ -28,21 +31,20 @@ import kotlin.test.assertEquals import kotlin.test.assertFailsWith class ValidatingNotaryServiceTests { - lateinit var mockNet: MockNetwork - lateinit var notaryServices: StartedNodeServices - lateinit var aliceServices: StartedNodeServices - lateinit var notary: Party - lateinit var alice: Party + private lateinit var mockNet: MockNetwork + private lateinit var notaryServices: StartedNodeServices + private lateinit var aliceServices: StartedNodeServices + private lateinit var notary: Party + private lateinit var alice: Party @Before fun setup() { mockNet = MockNetwork(cordappPackages = listOf("net.corda.testing.contracts")) - val notaryNode = mockNet.createNotaryNode() val aliceNode = mockNet.createNode(MockNodeParameters(legalName = ALICE_NAME)) mockNet.runNetwork() // Clear network map registration messages - notaryServices = notaryNode.services + notaryServices = mockNet.defaultNotaryNode.services aliceServices = aliceNode.services - notary = notaryServices.getDefaultNotary() + notary = mockNet.defaultNotaryIdentity alice = aliceNode.info.singleIdentity() } @@ -97,7 +99,7 @@ class ValidatingNotaryServiceTests { return future } - fun issueState(serviceHub: ServiceHub, identity: Party): StateAndRef<*> { + private fun issueState(serviceHub: ServiceHub, identity: Party): StateAndRef<*> { val tx = DummyContract.generateInitial(Random().nextInt(), notary, identity.ref(0)) val signedByNode = serviceHub.signInitialTransaction(tx) val stx = notaryServices.addSignature(signedByNode, notary.owningKey) 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 7beb578048..16c6d62c43 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 @@ -7,7 +7,6 @@ import net.corda.node.services.Permissions.Companion.startFlow import net.corda.nodeapi.User import net.corda.testing.DUMMY_BANK_A import net.corda.testing.DUMMY_BANK_B -import net.corda.testing.DUMMY_NOTARY import net.corda.testing.driver.PortAllocation import net.corda.testing.driver.driver import org.junit.Test @@ -27,11 +26,10 @@ class AttachmentDemoTest { invokeRpc(CordaRPCOps::wellKnownPartyFromX500Name), invokeRpc(CordaRPCOps::internalVerifiedTransactionsFeed) ))) - val (_, nodeA, nodeB) = listOf( - startNotaryNode(DUMMY_NOTARY.name, validating = false), + val (nodeA, nodeB) = listOf( startNode(providedName = DUMMY_BANK_A.name, rpcUsers = demoUser, maximumHeapSize = "1g"), - startNode(providedName = DUMMY_BANK_B.name, rpcUsers = demoUser, maximumHeapSize = "1g")) - .map { it.getOrThrow() } + startNode(providedName = DUMMY_BANK_B.name, rpcUsers = demoUser, maximumHeapSize = "1g") + ).map { it.getOrThrow() } startWebserver(nodeB).getOrThrow() val senderThread = supplyAsync { diff --git a/samples/attachment-demo/src/main/kotlin/net/corda/attachmentdemo/Main.kt b/samples/attachment-demo/src/main/kotlin/net/corda/attachmentdemo/Main.kt index f63d1c68a7..ac2c59abac 100644 --- a/samples/attachment-demo/src/main/kotlin/net/corda/attachmentdemo/Main.kt +++ b/samples/attachment-demo/src/main/kotlin/net/corda/attachmentdemo/Main.kt @@ -4,7 +4,6 @@ import net.corda.core.internal.div import net.corda.nodeapi.User import net.corda.testing.DUMMY_BANK_A import net.corda.testing.DUMMY_BANK_B -import net.corda.testing.DUMMY_NOTARY import net.corda.testing.driver.driver /** @@ -14,7 +13,6 @@ import net.corda.testing.driver.driver fun main(args: Array) { val demoUser = listOf(User("demo", "demo", setOf("StartFlow.net.corda.flows.FinalityFlow"))) driver(isDebug = true, driverDirectory = "build" / "attachment-demo-nodes") { - startNotaryNode(DUMMY_NOTARY.name, validating = false) startNode(providedName = DUMMY_BANK_A.name, rpcUsers = demoUser) startNode(providedName = DUMMY_BANK_B.name, rpcUsers = demoUser) waitForAllNodesToFinish() diff --git a/samples/bank-of-corda-demo/src/integration-test/kotlin/net/corda/bank/BankOfCordaHttpAPITest.kt b/samples/bank-of-corda-demo/src/integration-test/kotlin/net/corda/bank/BankOfCordaHttpAPITest.kt index 91df6bed2c..0f37be7cbc 100644 --- a/samples/bank-of-corda-demo/src/integration-test/kotlin/net/corda/bank/BankOfCordaHttpAPITest.kt +++ b/samples/bank-of-corda-demo/src/integration-test/kotlin/net/corda/bank/BankOfCordaHttpAPITest.kt @@ -2,10 +2,8 @@ package net.corda.bank import net.corda.bank.api.BankOfCordaClientApi import net.corda.bank.api.BankOfCordaWebApi.IssueRequestParams -import net.corda.core.internal.concurrent.transpose import net.corda.core.utilities.getOrThrow import net.corda.testing.BOC -import net.corda.testing.DUMMY_NOTARY import net.corda.testing.driver.driver import org.junit.Test import kotlin.test.assertTrue @@ -13,14 +11,13 @@ import kotlin.test.assertTrue class BankOfCordaHttpAPITest { @Test fun `issuer flow via Http`() { - driver(isDebug = true, extraCordappPackagesToScan = listOf("net.corda.finance")) { - val (_, bocNode) = listOf( - startNotaryNode(providedName = DUMMY_NOTARY.name), + driver(extraCordappPackagesToScan = listOf("net.corda.finance"), isDebug = true) { + val (bocNode) = listOf( startNode(providedName = BOC.name), startNode(providedName = BIGCORP_LEGAL_NAME) - ).transpose().getOrThrow() + ).map { it.getOrThrow() } val bocApiAddress = startWebserver(bocNode).getOrThrow().listenAddress - val issueRequestParams = IssueRequestParams(1000, "USD", BIGCORP_LEGAL_NAME, "1", BOC.name, DUMMY_NOTARY.name) + val issueRequestParams = IssueRequestParams(1000, "USD", BIGCORP_LEGAL_NAME, "1", BOC.name, defaultNotaryIdentity.name) assertTrue(BankOfCordaClientApi(bocApiAddress).requestWebIssue(issueRequestParams)) } } 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 c1a9d461fd..4b2e6c2580 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 @@ -23,12 +23,12 @@ class BankOfCordaRPCClientTest { invokeRpc(CordaRPCOps::wellKnownPartyFromX500Name), invokeRpc(CordaRPCOps::notaryIdentities) ) - driver(extraCordappPackagesToScan = listOf("net.corda.finance"), dsl = { + driver(extraCordappPackagesToScan = listOf("net.corda.finance"), isDebug = true) { val bocManager = User("bocManager", "password1", permissions = setOf( startFlow()) + commonPermissions) val bigCorpCFO = User("bigCorpCFO", "password2", permissions = emptySet() + commonPermissions) val (nodeBankOfCorda, nodeBigCorporation) = listOf( - startNotaryNode(BOC.name, rpcUsers = listOf(bocManager), validating = false), + startNode(providedName = BOC.name, rpcUsers = listOf(bocManager)), startNode(providedName = BIGCORP_LEGAL_NAME, rpcUsers = listOf(bigCorpCFO)) ).map { it.getOrThrow() } @@ -51,12 +51,11 @@ class BankOfCordaRPCClientTest { // Kick-off actual Issuer Flow val anonymous = true - val notary = bocProxy.notaryIdentities().first() bocProxy.startFlow(::CashIssueAndPaymentFlow, 1000.DOLLARS, BIG_CORP_PARTY_REF, bigCorporation, anonymous, - notary).returnValue.getOrThrow() + defaultNotaryIdentity).returnValue.getOrThrow() // Check Bank of Corda Vault Updates vaultUpdatesBoc.expectEvents { @@ -84,6 +83,6 @@ class BankOfCordaRPCClientTest { } ) } - }, isDebug = true) + } } } \ No newline at end of file diff --git a/samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/BankOfCordaDriver.kt b/samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/BankOfCordaDriver.kt index 4faac3f3a9..a0e7752640 100644 --- a/samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/BankOfCordaDriver.kt +++ b/samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/BankOfCordaDriver.kt @@ -10,7 +10,6 @@ import net.corda.finance.flows.CashExitFlow import net.corda.finance.flows.CashIssueAndPaymentFlow import net.corda.finance.flows.CashPaymentFlow import net.corda.node.services.Permissions.Companion.startFlow -import net.corda.node.services.transactions.ValidatingNotaryService import net.corda.nodeapi.User import net.corda.testing.BOC import net.corda.testing.DUMMY_NOTARY @@ -58,7 +57,6 @@ private class BankOfCordaDriver { when (role) { Role.ISSUER -> { driver(isDebug = true, extraCordappPackagesToScan = listOf("net.corda.finance.contracts.asset")) { - startNotaryNode(providedName = DUMMY_NOTARY.name, validating = true) val bankUser = User( BANK_USERNAME, "test", @@ -83,7 +81,7 @@ private class BankOfCordaDriver { } else -> { val requestParams = IssueRequestParams(options.valueOf(quantity), options.valueOf(currency), BIGCORP_LEGAL_NAME, - "1", BOC.name, DUMMY_NOTARY.name.copy(commonName = ValidatingNotaryService.id)) + "1", BOC.name, DUMMY_NOTARY.name) when(role) { Role.ISSUE_CASH_RPC -> { println("Requesting Cash via RPC ...") diff --git a/samples/irs-demo/cordapp/src/test/kotlin/net/corda/irs/Main.kt b/samples/irs-demo/cordapp/src/test/kotlin/net/corda/irs/Main.kt index d1e770b257..64acf2c4ac 100644 --- a/samples/irs-demo/cordapp/src/test/kotlin/net/corda/irs/Main.kt +++ b/samples/irs-demo/cordapp/src/test/kotlin/net/corda/irs/Main.kt @@ -3,7 +3,6 @@ package net.corda.irs import net.corda.core.utilities.getOrThrow import net.corda.testing.DUMMY_BANK_A import net.corda.testing.DUMMY_BANK_B -import net.corda.testing.DUMMY_NOTARY import net.corda.testing.driver.driver /** @@ -11,17 +10,17 @@ import net.corda.testing.driver.driver * Do not use in a production environment. */ fun main(args: Array) { - driver(dsl = { - val (controller, nodeA, nodeB) = listOf( - startNotaryNode(DUMMY_NOTARY.name, validating = false), + driver(useTestClock = true, isDebug = true) { + val (nodeA, nodeB) = listOf( startNode(providedName = DUMMY_BANK_A.name), - startNode(providedName = DUMMY_BANK_B.name)) - .map { it.getOrThrow() } + startNode(providedName = DUMMY_BANK_B.name) + ).map { it.getOrThrow() } + val controller = defaultNotaryNode.getOrThrow() startWebserver(controller) startWebserver(nodeA) startWebserver(nodeB) waitForAllNodesToFinish() - }, useTestClock = true, isDebug = true) + } } 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 98e7012ef0..42c4597aee 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 @@ -8,6 +8,7 @@ import com.fasterxml.jackson.databind.JsonDeserializer import com.fasterxml.jackson.databind.ObjectMapper import com.fasterxml.jackson.databind.module.SimpleModule import com.fasterxml.jackson.module.kotlin.readValue +import net.corda.client.jackson.JacksonSupport import net.corda.client.rpc.CordaRPCClient import net.corda.core.contracts.UniqueIdentifier import net.corda.core.identity.Party @@ -24,6 +25,7 @@ import net.corda.nodeapi.User import net.corda.test.spring.springDriver import net.corda.testing.* import net.corda.testing.http.HttpApi +import net.corda.testing.node.NotarySpec import org.apache.commons.io.IOUtils import org.assertj.core.api.Assertions.assertThat import org.junit.Test @@ -44,11 +46,17 @@ class IRSDemoTest : IntegrationTestCategory { @Test fun `runs IRS demo`() { - springDriver(useTestClock = true, isDebug = true, extraCordappPackagesToScan = listOf("net.corda.irs")) { - val (controller, nodeA, nodeB) = listOf( - startNotaryNode(DUMMY_NOTARY.name, validating = true, rpcUsers = rpcUsers), + springDriver( + useTestClock = true, + notarySpecs = listOf(NotarySpec(DUMMY_NOTARY.name, rpcUsers = rpcUsers)), + isDebug = true, + extraCordappPackagesToScan = listOf("net.corda.irs") + ) { + val (nodeA, nodeB) = listOf( startNode(providedName = DUMMY_BANK_A.name, rpcUsers = rpcUsers), - startNode(providedName = DUMMY_BANK_B.name, rpcUsers = rpcUsers)).map { it.getOrThrow() } + startNode(providedName = DUMMY_BANK_B.name, rpcUsers = rpcUsers) + ).map { it.getOrThrow() } + val controller = defaultNotaryNode.getOrThrow() log.info("All nodes started") @@ -61,7 +69,7 @@ class IRSDemoTest : IntegrationTestCategory { log.info("All webservers started") val (controllerApi, nodeAApi, nodeBApi) = listOf(controller, nodeA, nodeB).zip(listOf(controllerAddr, nodeAAddr, nodeBAddr)).map { - val mapper = net.corda.client.jackson.JacksonSupport.createDefaultMapper(it.first.rpc) + val mapper = JacksonSupport.createDefaultMapper(it.first.rpc) registerFinanceJSONMappers(mapper) registerIRSModule(mapper) HttpApi.fromHostAndPort(it.second, "api/irs", mapper = mapper) @@ -86,7 +94,9 @@ class IRSDemoTest : IntegrationTestCategory { } } - fun getFloatingLegFixCount(nodeApi: HttpApi) = getTrades(nodeApi)[0].calculation.floatingLegPaymentSchedule.count { it.value.rate.ratioUnit != null } + private fun getFloatingLegFixCount(nodeApi: HttpApi): Int { + return getTrades(nodeApi)[0].calculation.floatingLegPaymentSchedule.count { it.value.rate.ratioUnit != null } + } private fun getFixingDateObservable(config: NodeConfiguration): Observable { val client = CordaRPCClient(config.rpcAddress!!) 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 df5b25f480..41a17e1d36 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 @@ -6,6 +6,7 @@ import net.corda.core.internal.concurrent.fork import net.corda.core.internal.concurrent.map import net.corda.core.utilities.loggerFor import net.corda.testing.driver.* +import net.corda.testing.node.NotarySpec import okhttp3.OkHttpClient import okhttp3.Request import java.net.ConnectException @@ -41,6 +42,7 @@ fun springDriver( useTestClock: Boolean = defaultParameters.useTestClock, initialiseSerialization: Boolean = defaultParameters.initialiseSerialization, startNodesInProcess: Boolean = defaultParameters.startNodesInProcess, + notarySpecs: List, extraCordappPackagesToScan: List = defaultParameters.extraCordappPackagesToScan, dsl: SpringDriverExposedDSLInterface.() -> A ) = genericDriver( @@ -54,9 +56,9 @@ fun springDriver( initialiseSerialization = initialiseSerialization, startNodesInProcess = startNodesInProcess, extraCordappPackagesToScan = extraCordappPackagesToScan, + notarySpecs = notarySpecs, driverDslWrapper = { driverDSL:DriverDSL -> SpringBootDriverDSL(driverDSL) }, - coerce = { it }, - dsl = dsl + coerce = { it }, dsl = dsl ) data class SpringBootDriverDSL( diff --git a/samples/network-visualiser/src/main/kotlin/net/corda/netmap/simulation/IRSSimulation.kt b/samples/network-visualiser/src/main/kotlin/net/corda/netmap/simulation/IRSSimulation.kt index 300a173bf3..6a25314b1f 100644 --- a/samples/network-visualiser/src/main/kotlin/net/corda/netmap/simulation/IRSSimulation.kt +++ b/samples/network-visualiser/src/main/kotlin/net/corda/netmap/simulation/IRSSimulation.kt @@ -31,7 +31,6 @@ import java.util.* import java.util.concurrent.CompletableFuture import java.util.concurrent.CompletableFuture.allOf - /** * A simulation in which banks execute interest rate swaps with each other, including the fixing events. */ @@ -140,8 +139,6 @@ class IRSSimulation(networkSendManuallyPumped: Boolean, runAsync: Boolean, laten node1.internals.registerInitiatedFlow(FixingFlow.Fixer::class.java) node2.internals.registerInitiatedFlow(FixingFlow.Fixer::class.java) - val notaryId = notary.info.legalIdentities[0] - @InitiatingFlow class StartDealFlow(val otherParty: Party, val payload: AutoOffer) : FlowLogic() { @@ -166,7 +163,7 @@ class IRSSimulation(networkSendManuallyPumped: Boolean, runAsync: Boolean, laten val instigator = StartDealFlow( node2.info.chooseIdentity(), - AutoOffer(notaryId, irs)) // TODO Pass notary as parameter to Simulation. + AutoOffer(mockNet.defaultNotaryIdentity, irs)) // TODO Pass notary as parameter to Simulation. val instigatorTxFuture = node1.services.startFlow(instigator).resultFuture return allOf(instigatorTxFuture.toCompletableFuture(), acceptorTxFuture).thenCompose { instigatorTxFuture.toCompletableFuture() } 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 4e774cc191..e1c597e6a5 100644 --- a/samples/network-visualiser/src/main/kotlin/net/corda/netmap/simulation/Simulation.kt +++ b/samples/network-visualiser/src/main/kotlin/net/corda/netmap/simulation/Simulation.kt @@ -9,7 +9,6 @@ import net.corda.finance.utils.CityDatabase import net.corda.irs.api.NodeInterestRates import net.corda.node.internal.StartedNode import net.corda.node.services.statemachine.StateMachineManager -import net.corda.testing.DUMMY_NOTARY import net.corda.testing.DUMMY_REGULATOR import net.corda.testing.node.* import net.corda.testing.node.MockNetwork.MockNode @@ -71,14 +70,13 @@ abstract class Simulation(val networkSendManuallyPumped: Boolean, networkSendManuallyPumped = networkSendManuallyPumped, threadPerNode = runAsync, cordappPackages = listOf("net.corda.irs.contract", "net.corda.finance.contract", "net.corda.irs")) - val notary = mockNet.createNotaryNode(defaultParams.copy(legalName = DUMMY_NOTARY.name), false) // TODO: Regulatory nodes don't actually exist properly, this is a last minute demo request. // So we just fire a message at a node that doesn't know how to handle it, and it'll ignore it. // But that's fine for visualisation purposes. 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(notary.internals, ratesOracle) + 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. diff --git a/samples/simm-valuation-demo/src/integration-test/kotlin/net/corda/vega/SimmValuationTest.kt b/samples/simm-valuation-demo/src/integration-test/kotlin/net/corda/vega/SimmValuationTest.kt index 87b9fa7839..a970dd7312 100644 --- a/samples/simm-valuation-demo/src/integration-test/kotlin/net/corda/vega/SimmValuationTest.kt +++ b/samples/simm-valuation-demo/src/integration-test/kotlin/net/corda/vega/SimmValuationTest.kt @@ -3,7 +3,9 @@ package net.corda.vega import com.opengamma.strata.product.common.BuySell import net.corda.core.identity.CordaX500Name import net.corda.core.utilities.getOrThrow -import net.corda.testing.* +import net.corda.testing.DUMMY_BANK_A +import net.corda.testing.DUMMY_BANK_B +import net.corda.testing.IntegrationTestCategory import net.corda.testing.driver.driver import net.corda.testing.http.HttpApi import net.corda.vega.api.PortfolioApi @@ -27,7 +29,6 @@ class SimmValuationTest : IntegrationTestCategory { @Test fun `runs SIMM valuation demo`() { driver(isDebug = true, extraCordappPackagesToScan = listOf("net.corda.vega.contracts")) { - startNotaryNode(DUMMY_NOTARY.name, validating = false).getOrThrow() val nodeAFuture = startNode(providedName = nodeALegalName) val nodeBFuture = startNode(providedName = nodeBLegalName) val (nodeA, nodeB) = listOf(nodeAFuture, nodeBFuture).map { it.getOrThrow() } diff --git a/samples/simm-valuation-demo/src/test/kotlin/net/corda/vega/Main.kt b/samples/simm-valuation-demo/src/test/kotlin/net/corda/vega/Main.kt index 9cc72b5af0..c6bdcb2a2b 100644 --- a/samples/simm-valuation-demo/src/test/kotlin/net/corda/vega/Main.kt +++ b/samples/simm-valuation-demo/src/test/kotlin/net/corda/vega/Main.kt @@ -4,7 +4,6 @@ import net.corda.core.utilities.getOrThrow import net.corda.testing.DUMMY_BANK_A import net.corda.testing.DUMMY_BANK_B import net.corda.testing.DUMMY_BANK_C -import net.corda.testing.DUMMY_NOTARY import net.corda.testing.driver.driver /** @@ -13,17 +12,17 @@ import net.corda.testing.driver.driver * via the web api. */ fun main(args: Array) { - driver(dsl = { - val notaryFuture = startNotaryNode(DUMMY_NOTARY.name, validating = false) - val nodeAFuture = startNode(providedName = DUMMY_BANK_A.name) - val nodeBFuture = startNode(providedName = DUMMY_BANK_B.name) - val nodeCFuture = startNode(providedName = DUMMY_BANK_C.name) - val (nodeA, nodeB, nodeC) = listOf(nodeAFuture, nodeBFuture, nodeCFuture, notaryFuture).map { it.getOrThrow() } + driver(isDebug = true) { + val (nodeA, nodeB, nodeC) = listOf( + startNode(providedName = DUMMY_BANK_A.name), + startNode(providedName = DUMMY_BANK_B.name), + startNode(providedName = DUMMY_BANK_C.name) + ).map { it.getOrThrow() } startWebserver(nodeA) startWebserver(nodeB) startWebserver(nodeC) waitForAllNodesToFinish() - }, isDebug = true) + } } 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 51cbce94b3..b741356548 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 @@ -9,7 +9,10 @@ import net.corda.finance.flows.CashPaymentFlow import net.corda.node.services.Permissions.Companion.all import net.corda.node.services.Permissions.Companion.startFlow import net.corda.nodeapi.User -import net.corda.testing.* +import net.corda.testing.BOC +import net.corda.testing.DUMMY_BANK_A +import net.corda.testing.DUMMY_BANK_B +import net.corda.testing.chooseIdentity import net.corda.testing.driver.NodeHandle import net.corda.testing.driver.driver import net.corda.testing.driver.poll @@ -30,8 +33,7 @@ class TraderDemoTest { startFlow(), all())) driver(startNodesInProcess = true, extraCordappPackagesToScan = listOf("net.corda.finance")) { - val (_, nodeA, nodeB, bankNode) = listOf( - startNotaryNode(DUMMY_NOTARY.name, validating = false), + val (nodeA, nodeB, bankNode) = listOf( 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)) diff --git a/samples/trader-demo/src/test/kotlin/net/corda/traderdemo/Main.kt b/samples/trader-demo/src/test/kotlin/net/corda/traderdemo/Main.kt index a349d4c210..3413a012f5 100644 --- a/samples/trader-demo/src/test/kotlin/net/corda/traderdemo/Main.kt +++ b/samples/trader-demo/src/test/kotlin/net/corda/traderdemo/Main.kt @@ -8,7 +8,6 @@ import net.corda.nodeapi.User import net.corda.testing.BOC import net.corda.testing.DUMMY_BANK_A import net.corda.testing.DUMMY_BANK_B -import net.corda.testing.DUMMY_NOTARY import net.corda.testing.driver.driver import net.corda.traderdemo.flow.CommercialPaperIssueFlow import net.corda.traderdemo.flow.SellerFlow @@ -27,7 +26,6 @@ fun main(args: Array) { val user = User("user1", "test", permissions = setOf(startFlow(), startFlow(), startFlow())) - startNotaryNode(DUMMY_NOTARY.name, validating = false) startNode(providedName = DUMMY_BANK_A.name, rpcUsers = demoUser) startNode(providedName = DUMMY_BANK_B.name, rpcUsers = demoUser) startNode(providedName = BOC.name, rpcUsers = listOf(user)) diff --git a/testing/node-driver/src/integration-test/kotlin/net/corda/testing/FlowStackSnapshotTest.kt b/testing/node-driver/src/integration-test/kotlin/net/corda/testing/FlowStackSnapshotTest.kt index 68537ea0ab..e824c782f9 100644 --- a/testing/node-driver/src/integration-test/kotlin/net/corda/testing/FlowStackSnapshotTest.kt +++ b/testing/node-driver/src/integration-test/kotlin/net/corda/testing/FlowStackSnapshotTest.kt @@ -290,7 +290,6 @@ class FlowStackSnapshotTest { @Test fun `flowStackSnapshot object is serializable`() { val mockNet = MockNetwork(threadPerNode = true) - mockNet.createNotaryNode() val node = mockNet.createPartyNode() node.internals.registerInitiatedFlow(DummyFlow::class.java) node.services.startFlow(FlowStackSnapshotSerializationTestingFlow()).resultFuture.get() 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 c9cf4ae4a0..de8d83cc82 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 @@ -7,7 +7,6 @@ import net.corda.core.internal.readLines import net.corda.core.utilities.getOrThrow import net.corda.node.internal.NodeStartup import net.corda.testing.DUMMY_BANK_A -import net.corda.testing.DUMMY_NOTARY import net.corda.testing.DUMMY_REGULATOR import net.corda.testing.ProjectStructure.projectRootDir import org.assertj.core.api.Assertions.assertThat @@ -18,7 +17,6 @@ import java.util.concurrent.ScheduledExecutorService class DriverTests { companion object { - private val executorService: ScheduledExecutorService = Executors.newScheduledThreadPool(2) private fun nodeMustBeUp(handleFuture: CordaFuture) = handleFuture.getOrThrow().apply { @@ -32,26 +30,15 @@ class DriverTests { // Check that the port is bound addressMustNotBeBound(executorService, hostAndPort) } - } @Test fun `simple node startup and shutdown`() { - val handles = driver { - val notary = startNotaryNode(DUMMY_NOTARY.name, validating = false) + val handle = driver { val regulator = startNode(providedName = DUMMY_REGULATOR.name) - listOf(nodeMustBeUp(notary), nodeMustBeUp(regulator)) + nodeMustBeUp(regulator) } - handles.map { nodeMustBeDown(it) } - } - - @Test - fun `starting node with no services`() { - val noService = driver { - val noService = startNode(providedName = DUMMY_BANK_A.name) - nodeMustBeUp(noService) - } - nodeMustBeDown(noService) + nodeMustBeDown(handle) } @Test diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/DriverConstants.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/DriverConstants.kt index 5f872987dd..6c050f24b8 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/DriverConstants.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/DriverConstants.kt @@ -15,17 +15,11 @@ import net.corda.testing.driver.DriverDSLExposedInterface /** * A simple wrapper for objects provided by the integration test driver DSL. The fields are lazy so * node construction won't start until you access the members. You can get one of these from the - * [alice], [bob] and [notaryAliceAndBob] functions. + * [alice], [bob] and [aliceAndBob] functions. */ -class PredefinedTestNode internal constructor(party: Party, driver: DriverDSLExposedInterface, ifNotaryIsValidating: Boolean?) { +class PredefinedTestNode internal constructor(party: Party, driver: DriverDSLExposedInterface) { val rpcUsers = listOf(User("admin", "admin", setOf("ALL"))) // TODO: Randomize? - val nodeFuture by lazy { - if (ifNotaryIsValidating != null) { - driver.startNotaryNode(providedName = party.name, rpcUsers = rpcUsers, validating = ifNotaryIsValidating) - } else { - driver.startNode(providedName = party.name, rpcUsers = rpcUsers) - } - } + val nodeFuture by lazy { driver.startNode(providedName = party.name, rpcUsers = rpcUsers) } val node by lazy { nodeFuture.get()!! } val rpc by lazy { node.rpcClientToNode() } @@ -38,28 +32,21 @@ class PredefinedTestNode internal constructor(party: Party, driver: DriverDSLExp * Returns a plain, entirely stock node pre-configured with the [ALICE] identity. Note that a random key will be generated * for it: you won't have [ALICE_KEY]. */ -fun DriverDSLExposedInterface.alice(): PredefinedTestNode = PredefinedTestNode(ALICE, this, null) +fun DriverDSLExposedInterface.alice(): PredefinedTestNode = PredefinedTestNode(ALICE, this) /** * Returns a plain, entirely stock node pre-configured with the [BOB] identity. Note that a random key will be generated * for it: you won't have [BOB_KEY]. */ -fun DriverDSLExposedInterface.bob(): PredefinedTestNode = PredefinedTestNode(BOB, this, null) +fun DriverDSLExposedInterface.bob(): PredefinedTestNode = PredefinedTestNode(BOB, this) /** - * Returns a plain single node notary pre-configured with the [DUMMY_NOTARY] identity. Note that a random key will be generated - * for it: you won't have [DUMMY_NOTARY_KEY]. + * Returns plain, entirely stock nodes pre-configured with the [ALICE] and [BOB] X.500 names in that order. They have been + * started up in parallel and are now ready to use. */ -fun DriverDSLExposedInterface.notary(): PredefinedTestNode = PredefinedTestNode(DUMMY_NOTARY, this, true) - -/** - * Returns plain, entirely stock nodes pre-configured with the [ALICE], [BOB] and [DUMMY_NOTARY] X.500 names in that - * order. They have been started up in parallel and are now ready to use. - */ -fun DriverDSLExposedInterface.notaryAliceAndBob(): List { - val notary = notary() +fun DriverDSLExposedInterface.aliceAndBob(): List { val alice = alice() val bob = bob() - listOf(notary.nodeFuture, alice.nodeFuture, bob.nodeFuture).transpose().get() - return listOf(alice, bob, notary) + listOf(alice.nodeFuture, bob.nodeFuture).transpose().get() + return listOf(alice, bob) } diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/RPCDriver.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/RPCDriver.kt index 4de8f86442..9c37ffaa42 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/RPCDriver.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/RPCDriver.kt @@ -23,6 +23,7 @@ import net.corda.nodeapi.RPCApi import net.corda.nodeapi.User import net.corda.nodeapi.internal.serialization.KRYO_RPC_CLIENT_CONTEXT import net.corda.testing.driver.* +import net.corda.testing.node.NotarySpec import org.apache.activemq.artemis.api.core.SimpleString import org.apache.activemq.artemis.api.core.TransportConfiguration import org.apache.activemq.artemis.api.core.client.ActiveMQClient @@ -230,6 +231,7 @@ fun rpcDriver( initialiseSerialization: Boolean = true, startNodesInProcess: Boolean = false, extraCordappPackagesToScan: List = emptyList(), + notarySpecs: List = emptyList(), dsl: RPCDriverExposedDSLInterface.() -> A ) = genericDriver( driverDsl = RPCDriverDSL( @@ -241,7 +243,8 @@ fun rpcDriver( useTestClock = useTestClock, isDebug = isDebug, startNodesInProcess = startNodesInProcess, - extraCordappPackagesToScan = extraCordappPackagesToScan + extraCordappPackagesToScan = extraCordappPackagesToScan, + notarySpecs = notarySpecs ) ), coerce = { it }, 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 86f8669777..2f1decd1a2 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 @@ -16,6 +16,7 @@ import net.corda.core.identity.Party import net.corda.core.internal.* import net.corda.core.internal.concurrent.* import net.corda.core.messaging.CordaRPCOps +import net.corda.core.node.NetworkParameters import net.corda.core.node.NodeInfo import net.corda.core.node.NotaryInfo import net.corda.core.node.services.NetworkMapCache @@ -33,13 +34,12 @@ import net.corda.nodeapi.NodeInfoFilesCopier import net.corda.nodeapi.User import net.corda.nodeapi.config.toConfig import net.corda.nodeapi.internal.addShutdownHook -import net.corda.testing.ALICE -import net.corda.testing.BOB -import net.corda.testing.DUMMY_BANK_A +import net.corda.testing.* import net.corda.testing.common.internal.NetworkParametersCopier import net.corda.testing.common.internal.testNetworkParameters -import net.corda.testing.initialiseTestSerialization +import net.corda.testing.node.ClusterSpec import net.corda.testing.node.MockServices.Companion.MOCK_VERSION_INFO +import net.corda.testing.node.NotarySpec import okhttp3.OkHttpClient import okhttp3.Request import org.slf4j.Logger @@ -84,10 +84,48 @@ private val DRIVER_REQUIRED_PERMISSIONS = setOf( invokeRpc(CordaRPCOps::networkMapSnapshot) ) +/** + * Object ecapsulating a notary started automatically by the driver. + */ +data class NotaryHandle(val identity: Party, val validating: Boolean, val nodeHandles: CordaFuture>) + /** * This is the interface that's exposed to DSL users. */ interface DriverDSLExposedInterface : CordformContext { + /** Returns a list of [NotaryHandle]s matching the list of [NotarySpec]s passed into [driver]. */ + val notaryHandles: List + + /** + * Returns the [NotaryHandle] for the single notary on the network. Throws if there are none or more than one. + * @see notaryHandles + */ + val defaultNotaryHandle: NotaryHandle get() { + return when (notaryHandles.size) { + 0 -> throw IllegalStateException("There are no notaries defined on the network") + 1 -> notaryHandles[0] + else -> throw IllegalStateException("There is more than one notary defined on the network") + } + } + + /** + * Returns the identity of the single notary on the network. Throws if there are none or more than one. + * @see defaultNotaryHandle + */ + val defaultNotaryIdentity: Party get() = defaultNotaryHandle.identity + + /** + * Returns a [CordaFuture] on the [NodeHandle] for the single-node notary on the network. Throws if there + * are no notaries or more than one, or if the notary is a distributed cluster. + * @see defaultNotaryHandle + * @see notaryHandles + */ + val defaultNotaryNode: CordaFuture get() { + return defaultNotaryHandle.nodeHandles.map { + it.singleOrNull() ?: throw IllegalStateException("Default notary is not a single node") + } + } + /** * Start a node. * @@ -110,13 +148,6 @@ interface DriverDSLExposedInterface : CordformContext { startInSameProcess: Boolean? = defaultParameters.startInSameProcess, maximumHeapSize: String = defaultParameters.maximumHeapSize): CordaFuture - // TODO This method has been added temporarily, to be deleted once the set of notaries is defined at the network level. - fun startNotaryNode(providedName: CordaX500Name, - rpcUsers: List = emptyList(), - verifierType: VerifierType = VerifierType.InMemory, - customOverrides: Map = emptyMap(), - validating: Boolean = true): CordaFuture - /** * Helper function for starting a [Node] with custom parameters from Java. * @@ -131,24 +162,6 @@ interface DriverDSLExposedInterface : CordformContext { maximumHeapSize: String = "200m" ): List> - /** - * Starts a distributed notary cluster. - * - * @param notaryName The legal name of the advertised distributed notary service. - * @param clusterSize Number of nodes to create for the cluster. - * @param verifierType The type of transaction verifier to use. See: [VerifierType] - * @param rpcUsers List of users who are authorised to use the RPC system. Defaults to empty list. - * @param startInSameProcess Determines if the node should be started inside the same process the Driver is running - * in. If null the Driver-level value will be used. - * @return The [Party] identity of the distributed notary service, and the [NodeInfo]s of the notaries in the cluster. - */ - fun startNotaryCluster( - notaryName: CordaX500Name, - clusterSize: Int = 3, - verifierType: VerifierType = VerifierType.InMemory, - rpcUsers: List = emptyList(), - startInSameProcess: Boolean? = null): CordaFuture>> - /** Call [startWebserver] with a default maximumHeapSize. */ fun startWebserver(handle: NodeHandle): CordaFuture = startWebserver(handle, "200m") @@ -310,6 +323,8 @@ data class NodeParameters( * @param useTestClock If true the test clock will be used in Node. * @param startNodesInProcess Provides the default behaviour of whether new nodes should start inside this process or * not. Note that this may be overridden in [DriverDSLExposedInterface.startNode]. + * @param notarySpecs The notaries advertised in the [NetworkParameters] for this network. These nodes will be started + * automatically and will be available from [DriverDSLExposedInterface.notaryHandles]. Defaults to a simple validating notary. * @param dsl The dsl itself. * @return The value returned in the [dsl] closure. */ @@ -323,6 +338,7 @@ fun driver( useTestClock: Boolean = defaultParameters.useTestClock, initialiseSerialization: Boolean = defaultParameters.initialiseSerialization, startNodesInProcess: Boolean = defaultParameters.startNodesInProcess, + notarySpecs: List = defaultParameters.notarySpecs, extraCordappPackagesToScan: List = defaultParameters.extraCordappPackagesToScan, dsl: DriverDSLExposedInterface.() -> A ): A { @@ -335,6 +351,7 @@ fun driver( useTestClock = useTestClock, isDebug = isDebug, startNodesInProcess = startNodesInProcess, + notarySpecs = notarySpecs, extraCordappPackagesToScan = extraCordappPackagesToScan ), coerce = { it }, @@ -368,6 +385,7 @@ data class DriverParameters( val useTestClock: Boolean = false, val initialiseSerialization: Boolean = true, val startNodesInProcess: Boolean = false, + val notarySpecs: List = listOf(NotarySpec(DUMMY_NOTARY.name)), val extraCordappPackagesToScan: List = emptyList() ) { fun setIsDebug(isDebug: Boolean) = copy(isDebug = isDebug) @@ -379,6 +397,7 @@ data class DriverParameters( fun setInitialiseSerialization(initialiseSerialization: Boolean) = copy(initialiseSerialization = initialiseSerialization) fun setStartNodesInProcess(startNodesInProcess: Boolean) = copy(startNodesInProcess = startNodesInProcess) fun setExtraCordappPackagesToScan(extraCordappPackagesToScan: List) = copy(extraCordappPackagesToScan = extraCordappPackagesToScan) + fun setNotarySpecs(notarySpecs: List) = copy(notarySpecs = notarySpecs) } /** @@ -428,10 +447,10 @@ fun genericD useTestClock: Boolean = defaultParameters.useTestClock, initialiseSerialization: Boolean = defaultParameters.initialiseSerialization, startNodesInProcess: Boolean = defaultParameters.startNodesInProcess, + notarySpecs: List, extraCordappPackagesToScan: List = defaultParameters.extraCordappPackagesToScan, driverDslWrapper: (DriverDSL) -> D, - coerce: (D) -> DI, - dsl: DI.() -> A + coerce: (D) -> DI, dsl: DI.() -> A ): A { val serializationEnv = initialiseTestSerialization(initialiseSerialization) val driverDsl = driverDslWrapper( @@ -443,7 +462,8 @@ fun genericD useTestClock = useTestClock, isDebug = isDebug, startNodesInProcess = startNodesInProcess, - extraCordappPackagesToScan = extraCordappPackagesToScan + extraCordappPackagesToScan = extraCordappPackagesToScan, + notarySpecs = notarySpecs ) ) val shutdownHook = addShutdownHook(driverDsl::shutdown) @@ -643,7 +663,8 @@ class DriverDSL( val useTestClock: Boolean, val isDebug: Boolean, val startNodesInProcess: Boolean, - extraCordappPackagesToScan: List + extraCordappPackagesToScan: List, + val notarySpecs: List ) : DriverDSLInternalInterface { private var _executorService: ScheduledExecutorService? = null val executorService get() = _executorService!! @@ -656,7 +677,9 @@ class DriverDSL( private val nodeInfoFilesCopier = NodeInfoFilesCopier() // Map from a nodes legal name to an observable emitting the number of nodes in its network map. private val countObservables = mutableMapOf>() - private var networkParameters: NetworkParametersCopier? = null + private lateinit var _notaries: List + override val notaryHandles: List get() = _notaries + private lateinit var networkParameters: NetworkParametersCopier class State { val processes = ArrayList>() @@ -744,16 +767,6 @@ class DriverDSL( return startNodeInternal(config, webAddress, startInSameProcess, maximumHeapSize) } - override fun startNotaryNode(providedName: CordaX500Name, - rpcUsers: List, - verifierType: VerifierType, - customOverrides: Map, - validating: Boolean): CordaFuture { - createNetworkParameters(listOf(providedName), providedName, validating, "identity") - val config = customOverrides + NotaryConfig(validating).toConfigMap() - return startNode(providedName = providedName, rpcUsers = rpcUsers, verifierType = verifierType, customOverrides = config) - } - override fun startNodes(nodes: List, startInSameProcess: Boolean?, maximumHeapSize: String): List> { return nodes.map { node -> portAllocation.nextHostAndPort() // rpcAddress @@ -772,72 +785,6 @@ class DriverDSL( } } - // TODO This mapping is done is several plaecs including the gradle plugin. In general we need a better way of - // generating the configs for the nodes, probably making use of Any.toConfig() - private fun NotaryConfig.toConfigMap(): Map = mapOf("notary" to toConfig().root().unwrapped()) - - override fun startNotaryCluster( - notaryName: CordaX500Name, - clusterSize: Int, - verifierType: VerifierType, - rpcUsers: List, - startInSameProcess: Boolean? - ): CordaFuture>> { - fun notaryConfig(nodeAddress: NetworkHostAndPort, clusterAddress: NetworkHostAndPort? = null): Map { - val clusterAddresses = if (clusterAddress != null) listOf(clusterAddress) else emptyList() - val config = NotaryConfig(validating = true, raft = RaftConfig(nodeAddress = nodeAddress, clusterAddresses = clusterAddresses)) - return config.toConfigMap() - } - - require(clusterSize > 0) - - val nodeNames = (0 until clusterSize).map { notaryName.copy(organisation = "${notaryName.organisation}-$it") } - val notaryIdentity = createNetworkParameters( - nodeNames, - notaryName, - validating = true, - serviceId = NotaryService.constructId(validating = true, raft = true)) - - val clusterAddress = portAllocation.nextHostAndPort() - - // Start the first node that will bootstrap the cluster - val firstNotaryFuture = startNode( - providedName = nodeNames[0], - rpcUsers = rpcUsers, - verifierType = verifierType, - customOverrides = notaryConfig(clusterAddress) + mapOf( - "database.serverNameTablePrefix" to nodeNames[0].toString().replace(Regex("[^0-9A-Za-z]+"), "") - ), - startInSameProcess = startInSameProcess - ) - - // All other nodes will join the cluster - val restNotaryFutures = nodeNames.drop(1).map { - val nodeAddress = portAllocation.nextHostAndPort() - startNode( - providedName = it, - rpcUsers = rpcUsers, - verifierType = verifierType, - customOverrides = notaryConfig(nodeAddress, clusterAddress) + mapOf( - "database.serverNameTablePrefix" to it.toString().replace(Regex("[^0-9A-Za-z]+"), "") - )) - } - - return firstNotaryFuture.flatMap { firstNotary -> - restNotaryFutures.transpose().map { restNotaries -> Pair(notaryIdentity, listOf(firstNotary) + restNotaries) } - } - } - - private fun createNetworkParameters(notaryNodeNames: List, notaryName: CordaX500Name, validating: Boolean, serviceId: String): Party { - check(networkParameters == null) { "Notaries must be started first" } - val identity = ServiceIdentityGenerator.generateToDisk( - notaryNodeNames.map { baseDirectory(it) }, - notaryName, - serviceId) - networkParameters = NetworkParametersCopier(testNetworkParameters(listOf(NotaryInfo(identity, validating)))) - return identity - } - private fun queryWebserver(handle: NodeHandle, process: Process): WebserverHandle { val protocol = if (handle.configuration.useHTTPS) "https://" else "http://" val url = URL("$protocol${handle.webAddress}/api/status") @@ -866,6 +813,97 @@ class DriverDSL( _executorService = Executors.newScheduledThreadPool(2, ThreadFactoryBuilder().setNameFormat("driver-pool-thread-%d").build()) _shutdownManager = ShutdownManager(executorService) shutdownManager.registerShutdown { nodeInfoFilesCopier.close() } + val notaryInfos = generateNotaryIdentities() + // The network parameters must be serialised before starting any of the nodes + networkParameters = NetworkParametersCopier(testNetworkParameters(notaryInfos)) + val nodeHandles = startNotaries() + _notaries = notaryInfos.zip(nodeHandles) { (identity, validating), nodes -> NotaryHandle(identity, validating, nodes) } + } + + private fun generateNotaryIdentities(): List { + return notarySpecs.map { spec -> + val identity = if (spec.cluster == null) { + ServiceIdentityGenerator.generateToDisk( + dirs = listOf(baseDirectory(spec.name)), + serviceName = spec.name, + serviceId = "identity") + } else { + ServiceIdentityGenerator.generateToDisk( + dirs = generateNodeNames(spec).map { baseDirectory(it) }, + serviceName = spec.name, + serviceId = NotaryService.constructId( + validating = spec.validating, + raft = spec.cluster is ClusterSpec.Raft)) + } + NotaryInfo(identity, spec.validating) + } + } + + private fun generateNodeNames(spec: NotarySpec): List { + return (0 until spec.cluster!!.clusterSize).map { spec.name.copy(organisation = "${spec.name.organisation}-$it") } + } + + private fun startNotaries(): List>> { + return notarySpecs.map { + when { + it.cluster == null -> startSingleNotary(it) + it.cluster is ClusterSpec.Raft -> startRaftNotaryCluster(it) + else -> throw IllegalArgumentException("BFT-SMaRt not supported") + } + } + } + + // TODO This mapping is done is several places including the gradle plugin. In general we need a better way of + // generating the configs for the nodes, probably making use of Any.toConfig() + private fun NotaryConfig.toConfigMap(): Map = mapOf("notary" to toConfig().root().unwrapped()) + + private fun startSingleNotary(spec: NotarySpec): CordaFuture> { + return startNode( + providedName = spec.name, + rpcUsers = spec.rpcUsers, + verifierType = spec.verifierType, + customOverrides = NotaryConfig(spec.validating).toConfigMap() + ).map { listOf(it) } + } + + private fun startRaftNotaryCluster(spec: NotarySpec): CordaFuture> { + fun notaryConfig(nodeAddress: NetworkHostAndPort, clusterAddress: NetworkHostAndPort? = null): Map { + val clusterAddresses = if (clusterAddress != null) listOf(clusterAddress) else emptyList() + val config = NotaryConfig( + validating = spec.validating, + raft = RaftConfig(nodeAddress = nodeAddress, clusterAddresses = clusterAddresses)) + return config.toConfigMap() + } + + val nodeNames = generateNodeNames(spec) + val clusterAddress = portAllocation.nextHostAndPort() + + // Start the first node that will bootstrap the cluster + val firstNodeFuture = startNode( + providedName = nodeNames[0], + rpcUsers = spec.rpcUsers, + verifierType = spec.verifierType, + customOverrides = notaryConfig(clusterAddress) + mapOf( + "database.serverNameTablePrefix" to nodeNames[0].toString().replace(Regex("[^0-9A-Za-z]+"), "") + ) + ) + + // All other nodes will join the cluster + val restNodeFutures = nodeNames.drop(1).map { + val nodeAddress = portAllocation.nextHostAndPort() + startNode( + providedName = it, + rpcUsers = spec.rpcUsers, + verifierType = spec.verifierType, + customOverrides = notaryConfig(nodeAddress, clusterAddress) + mapOf( + "database.serverNameTablePrefix" to it.toString().replace(Regex("[^0-9A-Za-z]+"), "") + ) + ) + } + + return firstNodeFuture.flatMap { first -> + restNodeFutures.transpose().map { rest -> listOf(first) + rest } + } } fun baseDirectory(nodeName: CordaX500Name): Path { @@ -925,10 +963,7 @@ class DriverDSL( maximumHeapSize: String): CordaFuture { val configuration = config.parseAsNodeConfiguration() val baseDirectory = configuration.baseDirectory.createDirectories() - if (networkParameters == null) { - networkParameters = NetworkParametersCopier(testNetworkParameters(emptyList())) - } - networkParameters!!.install(baseDirectory) + networkParameters.install(baseDirectory) nodeInfoFilesCopier.addConfig(baseDirectory) val onNodeExit: () -> Unit = { nodeInfoFilesCopier.removeConfig(baseDirectory) diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNode.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNode.kt index 321bb9c41a..6de62ae7db 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNode.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNode.kt @@ -7,6 +7,7 @@ import com.nhaarman.mockito_kotlin.whenever import net.corda.core.crypto.entropyToKeyPair import net.corda.core.crypto.random63BitValue import net.corda.core.identity.CordaX500Name +import net.corda.core.identity.Party import net.corda.core.internal.createDirectories import net.corda.core.internal.createDirectory import net.corda.core.internal.uncheckedCast @@ -110,6 +111,9 @@ data class MockNodeArgs( * 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]. */ class MockNetwork(defaultParameters: MockNetworkParameters = MockNetworkParameters(), private val networkSendManuallyPumped: Boolean = defaultParameters.networkSendManuallyPumped, @@ -117,6 +121,7 @@ class MockNetwork(defaultParameters: MockNetworkParameters = MockNetworkParamete servicePeerAllocationStrategy: InMemoryMessagingNetwork.ServicePeerAllocationStrategy = defaultParameters.servicePeerAllocationStrategy, private val defaultFactory: (MockNodeArgs) -> MockNode = defaultParameters.defaultFactory, initialiseSerialization: Boolean = defaultParameters.initialiseSerialization, + private val notarySpecs: List = listOf(NotarySpec(DUMMY_NOTARY.name)), private val cordappPackages: List = defaultParameters.cordappPackages) : Closeable { /** Helper constructor for creating a [MockNetwork] with custom parameters from Java. */ constructor(parameters: MockNetworkParameters) : this(defaultParameters = parameters) @@ -128,22 +133,43 @@ class MockNetwork(defaultParameters: MockNetworkParameters = MockNetworkParamete val messagingNetwork = InMemoryMessagingNetwork(networkSendManuallyPumped, servicePeerAllocationStrategy, busyLatch) // A unique identifier for this network to segregate databases with the same nodeID but different networks. private val networkId = random63BitValue() - private lateinit var networkParameters: NetworkParametersCopier - private var notaryInfos: MutableList? = ArrayList() - private val _nodes = ArrayList() + private val networkParameters: NetworkParametersCopier + private val _nodes = mutableListOf() private val serializationEnv = initialiseTestSerialization(initialiseSerialization) + private val sharedUserCount = AtomicInteger(0) /** A read only view of the current set of executing nodes. */ val nodes: List get() = _nodes - init { - filesystem.getPath("/nodes").createDirectory() + /** + * Returns the list of nodes started by the network. Each notary specified when the network is constructed ([notarySpecs] + * parameter) maps 1:1 to the notaries returned by this list. + */ + val notaryNodes: List> + + /** + * Returns the single notary node on the network. Throws if there are none or more than one. + * @see notaryNodes + */ + val defaultNotaryNode: StartedNode get() { + return when (notaryNodes.size) { + 0 -> throw IllegalStateException("There are no notaries defined on the network") + 1 -> notaryNodes[0] + else -> throw IllegalStateException("There is more than one notary defined on the network") + } + } + + /** + * Return the identity of the default notary node. + * @see defaultNotaryNode + */ + val defaultNotaryIdentity: Party get() { + return defaultNotaryNode.info.legalIdentities.singleOrNull() ?: throw IllegalStateException("Default notary has multiple identities") } /** * Because this executor is shared, we need to be careful about nodes shutting it down. */ - private val sharedUserCount = AtomicInteger(0) private val sharedServerThread = object : ServiceAffinityExecutor("Mock network", 1) { override fun shutdown() { // We don't actually allow the shutdown of the network-wide shared thread pool until all references to @@ -163,6 +189,32 @@ class MockNetwork(defaultParameters: MockNetworkParameters = MockNetworkParamete } } + init { + filesystem.getPath("/nodes").createDirectory() + val notaryInfos = generateNotaryIdentities() + // The network parameters must be serialised before starting any of the nodes + networkParameters = NetworkParametersCopier(testNetworkParameters(notaryInfos)) + notaryNodes = createNotaries() + } + + private fun generateNotaryIdentities(): List { + return notarySpecs.mapIndexed { index, spec -> + val identity = ServiceIdentityGenerator.generateToDisk( + dirs = listOf(baseDirectory(nextNodeId + index)), + serviceName = spec.name, + serviceId = "identity") + NotaryInfo(identity, spec.validating) + } + } + + private fun createNotaries(): List> { + return notarySpecs.map { spec -> + createNode(MockNodeParameters(legalName = spec.name, configOverrides = { + doReturn(NotaryConfig(spec.validating)).whenever(it).notary + })) + } + } + open class MockNode(args: MockNodeArgs) : AbstractNode( args.config, TestClock(), @@ -188,20 +240,12 @@ class MockNetwork(defaultParameters: MockNetworkParameters = MockNetworkParamete override val started: StartedNode? get() = uncheckedCast(super.started) override fun start(): StartedNode { - installNetworkParameters() + mockNet.networkParameters.install(configuration.baseDirectory) val started: StartedNode = uncheckedCast(super.start()) advertiseNodeToNetwork(started) return started } - private fun installNetworkParameters() { - mockNet.notaryInfos?.let { - mockNet.networkParameters = NetworkParametersCopier(testNetworkParameters(it)) - } - mockNet.notaryInfos = null - mockNet.networkParameters.install(configuration.baseDirectory) - } - private fun advertiseNodeToNetwork(newNode: StartedNode) { mockNet.nodes .mapNotNull { it.started } @@ -273,9 +317,6 @@ class MockNetwork(defaultParameters: MockNetworkParameters = MockNetworkParamete dbCloser = null } - fun hasDBConnection() = dbCloser != null - - // You can change this from zero if you have custom [FlowLogic] that park themselves. e.g. [StateMachineManagerTests] var acceptableLiveFiberCountOnStop: Int = 0 override fun acceptableLiveFiberCountOnStop(): Int = acceptableLiveFiberCountOnStop @@ -321,23 +362,11 @@ class MockNetwork(defaultParameters: MockNetworkParameters = MockNetworkParamete doReturn(makeTestDataSourceProperties("node_${id}_net_$networkId")).whenever(it).dataSourceProperties parameters.configOverrides(it) } - val node = nodeFactory(MockNodeArgs(config, this, id, parameters.entropyRoot)) _nodes += node - - config.notary?.let { notaryConfig -> - val notaryInfos = notaryInfos ?: throw IllegalStateException("Cannot add notaries once nodes have started") - if (!notaryConfig.isClusterConfig) { - // Create the node's main identity, which will also double as its notary identity - val identity = ServiceIdentityGenerator.generateToDisk(listOf(config.baseDirectory), config.myLegalName, "identity") - notaryInfos += NotaryInfo(identity, notaryConfig.validating) - } - } - if (start) { node.start() } - return node } @@ -365,24 +394,7 @@ class MockNetwork(defaultParameters: MockNetworkParameters = MockNetworkParamete } @JvmOverloads - fun createNotaryNode(parameters: MockNodeParameters = MockNodeParameters(legalName = DUMMY_NOTARY.name), - validating: Boolean = true): StartedNode { - return createNotaryNode(parameters, validating, defaultFactory) - } - fun createNotaryNode(parameters: MockNodeParameters = MockNodeParameters(legalName = DUMMY_NOTARY.name), - validating: Boolean = true, - nodeFactory: (MockNodeArgs) -> N): StartedNode { - return createNode( - parameters.copy(configOverrides = { - doReturn(NotaryConfig(validating)).whenever(it).notary - parameters.configOverrides(it) - }), - nodeFactory - ) - } - - @JvmOverloads fun createPartyNode(legalName: CordaX500Name? = null): StartedNode { return createNode(MockNodeParameters(legalName = legalName)) } @@ -418,14 +430,16 @@ class MockNetwork(defaultParameters: MockNetworkParameters = MockNetworkParamete override fun close() { stopNodes() } + + data class NotarySpec(val name: CordaX500Name, val validating: Boolean = true) { + constructor(name: CordaX500Name) : this(name, validating = true) + } } -fun network(nodesCount: Int, action: MockNetwork.(nodes: List>, notary: StartedNode) -> Unit) { - MockNetwork().use { - it.runNetwork() - val notary = it.createNotaryNode() - val nodes = (1..nodesCount).map { _ -> it.createPartyNode() } - action(it, nodes, notary) +fun network(nodesCount: Int, action: MockNetwork.(List>) -> Unit) { + MockNetwork().use { mockNet -> + val nodes = (1..nodesCount).map { mockNet.createPartyNode() } + mockNet.action(nodes) } } diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/NotarySpec.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/NotarySpec.kt new file mode 100644 index 0000000000..336d6bfc8d --- /dev/null +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/NotarySpec.kt @@ -0,0 +1,23 @@ +package net.corda.testing.node + +import net.corda.core.identity.CordaX500Name +import net.corda.node.services.config.VerifierType +import net.corda.nodeapi.User + +data class NotarySpec( + val name: CordaX500Name, + val validating: Boolean = true, + val rpcUsers: List = emptyList(), + val verifierType: VerifierType = VerifierType.InMemory, + val cluster: ClusterSpec? = null +) + +sealed class ClusterSpec { + abstract val clusterSize: Int + + data class Raft(override val clusterSize: Int) : ClusterSpec() { + init { + require(clusterSize > 0) + } + } +} \ 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 869df2ab64..3bd939d4ab 100644 --- a/tools/explorer/src/main/kotlin/net/corda/explorer/ExplorerSimulation.kt +++ b/tools/explorer/src/main/kotlin/net/corda/explorer/ExplorerSimulation.kt @@ -24,14 +24,13 @@ import net.corda.node.services.Permissions.Companion.startFlow import net.corda.nodeapi.User import net.corda.testing.ALICE import net.corda.testing.BOB -import net.corda.testing.DUMMY_NOTARY import net.corda.testing.driver.NodeHandle import net.corda.testing.driver.PortAllocation import net.corda.testing.driver.driver import java.time.Instant import java.util.* -class ExplorerSimulation(val options: OptionSet) { +class ExplorerSimulation(private val options: OptionSet) { private val user = User("user1", "test", permissions = setOf( startFlow(), startFlow() @@ -67,23 +66,16 @@ class ExplorerSimulation(val options: OptionSet) { val portAllocation = PortAllocation.Incremental(20000) driver(portAllocation = portAllocation, extraCordappPackagesToScan = listOf("net.corda.finance")) { // TODO : Supported flow should be exposed somehow from the node instead of set of ServiceInfo. - val notary = startNotaryNode(DUMMY_NOTARY.name, customOverrides = mapOf("nearestCity" to "Zurich"), validating = false) - val alice = startNode(providedName = ALICE.name, rpcUsers = arrayListOf(user), - customOverrides = mapOf("nearestCity" to "Milan")) - val bob = startNode(providedName = BOB.name, rpcUsers = arrayListOf(user), - customOverrides = mapOf("nearestCity" to "Madrid")) + val alice = startNode(providedName = ALICE.name, rpcUsers = listOf(user)) + val bob = startNode(providedName = BOB.name, rpcUsers = listOf(user)) val ukBankName = CordaX500Name(organisation = "UK Bank Plc", locality = "London", country = "GB") val usaBankName = CordaX500Name(organisation = "USA Bank Corp", locality = "New York", country = "US") - val issuerGBP = startNode(providedName = ukBankName, rpcUsers = arrayListOf(manager), - customOverrides = mapOf( - "issuableCurrencies" to listOf("GBP"), - "nearestCity" to "London")) - val issuerUSD = startNode(providedName = usaBankName, rpcUsers = arrayListOf(manager), - customOverrides = mapOf( - "issuableCurrencies" to listOf("USD"), - "nearestCity" to "New York")) + val issuerGBP = startNode(providedName = ukBankName, rpcUsers = listOf(manager), + customOverrides = mapOf("issuableCurrencies" to listOf("GBP"))) + val issuerUSD = startNode(providedName = usaBankName, rpcUsers = listOf(manager), + customOverrides = mapOf("issuableCurrencies" to listOf("USD"))) - notaryNode = notary.get() + notaryNode = defaultNotaryNode.get() aliceNode = alice.get() bobNode = bob.get() issuerNodeGBP = issuerGBP.get() 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 7184c381b7..d6935f8913 100644 --- a/verifier/src/integration-test/kotlin/net/corda/verifier/VerifierDriver.kt +++ b/verifier/src/integration-test/kotlin/net/corda/verifier/VerifierDriver.kt @@ -19,6 +19,7 @@ import net.corda.nodeapi.VerifierApi import net.corda.nodeapi.config.NodeSSLConfiguration import net.corda.nodeapi.config.SSLConfiguration import net.corda.testing.driver.* +import net.corda.testing.node.NotarySpec import org.apache.activemq.artemis.api.core.SimpleString import org.apache.activemq.artemis.api.core.client.ActiveMQClient import org.apache.activemq.artemis.api.core.client.ClientProducer @@ -77,6 +78,7 @@ fun verifierDriver( useTestClock: Boolean = false, startNodesInProcess: Boolean = false, extraCordappPackagesToScan: List = emptyList(), + notarySpecs: List = emptyList(), dsl: VerifierExposedDSLInterface.() -> A ) = genericDriver( driverDsl = VerifierDriverDSL( @@ -88,7 +90,8 @@ fun verifierDriver( useTestClock = useTestClock, isDebug = isDebug, startNodesInProcess = startNodesInProcess, - extraCordappPackagesToScan = extraCordappPackagesToScan + extraCordappPackagesToScan = extraCordappPackagesToScan, + notarySpecs = notarySpecs ) ), coerce = { it }, 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 9072bdbe52..852caaecda 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.ALICE import net.corda.testing.ALICE_NAME import net.corda.testing.DUMMY_NOTARY +import net.corda.testing.node.NotarySpec import org.junit.Test import java.util.* import java.util.concurrent.atomic.AtomicInteger @@ -128,15 +129,15 @@ class VerifierTests { @Test fun `single verifier works with a node`() { - verifierDriver(extraCordappPackagesToScan = listOf("net.corda.finance.contracts")) { - val (notaryNode, aliceNode) = listOf( - startNotaryNode(DUMMY_NOTARY.name, verifierType = VerifierType.OutOfProcess), - startNode(providedName = ALICE.name) - ).transpose().getOrThrow() + verifierDriver( + extraCordappPackagesToScan = listOf("net.corda.finance.contracts"), + notarySpecs = listOf(NotarySpec(DUMMY_NOTARY.name, verifierType = VerifierType.OutOfProcess)) + ) { + val aliceNode = startNode(providedName = ALICE.name).getOrThrow() + val notaryNode = defaultNotaryNode.getOrThrow() val alice = aliceNode.rpc.wellKnownPartyFromX500Name(ALICE_NAME)!! - val notary = notaryNode.rpc.notaryPartyFromX500Name(DUMMY_NOTARY.name)!! startVerifier(notaryNode) - aliceNode.rpc.startFlow(::CashIssueFlow, 10.DOLLARS, OpaqueBytes.of(0), notary).returnValue.get() + aliceNode.rpc.startFlow(::CashIssueFlow, 10.DOLLARS, OpaqueBytes.of(0), defaultNotaryIdentity).returnValue.get() notaryNode.waitUntilNumberOfVerifiers(1) for (i in 1..10) { val cashFlowResult = aliceNode.rpc.startFlow(::CashPaymentFlow, 10.DOLLARS, alice).returnValue.get() diff --git a/webserver/src/integration-test/kotlin/net/corda/webserver/WebserverDriverTests.kt b/webserver/src/integration-test/kotlin/net/corda/webserver/WebserverDriverTests.kt index cd4f35577d..ee85cef6fd 100644 --- a/webserver/src/integration-test/kotlin/net/corda/webserver/WebserverDriverTests.kt +++ b/webserver/src/integration-test/kotlin/net/corda/webserver/WebserverDriverTests.kt @@ -11,7 +11,7 @@ import org.junit.Test import java.util.concurrent.Executors import java.util.concurrent.ScheduledExecutorService -class DriverTests { +class WebserverDriverTests { companion object { val executorService: ScheduledExecutorService = Executors.newScheduledThreadPool(2) From 9baa9037ae02c94a73f833ab4b30b63775659c8b Mon Sep 17 00:00:00 2001 From: Andrzej Cichocki Date: Mon, 6 Nov 2017 10:53:02 +0000 Subject: [PATCH 21/24] Retire marker interface. (#1997) --- .../integration-test/kotlin/net/corda/irs/IRSDemoTest.kt | 3 +-- .../kotlin/net/corda/vega/SimmValuationTest.kt | 2 +- .../kotlin/net/corda/testing/IntegrationTestCategory.kt | 6 ------ 3 files changed, 2 insertions(+), 9 deletions(-) delete mode 100644 testing/test-utils/src/main/kotlin/net/corda/testing/IntegrationTestCategory.kt 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 42c4597aee..ca9b841d9f 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 @@ -33,8 +33,7 @@ import rx.Observable import java.time.Duration import java.time.LocalDate -class IRSDemoTest : IntegrationTestCategory { - +class IRSDemoTest { companion object { val log = loggerFor() } diff --git a/samples/simm-valuation-demo/src/integration-test/kotlin/net/corda/vega/SimmValuationTest.kt b/samples/simm-valuation-demo/src/integration-test/kotlin/net/corda/vega/SimmValuationTest.kt index a970dd7312..6c9c6cff74 100644 --- a/samples/simm-valuation-demo/src/integration-test/kotlin/net/corda/vega/SimmValuationTest.kt +++ b/samples/simm-valuation-demo/src/integration-test/kotlin/net/corda/vega/SimmValuationTest.kt @@ -17,7 +17,7 @@ import org.junit.Test import java.math.BigDecimal import java.time.LocalDate -class SimmValuationTest : IntegrationTestCategory { +class SimmValuationTest { private companion object { // SIMM demo can only currently handle one valuation date due to a lack of market data or a market data source. val valuationDate: LocalDate = LocalDate.parse("2016-06-06") diff --git a/testing/test-utils/src/main/kotlin/net/corda/testing/IntegrationTestCategory.kt b/testing/test-utils/src/main/kotlin/net/corda/testing/IntegrationTestCategory.kt deleted file mode 100644 index cea4389b47..0000000000 --- a/testing/test-utils/src/main/kotlin/net/corda/testing/IntegrationTestCategory.kt +++ /dev/null @@ -1,6 +0,0 @@ -package net.corda.testing - -/** - * Marker interface for tests which are only run as part of the integration tests. - */ -interface IntegrationTestCategory From 00a90148528ddf71eed3a9a2dcf44da2140b4b2d Mon Sep 17 00:00:00 2001 From: Andrzej Cichocki Date: Mon, 6 Nov 2017 11:13:56 +0000 Subject: [PATCH 22/24] CORDA-716 Rename one TestClock to DemoClock, and unduplicate code (#1988) --- .../net/corda/node/internal/CordaClock.kt | 61 +++++++++++++++++++ .../net/corda/node/internal/MutableClock.kt | 45 -------------- .../kotlin/net/corda/node/internal/Node.kt | 5 +- .../net/corda/node/serialization/NodeClock.kt | 36 ----------- .../net/corda/node/utilities/DemoClock.kt | 23 +++++++ .../net/corda/node/utilities/TestClock.kt | 49 --------------- .../corda/irs/flows/UpdateBusinessDayFlow.kt | 5 +- .../net/corda/netmap/simulation/Simulation.kt | 3 +- .../kotlin/net/corda/testing/node/MockNode.kt | 3 +- .../net/corda/testing/node/TestClock.kt | 40 +----------- 10 files changed, 95 insertions(+), 175 deletions(-) create mode 100644 node/src/main/kotlin/net/corda/node/internal/CordaClock.kt delete mode 100644 node/src/main/kotlin/net/corda/node/internal/MutableClock.kt delete mode 100644 node/src/main/kotlin/net/corda/node/serialization/NodeClock.kt create mode 100644 node/src/main/kotlin/net/corda/node/utilities/DemoClock.kt delete mode 100644 node/src/main/kotlin/net/corda/node/utilities/TestClock.kt diff --git a/node/src/main/kotlin/net/corda/node/internal/CordaClock.kt b/node/src/main/kotlin/net/corda/node/internal/CordaClock.kt new file mode 100644 index 0000000000..cd86529f67 --- /dev/null +++ b/node/src/main/kotlin/net/corda/node/internal/CordaClock.kt @@ -0,0 +1,61 @@ +package net.corda.node.internal + +import net.corda.core.serialization.SerializeAsToken +import net.corda.core.serialization.SerializeAsTokenContext +import net.corda.core.serialization.SingletonSerializationToken +import rx.Observable +import rx.Subscriber +import rx.subscriptions.Subscriptions +import java.time.Clock +import java.time.Instant +import java.time.ZoneId +import java.util.concurrent.CopyOnWriteArraySet +import java.util.concurrent.atomic.AtomicLong +import javax.annotation.concurrent.ThreadSafe + +/** A [Clock] that tokenizes itself when serialized, and delegates to an underlying [Clock] implementation. */ +abstract class CordaClock : Clock(), SerializeAsToken { + protected abstract val delegateClock: Clock + private val token = SingletonSerializationToken.singletonSerializationToken(javaClass) + override fun toToken(context: SerializeAsTokenContext) = token.registerWithContext(context, this) + override fun instant(): Instant = delegateClock.instant() + override fun getZone(): ZoneId = delegateClock.zone + @Deprecated("Do not use this. Instead seek to use ZonedDateTime methods.", level = DeprecationLevel.ERROR) + override fun withZone(zone: ZoneId) = throw UnsupportedOperationException("Tokenized clock does not support withZone()") +} + +@ThreadSafe +class SimpleClock(override val delegateClock: Clock) : CordaClock() + +/** + * An abstract class with helper methods for a type of Clock that might have it's concept of "now" adjusted externally. + * e.g. for testing (so unit tests do not have to wait for timeouts in realtime) or for demos and simulations. + */ +abstract class MutableClock(private var _delegateClock: Clock) : CordaClock() { + override var delegateClock + @Synchronized get() = _delegateClock + @Synchronized set(clock) { + _delegateClock = clock + } + private val _version = AtomicLong(0L) + /** This is an observer on the mutation count of this [Clock], which reflects the occurence of mutations. */ + val mutations: Observable by lazy { + Observable.create { subscriber: Subscriber -> + if (!subscriber.isUnsubscribed) { + mutationObservers.add(subscriber) + // This is not very intuitive, but subscribing to a subscriber observes unsubscribes. + subscriber.add(Subscriptions.create { mutationObservers.remove(subscriber) }) + } + } + } + private val mutationObservers = CopyOnWriteArraySet>() + /** Must be called by subclasses when they mutate (but not just with the passage of time as per the "wall clock"). */ + protected fun notifyMutationObservers() { + val version = _version.incrementAndGet() + for (observer in mutationObservers) { + if (!observer.isUnsubscribed) { + observer.onNext(version) + } + } + } +} diff --git a/node/src/main/kotlin/net/corda/node/internal/MutableClock.kt b/node/src/main/kotlin/net/corda/node/internal/MutableClock.kt deleted file mode 100644 index 3a52cf377c..0000000000 --- a/node/src/main/kotlin/net/corda/node/internal/MutableClock.kt +++ /dev/null @@ -1,45 +0,0 @@ -package net.corda.node.internal - -import rx.Observable -import rx.Subscriber -import rx.subscriptions.Subscriptions -import java.time.Clock -import java.util.concurrent.CopyOnWriteArraySet -import java.util.concurrent.atomic.AtomicLong - -/** - * An abstract class with helper methods for a type of Clock that might have it's concept of "now" - * adjusted externally. - * - * e.g. for testing (so unit tests do not have to wait for timeouts in realtime) or for demos and simulations. - */ -abstract class MutableClock : Clock() { - private val _version = AtomicLong(0L) - - /** - * This is an observer on the mutation count of this [Clock], which reflects the occurence of mutations. - */ - val mutations: Observable by lazy { - Observable.create({ subscriber: Subscriber -> - if (!subscriber.isUnsubscribed) { - mutationObservers.add(subscriber) - // This is not very intuitive, but subscribing to a subscriber observes unsubscribes. - subscriber.add(Subscriptions.create { mutationObservers.remove(subscriber) }) - } - }) - } - - private val mutationObservers = CopyOnWriteArraySet>() - - /** - * Must be called by subclasses when they mutate (but not just with the passage of time as per the "wall clock"). - */ - protected fun notifyMutationObservers() { - val version = _version.incrementAndGet() - for (observer in mutationObservers) { - if (!observer.isUnsubscribed) { - observer.onNext(version) - } - } - } -} \ No newline at end of file diff --git a/node/src/main/kotlin/net/corda/node/internal/Node.kt b/node/src/main/kotlin/net/corda/node/internal/Node.kt index bef75a6966..bb617bb55a 100644 --- a/node/src/main/kotlin/net/corda/node/internal/Node.kt +++ b/node/src/main/kotlin/net/corda/node/internal/Node.kt @@ -15,7 +15,6 @@ import net.corda.core.utilities.loggerFor import net.corda.node.VersionInfo import net.corda.node.internal.cordapp.CordappLoader import net.corda.node.serialization.KryoServerSerializationScheme -import net.corda.node.serialization.NodeClock import net.corda.node.services.RPCUserService import net.corda.node.services.RPCUserServiceImpl import net.corda.node.services.api.SchemaService @@ -25,7 +24,7 @@ import net.corda.node.services.messaging.MessagingService import net.corda.node.services.messaging.NodeMessagingClient import net.corda.node.utilities.AddressUtils import net.corda.node.utilities.AffinityExecutor -import net.corda.node.utilities.TestClock +import net.corda.node.utilities.DemoClock import net.corda.nodeapi.ArtemisMessagingComponent import net.corda.nodeapi.internal.ShutdownHook import net.corda.nodeapi.internal.addShutdownHook @@ -67,7 +66,7 @@ open class Node(configuration: NodeConfiguration, } private fun createClock(configuration: NodeConfiguration): Clock { - return if (configuration.useTestClock) TestClock() else NodeClock() + return (if (configuration.useTestClock) ::DemoClock else ::SimpleClock)(Clock.systemUTC()) } private val sameVmNodeCounter = AtomicInteger() diff --git a/node/src/main/kotlin/net/corda/node/serialization/NodeClock.kt b/node/src/main/kotlin/net/corda/node/serialization/NodeClock.kt deleted file mode 100644 index 313c3e14bc..0000000000 --- a/node/src/main/kotlin/net/corda/node/serialization/NodeClock.kt +++ /dev/null @@ -1,36 +0,0 @@ -package net.corda.node.serialization - -import net.corda.core.serialization.SerializeAsToken -import net.corda.core.serialization.SerializeAsTokenContext -import net.corda.core.serialization.SingletonSerializationToken -import net.corda.core.serialization.SingletonSerializationToken.Companion.singletonSerializationToken -import java.time.Clock -import java.time.Instant -import java.time.ZoneId -import javax.annotation.concurrent.ThreadSafe - - -/** - * A [Clock] that tokenizes itself when serialized, and delegates to an underlying [Clock] implementation. - */ -@ThreadSafe -class NodeClock(private val delegateClock: Clock = Clock.systemUTC()) : Clock(), SerializeAsToken { - - private val token = singletonSerializationToken(javaClass) - - override fun toToken(context: SerializeAsTokenContext) = token.registerWithContext(context, this) - - override fun instant(): Instant { - return delegateClock.instant() - } - - // Do not use this. Instead seek to use ZonedDateTime methods. - override fun withZone(zone: ZoneId): Clock { - throw UnsupportedOperationException("Tokenized clock does not support withZone()") - } - - override fun getZone(): ZoneId { - return delegateClock.zone - } - -} diff --git a/node/src/main/kotlin/net/corda/node/utilities/DemoClock.kt b/node/src/main/kotlin/net/corda/node/utilities/DemoClock.kt new file mode 100644 index 0000000000..0d6ab6b48c --- /dev/null +++ b/node/src/main/kotlin/net/corda/node/utilities/DemoClock.kt @@ -0,0 +1,23 @@ +package net.corda.node.utilities + +import net.corda.core.internal.until +import net.corda.node.internal.MutableClock +import java.time.Clock +import java.time.LocalDate +import javax.annotation.concurrent.ThreadSafe + +/** A [Clock] that can have the date advanced for use in demos. */ +@ThreadSafe +class DemoClock(delegateClock: Clock) : MutableClock(delegateClock) { + @Synchronized + fun updateDate(date: LocalDate): Boolean { + val currentDate = LocalDate.now(this) + if (currentDate.isBefore(date)) { + // It's ok to increment + delegateClock = Clock.offset(delegateClock, currentDate.atStartOfDay() until date.atStartOfDay()) + notifyMutationObservers() + return true + } + return false + } +} diff --git a/node/src/main/kotlin/net/corda/node/utilities/TestClock.kt b/node/src/main/kotlin/net/corda/node/utilities/TestClock.kt deleted file mode 100644 index 60af87f895..0000000000 --- a/node/src/main/kotlin/net/corda/node/utilities/TestClock.kt +++ /dev/null @@ -1,49 +0,0 @@ -package net.corda.node.utilities - -import net.corda.core.internal.until -import net.corda.core.serialization.SerializeAsToken -import net.corda.core.serialization.SerializeAsTokenContext -import net.corda.core.serialization.SingletonSerializationToken.Companion.singletonSerializationToken -import net.corda.node.internal.MutableClock -import java.time.Clock -import java.time.Instant -import java.time.LocalDate -import java.time.ZoneId -import javax.annotation.concurrent.ThreadSafe - -/** - * A [Clock] that can have the date advanced for use in demos. - */ -@ThreadSafe -class TestClock(private var delegateClock: Clock = Clock.systemUTC()) : MutableClock(), SerializeAsToken { - - private val token = singletonSerializationToken(javaClass) - - override fun toToken(context: SerializeAsTokenContext) = token.registerWithContext(context, this) - - @Synchronized - fun updateDate(date: LocalDate): Boolean { - val currentDate = LocalDate.now(this) - if (currentDate.isBefore(date)) { - // It's ok to increment - delegateClock = Clock.offset(delegateClock, currentDate.atStartOfDay() until date.atStartOfDay()) - notifyMutationObservers() - return true - } - return false - } - - @Synchronized override fun instant(): Instant { - return delegateClock.instant() - } - - // Do not use this. Instead seek to use ZonedDateTime methods. - override fun withZone(zone: ZoneId): Clock { - throw UnsupportedOperationException("Tokenized clock does not support withZone()") - } - - @Synchronized override fun getZone(): ZoneId { - return delegateClock.zone - } - -} diff --git a/samples/irs-demo/cordapp/src/test/kotlin/net/corda/irs/flows/UpdateBusinessDayFlow.kt b/samples/irs-demo/cordapp/src/test/kotlin/net/corda/irs/flows/UpdateBusinessDayFlow.kt index 1e36a3bc86..ae2dadab5e 100644 --- a/samples/irs-demo/cordapp/src/test/kotlin/net/corda/irs/flows/UpdateBusinessDayFlow.kt +++ b/samples/irs-demo/cordapp/src/test/kotlin/net/corda/irs/flows/UpdateBusinessDayFlow.kt @@ -7,11 +7,10 @@ import net.corda.core.flows.InitiatingFlow import net.corda.core.flows.StartableByRPC import net.corda.core.identity.Party import net.corda.core.flows.* -import net.corda.core.node.NodeInfo import net.corda.core.serialization.CordaSerializable import net.corda.core.utilities.ProgressTracker import net.corda.core.utilities.unwrap -import net.corda.node.utilities.TestClock +import net.corda.node.utilities.DemoClock import java.time.LocalDate /** @@ -27,7 +26,7 @@ object UpdateBusinessDayFlow { @Suspendable override fun call() { val message = otherPartySession.receive().unwrap { it } - (serviceHub.clock as TestClock).updateDate(message.date) + (serviceHub.clock as DemoClock).updateDate(message.date) } } 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 e1c597e6a5..25d3cfc205 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 @@ -110,7 +110,8 @@ abstract class Simulation(val networkSendManuallyPumped: Boolean, init { // Advance node clocks when current time is changed dateChanges.subscribe { - clocks.setTo(currentDateAndTime.toInstant(ZoneOffset.UTC)) + val instant = currentDateAndTime.toInstant(ZoneOffset.UTC) + clocks.forEach { it.setTo(instant) } } } diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNode.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNode.kt index 6de62ae7db..a44afbeb79 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNode.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNode.kt @@ -51,6 +51,7 @@ import java.math.BigInteger import java.nio.file.Path import java.security.KeyPair import java.security.PublicKey +import java.time.Clock import java.util.concurrent.TimeUnit import java.util.concurrent.atomic.AtomicInteger @@ -217,7 +218,7 @@ class MockNetwork(defaultParameters: MockNetworkParameters = MockNetworkParamete open class MockNode(args: MockNodeArgs) : AbstractNode( args.config, - TestClock(), + TestClock(Clock.systemUTC()), MOCK_VERSION_INFO, CordappLoader.createDefaultWithTestPackages(args.config, args.network.cordappPackages), args.network.busyLatch diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/TestClock.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/TestClock.kt index e1149b1fda..75d6563ae4 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/TestClock.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/TestClock.kt @@ -1,30 +1,16 @@ package net.corda.testing.node import net.corda.core.internal.until -import net.corda.core.serialization.SerializeAsToken -import net.corda.core.serialization.SerializeAsTokenContext -import net.corda.core.serialization.SingletonSerializationToken.Companion.singletonSerializationToken import net.corda.node.internal.MutableClock import java.time.Clock import java.time.Duration import java.time.Instant -import java.time.ZoneId import javax.annotation.concurrent.ThreadSafe - -/** - * A [Clock] that can have the time advanced for use in testing. - */ +/** A [Clock] that can have the time advanced for use in testing. */ @ThreadSafe -class TestClock(private var delegateClock: Clock = Clock.systemUTC()) : MutableClock(), SerializeAsToken { - - private val token = singletonSerializationToken(javaClass) - - override fun toToken(context: SerializeAsTokenContext) = token.registerWithContext(context, this) - - /** - * Advance this [Clock] by the specified [Duration] for testing purposes. - */ +class TestClock(delegateClock: Clock) : MutableClock(delegateClock) { + /** Advance this [Clock] by the specified [Duration] for testing purposes. */ @Synchronized fun advanceBy(duration: Duration) { delegateClock = offset(delegateClock, duration) @@ -33,28 +19,8 @@ class TestClock(private var delegateClock: Clock = Clock.systemUTC()) : MutableC /** * Move this [Clock] to the specified [Instant] for testing purposes. - * * This will only be approximate due to the time ticking away, but will be some time shortly after the requested [Instant]. */ @Synchronized fun setTo(newInstant: Instant) = advanceBy(instant() until newInstant) - - @Synchronized override fun instant(): Instant { - return delegateClock.instant() - } - - @Deprecated("Do not use this. Instead seek to use ZonedDateTime methods.", level = DeprecationLevel.ERROR) - override fun withZone(zone: ZoneId): Clock { - throw UnsupportedOperationException("Tokenized clock does not support withZone()") - } - - @Synchronized override fun getZone(): ZoneId { - return delegateClock.zone - } } - -/** - * A helper method to set several [TestClock]s to approximately the same time. The clocks may drift by the time it - * takes for each [TestClock] to have it's time set and any observers to execute. - */ -fun Iterable.setTo(instant: Instant) = this.forEach { it.setTo(instant) } From 41e0be195891b565b4044030b3e1e37221846919 Mon Sep 17 00:00:00 2001 From: Chris Rankin Date: Mon, 6 Nov 2017 11:54:48 +0000 Subject: [PATCH 23/24] Exit code is between 0-255. So protect against VERY unlikey chance of overflow. (#2000) --- .ci/check-api-changes.sh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.ci/check-api-changes.sh b/.ci/check-api-changes.sh index 72ae5f9d80..987d0ced3c 100755 --- a/.ci/check-api-changes.sh +++ b/.ci/check-api-changes.sh @@ -52,6 +52,10 @@ if [ $abstractCount -gt 0 ]; then fi badChanges=$(($removalCount + $abstractCount)) +if [ $badChanges -gt 255 ]; then + echo "OVERFLOW! Number of bad API changes: $badChanges" + badChanges=255 +fi echo "Exiting with exit code" $badChanges exit $badChanges From 8b55f415bda73df54e838dd9f08a8e26df42aa4e Mon Sep 17 00:00:00 2001 From: Christian Sailer Date: Mon, 6 Nov 2017 17:37:30 +0000 Subject: [PATCH 24/24] Merge branch 'master' of https://github.com/corda/corda into christians_os_merge_20171106 --- gradle/wrapper/gradle-wrapper.properties | 3 +- .../net/corda/node/internal/EnterpriseNode.kt | 1 - .../contracts/asset/CashSelectionH2Test.kt | 41 ++++++++-------- .../flows/CashExitFlowTests.kt | 8 ++-- .../flows/CashIssueFlowTests.kt | 9 ++-- .../flows/CashPaymentFlowTests.kt | 18 ++++--- .../flows/TwoPartyTradeFlowTest.kt | 48 +++++++++++-------- .../net/corda/vega/SimmValuationTest.kt | 1 - settings.gradle | 2 +- .../KryoVerifierSerializationScheme.kt | 4 +- 10 files changed, 65 insertions(+), 70 deletions(-) diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 92165eede8..b05efff0b2 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,6 @@ +#Mon Nov 06 15:05:49 GMT 2017 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.3-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-4.3-all.zip diff --git a/node/src/main/kotlin/net/corda/node/internal/EnterpriseNode.kt b/node/src/main/kotlin/net/corda/node/internal/EnterpriseNode.kt index 00f376726e..685a57afc2 100644 --- a/node/src/main/kotlin/net/corda/node/internal/EnterpriseNode.kt +++ b/node/src/main/kotlin/net/corda/node/internal/EnterpriseNode.kt @@ -7,7 +7,6 @@ import net.corda.core.utilities.loggerFor import net.corda.node.VersionInfo import net.corda.node.services.config.NodeConfiguration import net.corda.node.services.config.RelayConfiguration -import net.corda.nodeapi.internal.ServiceInfo import org.fusesource.jansi.Ansi import org.fusesource.jansi.AnsiConsole import java.io.IOException diff --git a/perftestcordapp/src/test/kotlin/com/r3/corda/enterprise/perftestcordapp/contracts/asset/CashSelectionH2Test.kt b/perftestcordapp/src/test/kotlin/com/r3/corda/enterprise/perftestcordapp/contracts/asset/CashSelectionH2Test.kt index 5aa10b3179..36b767c219 100644 --- a/perftestcordapp/src/test/kotlin/com/r3/corda/enterprise/perftestcordapp/contracts/asset/CashSelectionH2Test.kt +++ b/perftestcordapp/src/test/kotlin/com/r3/corda/enterprise/perftestcordapp/contracts/asset/CashSelectionH2Test.kt @@ -4,38 +4,35 @@ import net.corda.core.utilities.getOrThrow import com.r3.corda.enterprise.perftestcordapp.DOLLARS import com.r3.corda.enterprise.perftestcordapp.flows.CashException import com.r3.corda.enterprise.perftestcordapp.flows.CashPaymentFlow -import net.corda.testing.chooseIdentity import net.corda.testing.node.MockNetwork import net.corda.testing.node.MockNodeParameters import org.assertj.core.api.Assertions.assertThatThrownBy +import org.junit.After import org.junit.Test - class CashSelectionH2Test { + private val mockNet = MockNetwork(threadPerNode = true, cordappPackages = listOf("net.corda.finance")) + + @After + fun cleanUp() { + mockNet.stopNodes() + } @Test fun `check does not hold connection over retries`() { - val mockNet = MockNetwork(threadPerNode = true) - try { - val notaryNode = mockNet.createNotaryNode() - val bankA = mockNet.createNode(MockNodeParameters(configOverrides = { existingConfig -> - // Tweak connections to be minimal to make this easier (1 results in a hung node during start up, so use 2 connections). - existingConfig.dataSourceProperties.setProperty("maximumPoolSize", "2") - existingConfig - })) + val bankA = mockNet.createNode(MockNodeParameters(configOverrides = { + // Tweak connections to be minimal to make this easier (1 results in a hung node during start up, so use 2 connections). + it.dataSourceProperties.setProperty("maximumPoolSize", "2") + })) + val notary = mockNet.defaultNotaryIdentity - mockNet.startNodes() + // Start more cash spends than we have connections. If spend leaks a connection on retry, we will run out of connections. + val flow1 = bankA.services.startFlow(CashPaymentFlow(amount = 100.DOLLARS, anonymous = false, recipient = notary)) + val flow2 = bankA.services.startFlow(CashPaymentFlow(amount = 100.DOLLARS, anonymous = false, recipient = notary)) + val flow3 = bankA.services.startFlow(CashPaymentFlow(amount = 100.DOLLARS, anonymous = false, recipient = notary)) - // Start more cash spends than we have connections. If spend leaks a connection on retry, we will run out of connections. - val flow1 = bankA.services.startFlow(CashPaymentFlow(amount = 100.DOLLARS, anonymous = false, recipient = notaryNode.info.chooseIdentity())) - val flow2 = bankA.services.startFlow(CashPaymentFlow(amount = 100.DOLLARS, anonymous = false, recipient = notaryNode.info.chooseIdentity())) - val flow3 = bankA.services.startFlow(CashPaymentFlow(amount = 100.DOLLARS, anonymous = false, recipient = notaryNode.info.chooseIdentity())) - - assertThatThrownBy { flow1.resultFuture.getOrThrow() }.isInstanceOf(CashException::class.java) - assertThatThrownBy { flow2.resultFuture.getOrThrow() }.isInstanceOf(CashException::class.java) - assertThatThrownBy { flow3.resultFuture.getOrThrow() }.isInstanceOf(CashException::class.java) - } finally { - mockNet.stopNodes() - } + assertThatThrownBy { flow1.resultFuture.getOrThrow() }.isInstanceOf(CashException::class.java) + assertThatThrownBy { flow2.resultFuture.getOrThrow() }.isInstanceOf(CashException::class.java) + assertThatThrownBy { flow3.resultFuture.getOrThrow() }.isInstanceOf(CashException::class.java) } } \ No newline at end of file diff --git a/perftestcordapp/src/test/kotlin/com/r3/corda/enterprise/perftestcordapp/flows/CashExitFlowTests.kt b/perftestcordapp/src/test/kotlin/com/r3/corda/enterprise/perftestcordapp/flows/CashExitFlowTests.kt index fc827ac9c6..eb469fc55d 100644 --- a/perftestcordapp/src/test/kotlin/com/r3/corda/enterprise/perftestcordapp/flows/CashExitFlowTests.kt +++ b/perftestcordapp/src/test/kotlin/com/r3/corda/enterprise/perftestcordapp/flows/CashExitFlowTests.kt @@ -20,20 +20,18 @@ import kotlin.test.assertEquals import kotlin.test.assertFailsWith class CashExitFlowTests { - private lateinit var mockNet : MockNetwork + private lateinit var mockNet: MockNetwork private val initialBalance = 2000.DOLLARS private val ref = OpaqueBytes.of(0x01) private lateinit var bankOfCordaNode: StartedNode private lateinit var bankOfCorda: Party - private lateinit var notaryNode: StartedNode private lateinit var notary: Party @Before fun start() { - mockNet = MockNetwork(servicePeerAllocationStrategy = RoundRobin(), cordappPackages = listOf("com.r3.corda.enterprise.perftestcordapp.contracts.asset")) - notaryNode = mockNet.createNotaryNode() + mockNet = MockNetwork(servicePeerAllocationStrategy = RoundRobin(), + cordappPackages = listOf("com.r3.corda.enterprise.perftestcordapp.contracts.asset")) bankOfCordaNode = mockNet.createPartyNode(BOC.name) - notary = notaryNode.services.getDefaultNotary() bankOfCorda = bankOfCordaNode.info.chooseIdentity() mockNet.runNetwork() diff --git a/perftestcordapp/src/test/kotlin/com/r3/corda/enterprise/perftestcordapp/flows/CashIssueFlowTests.kt b/perftestcordapp/src/test/kotlin/com/r3/corda/enterprise/perftestcordapp/flows/CashIssueFlowTests.kt index 9df67f5f29..89287be7a9 100644 --- a/perftestcordapp/src/test/kotlin/com/r3/corda/enterprise/perftestcordapp/flows/CashIssueFlowTests.kt +++ b/perftestcordapp/src/test/kotlin/com/r3/corda/enterprise/perftestcordapp/flows/CashIssueFlowTests.kt @@ -7,9 +7,8 @@ import com.r3.corda.enterprise.perftestcordapp.DOLLARS import com.r3.corda.enterprise.perftestcordapp.`issued by` import com.r3.corda.enterprise.perftestcordapp.contracts.asset.Cash import net.corda.node.internal.StartedNode -import net.corda.testing.chooseIdentity -import net.corda.testing.getDefaultNotary import net.corda.testing.BOC +import net.corda.testing.chooseIdentity import net.corda.testing.node.InMemoryMessagingNetwork.ServicePeerAllocationStrategy.RoundRobin import net.corda.testing.node.MockNetwork import net.corda.testing.node.MockNetwork.MockNode @@ -20,19 +19,17 @@ import kotlin.test.assertEquals import kotlin.test.assertFailsWith class CashIssueFlowTests { - private lateinit var mockNet : MockNetwork + private lateinit var mockNet: MockNetwork private lateinit var bankOfCordaNode: StartedNode private lateinit var bankOfCorda: Party - private lateinit var notaryNode: StartedNode private lateinit var notary: Party @Before fun start() { mockNet = MockNetwork(servicePeerAllocationStrategy = RoundRobin(), cordappPackages = listOf("com.r3.corda.enterprise.perftestcordapp.contracts.asset")) - notaryNode = mockNet.createNotaryNode() bankOfCordaNode = mockNet.createPartyNode(BOC.name) bankOfCorda = bankOfCordaNode.info.chooseIdentity() - notary = notaryNode.services.getDefaultNotary() + notary = mockNet.defaultNotaryIdentity mockNet.runNetwork() } diff --git a/perftestcordapp/src/test/kotlin/com/r3/corda/enterprise/perftestcordapp/flows/CashPaymentFlowTests.kt b/perftestcordapp/src/test/kotlin/com/r3/corda/enterprise/perftestcordapp/flows/CashPaymentFlowTests.kt index 58aba54e43..a43515da84 100644 --- a/perftestcordapp/src/test/kotlin/com/r3/corda/enterprise/perftestcordapp/flows/CashPaymentFlowTests.kt +++ b/perftestcordapp/src/test/kotlin/com/r3/corda/enterprise/perftestcordapp/flows/CashPaymentFlowTests.kt @@ -21,23 +21,21 @@ import kotlin.test.assertEquals import kotlin.test.assertFailsWith class CashPaymentFlowTests { - private lateinit var mockNet : MockNetwork + private lateinit var mockNet: MockNetwork private val initialBalance = 2000.DOLLARS private val ref = OpaqueBytes.of(0x01) private lateinit var bankOfCordaNode: StartedNode private lateinit var bankOfCorda: Party - private lateinit var notaryNode: StartedNode - private lateinit var notary: Party + private lateinit var aliceNode: StartedNode @Before fun start() { mockNet = MockNetwork(servicePeerAllocationStrategy = RoundRobin(), cordappPackages = listOf("com.r3.corda.enterprise.perftestcordapp.contracts.asset")) - notaryNode = mockNet.createNotaryNode() bankOfCordaNode = mockNet.createPartyNode(BOC.name) + aliceNode = mockNet.createPartyNode(ALICE.name) bankOfCorda = bankOfCordaNode.info.chooseIdentity() - notary = notaryNode.services.getDefaultNotary() - val future = bankOfCordaNode.services.startFlow(CashIssueFlow(initialBalance, ref, notary)).resultFuture mockNet.runNetwork() + val future = bankOfCordaNode.services.startFlow(CashIssueFlow(initialBalance, ref, mockNet.defaultNotaryIdentity)).resultFuture future.getOrThrow() } @@ -48,7 +46,7 @@ class CashPaymentFlowTests { @Test fun `pay some cash`() { - val payTo = notaryNode.info.chooseIdentity() + val payTo = aliceNode.info.chooseIdentity() val expectedPayment = 500.DOLLARS val expectedChange = 1500.DOLLARS @@ -56,7 +54,7 @@ class CashPaymentFlowTests { // Register for vault updates val criteria = QueryCriteria.VaultQueryCriteria(status = Vault.StateStatus.ALL) val (_, vaultUpdatesBoc) = bankOfCordaNode.services.vaultService.trackBy(criteria) - val (_, vaultUpdatesBankClient) = notaryNode.services.vaultService.trackBy(criteria) + val (_, vaultUpdatesBankClient) = aliceNode.services.vaultService.trackBy(criteria) val future = bankOfCordaNode.services.startFlow(CashPaymentFlow(expectedPayment, payTo)).resultFuture @@ -88,7 +86,7 @@ class CashPaymentFlowTests { @Test fun `pay more than we have`() { - val payTo = notaryNode.info.chooseIdentity() + val payTo = aliceNode.info.chooseIdentity() val expected = 4000.DOLLARS val future = bankOfCordaNode.services.startFlow(CashPaymentFlow(expected, payTo)).resultFuture @@ -100,7 +98,7 @@ class CashPaymentFlowTests { @Test fun `pay zero cash`() { - val payTo = notaryNode.info.chooseIdentity() + val payTo = aliceNode.info.chooseIdentity() val expected = 0.DOLLARS val future = bankOfCordaNode.services.startFlow(CashPaymentFlow(expected, payTo)).resultFuture diff --git a/perftestcordapp/src/test/kotlin/com/r3/corda/enterprise/perftestcordapp/flows/TwoPartyTradeFlowTest.kt b/perftestcordapp/src/test/kotlin/com/r3/corda/enterprise/perftestcordapp/flows/TwoPartyTradeFlowTest.kt index 23653ad786..c5a863d023 100644 --- a/perftestcordapp/src/test/kotlin/com/r3/corda/enterprise/perftestcordapp/flows/TwoPartyTradeFlowTest.kt +++ b/perftestcordapp/src/test/kotlin/com/r3/corda/enterprise/perftestcordapp/flows/TwoPartyTradeFlowTest.kt @@ -82,7 +82,7 @@ internal fun CheckpointStorage.checkpoints(): List { * We assume that Alice and Bob already found each other via some market, and have agreed the details already. */ @RunWith(Parameterized::class) -class TwoPartyTradeFlowTests(val anonymous: Boolean) { +class TwoPartyTradeFlowTests(private val anonymous: Boolean) { companion object { private val cordappPackages = listOf("com.r3.corda.enterprise.perftestcordapp.contracts") @JvmStatic @@ -112,7 +112,7 @@ class TwoPartyTradeFlowTests(val anonymous: Boolean) { // allow interruption half way through. mockNet = MockNetwork(threadPerNode = true, cordappPackages = cordappPackages) ledger(MockServices(cordappPackages), initialiseSerialization = false) { - val notaryNode = mockNet.createNotaryNode() + val notaryNode = mockNet.defaultNotaryNode val aliceNode = mockNet.createPartyNode(ALICE_NAME) val bobNode = mockNet.createPartyNode(BOB_NAME) val bankNode = mockNet.createPartyNode(BOC_NAME) @@ -162,7 +162,7 @@ class TwoPartyTradeFlowTests(val anonymous: Boolean) { fun `trade cash for commercial paper fails using soft locking`() { mockNet = MockNetwork(threadPerNode = true, cordappPackages = cordappPackages) ledger(MockServices(cordappPackages), initialiseSerialization = false) { - val notaryNode = mockNet.createNotaryNode() + val notaryNode = mockNet.defaultNotaryNode val aliceNode = mockNet.createPartyNode(ALICE_NAME) val bobNode = mockNet.createPartyNode(BOB_NAME) val bankNode = mockNet.createPartyNode(BOC_NAME) @@ -218,7 +218,7 @@ class TwoPartyTradeFlowTests(val anonymous: Boolean) { fun `shutdown and restore`() { mockNet = MockNetwork(cordappPackages = cordappPackages) ledger(MockServices(cordappPackages), initialiseSerialization = false) { - val notaryNode = mockNet.createNotaryNode() + val notaryNode = mockNet.defaultNotaryNode val aliceNode = mockNet.createPartyNode(ALICE_NAME) var bobNode = mockNet.createPartyNode(BOB_NAME) val bankNode = mockNet.createPartyNode(BOC_NAME) @@ -311,15 +311,14 @@ class TwoPartyTradeFlowTests(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 = object : MockNetwork.Factory { - override fun create(args: MockNodeArgs): MockNetwork.MockNode { - return object : MockNetwork.MockNode(args) { - // That constructs a recording tx storage - override fun makeTransactionStorage(): WritableTransactionStorage { - return RecordingTransactionStorage(database, super.makeTransactionStorage()) - } + return mockNet.createNode(MockNodeParameters(legalName = name), nodeFactory = { args -> + object : MockNetwork.MockNode(args) { + // That constructs a recording tx storage + override fun makeTransactionStorage(): WritableTransactionStorage { + return RecordingTransactionStorage(database, super.makeTransactionStorage()) } } }) @@ -328,7 +327,7 @@ class TwoPartyTradeFlowTests(val anonymous: Boolean) { @Test fun `check dependencies of sale asset are resolved`() { mockNet = MockNetwork(cordappPackages = cordappPackages) - val notaryNode = mockNet.createNotaryNode() + val notaryNode = mockNet.defaultNotaryNode val aliceNode = makeNodeWithTracking(ALICE_NAME) val bobNode = makeNodeWithTracking(BOB_NAME) val bankNode = makeNodeWithTracking(BOC_NAME) @@ -434,7 +433,7 @@ class TwoPartyTradeFlowTests(val anonymous: Boolean) { @Test fun `track works`() { mockNet = MockNetwork(cordappPackages = cordappPackages) - val notaryNode = mockNet.createNotaryNode() + val notaryNode = mockNet.defaultNotaryNode val aliceNode = makeNodeWithTracking(ALICE_NAME) val bobNode = makeNodeWithTracking(BOB_NAME) val bankNode = makeNodeWithTracking(BOC_NAME) @@ -589,7 +588,7 @@ class TwoPartyTradeFlowTests(val anonymous: Boolean) { aliceError: Boolean, expectedMessageSubstring: String ) { - val notaryNode = mockNet.createNotaryNode() + val notaryNode = mockNet.defaultNotaryNode val aliceNode = mockNet.createPartyNode(ALICE_NAME) val bobNode = mockNet.createPartyNode(BOB_NAME) val bankNode = mockNet.createPartyNode(BOC_NAME) @@ -636,18 +635,25 @@ class TwoPartyTradeFlowTests(val anonymous: Boolean) { notaryNode: StartedNode<*>, vararg extraSigningNodes: StartedNode<*>): Map { + val notaryParty = notaryNode.info.legalIdentities[0] val signed = wtxToSign.map { val id = it.id val sigs = mutableListOf() val nodeKey = node.info.chooseIdentity().owningKey - sigs.add(node.services.keyManagementService.sign(SignableData(id, SignatureMetadata(1, Crypto.findSignatureScheme(nodeKey).schemeNumberID)), nodeKey)) - sigs.add(notaryNode.services.keyManagementService.sign(SignableData(id, SignatureMetadata(1, - Crypto.findSignatureScheme(notaryNode.info.legalIdentities[1].owningKey).schemeNumberID)), notaryNode.info.legalIdentities[1].owningKey)) + sigs += node.services.keyManagementService.sign( + SignableData(id, SignatureMetadata(1, Crypto.findSignatureScheme(nodeKey).schemeNumberID)), + nodeKey + ) + sigs += notaryNode.services.keyManagementService.sign( + SignableData(id, SignatureMetadata(1, Crypto.findSignatureScheme(notaryParty.owningKey).schemeNumberID)), + notaryParty.owningKey + ) extraSigningNodes.forEach { currentNode -> - sigs.add(currentNode.services.keyManagementService.sign( - SignableData(id, SignatureMetadata(1, Crypto.findSignatureScheme(currentNode.info.chooseIdentity().owningKey).schemeNumberID)), + sigs += currentNode.services.keyManagementService.sign( + SignableData(id, SignatureMetadata( + 1, + Crypto.findSignatureScheme(currentNode.info.chooseIdentity().owningKey).schemeNumberID)), currentNode.info.chooseIdentity().owningKey) - ) } SignedTransaction(it, sigs) } diff --git a/samples/simm-valuation-demo/src/integration-test/kotlin/net/corda/vega/SimmValuationTest.kt b/samples/simm-valuation-demo/src/integration-test/kotlin/net/corda/vega/SimmValuationTest.kt index 6c9c6cff74..d09d8f5a8c 100644 --- a/samples/simm-valuation-demo/src/integration-test/kotlin/net/corda/vega/SimmValuationTest.kt +++ b/samples/simm-valuation-demo/src/integration-test/kotlin/net/corda/vega/SimmValuationTest.kt @@ -5,7 +5,6 @@ import net.corda.core.identity.CordaX500Name import net.corda.core.utilities.getOrThrow import net.corda.testing.DUMMY_BANK_A import net.corda.testing.DUMMY_BANK_B -import net.corda.testing.IntegrationTestCategory import net.corda.testing.driver.driver import net.corda.testing.http.HttpApi import net.corda.vega.api.PortfolioApi diff --git a/settings.gradle b/settings.gradle index 2ef7265d8b..7ce3241252 100644 --- a/settings.gradle +++ b/settings.gradle @@ -19,7 +19,7 @@ include 'experimental' include 'experimental:sandbox' include 'experimental:quasar-hook' include 'experimental:kryo-hook' -include 'experimental:intellij-plugin' +//include 'experimental:intellij-plugin' include 'verifier' include 'test-common' include 'test-utils' diff --git a/verify-enclave/src/main/kotlin/com/r3/enclaves/txverify/KryoVerifierSerializationScheme.kt b/verify-enclave/src/main/kotlin/com/r3/enclaves/txverify/KryoVerifierSerializationScheme.kt index b81352281d..455be7ae40 100644 --- a/verify-enclave/src/main/kotlin/com/r3/enclaves/txverify/KryoVerifierSerializationScheme.kt +++ b/verify-enclave/src/main/kotlin/com/r3/enclaves/txverify/KryoVerifierSerializationScheme.kt @@ -3,10 +3,10 @@ package com.r3.enclaves.txverify import net.corda.core.serialization.SerializationContext import net.corda.core.serialization.SerializationDefaults import net.corda.core.utilities.ByteSequence -import net.corda.nodeapi.internal.serialization.AbstractKryoSerializationScheme import net.corda.nodeapi.internal.serialization.KRYO_P2P_CONTEXT -import net.corda.nodeapi.internal.serialization.KryoHeaderV0_1 import net.corda.nodeapi.internal.serialization.SerializationFactoryImpl +import net.corda.nodeapi.internal.serialization.kryo.AbstractKryoSerializationScheme +import net.corda.nodeapi.internal.serialization.kryo.KryoHeaderV0_1 @Suppress("UNUSED") private class KryoVerifierSerializationScheme : AbstractKryoSerializationScheme() {