Merge pull request #2017 from corda/kat/feature/enableAMQP

CORDA-780 - Enable AMQP for P2P and Storage Contexts
This commit is contained in:
Katelyn Baker 2017-12-11 11:37:16 +00:00 committed by GitHub
commit 4b5c60637c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
32 changed files with 567 additions and 54 deletions

View File

@ -2724,6 +2724,10 @@ public static final class net.corda.core.serialization.SerializationContext$UseC
public static net.corda.core.serialization.SerializationContext$UseCase valueOf(String) public static net.corda.core.serialization.SerializationContext$UseCase valueOf(String)
public static net.corda.core.serialization.SerializationContext$UseCase[] values() public static net.corda.core.serialization.SerializationContext$UseCase[] values()
## ##
public interface net.corda.core.serialization.SerializationCustomSerializer
public abstract Object fromProxy(Object)
public abstract Object toProxy(Object)
##
public final class net.corda.core.serialization.SerializationDefaults extends java.lang.Object public final class net.corda.core.serialization.SerializationDefaults extends java.lang.Object
@org.jetbrains.annotations.NotNull public final net.corda.core.serialization.SerializationContext getCHECKPOINT_CONTEXT() @org.jetbrains.annotations.NotNull public final net.corda.core.serialization.SerializationContext getCHECKPOINT_CONTEXT()
@org.jetbrains.annotations.NotNull public final net.corda.core.serialization.SerializationContext getP2P_CONTEXT() @org.jetbrains.annotations.NotNull public final net.corda.core.serialization.SerializationContext getP2P_CONTEXT()

View File

@ -41,7 +41,7 @@ class KryoClientSerializationScheme : AbstractKryoSerializationScheme() {
return SerializationEnvironmentImpl( return SerializationEnvironmentImpl(
SerializationFactoryImpl().apply { SerializationFactoryImpl().apply {
registerScheme(KryoClientSerializationScheme()) registerScheme(KryoClientSerializationScheme())
registerScheme(AMQPClientSerializationScheme()) registerScheme(AMQPClientSerializationScheme(emptyList()))
}, },
KRYO_P2P_CONTEXT, KRYO_P2P_CONTEXT,
rpcClientContext = KRYO_RPC_CLIENT_CONTEXT) rpcClientContext = KRYO_RPC_CLIENT_CONTEXT)

View File

@ -3,6 +3,7 @@ package net.corda.core.cordapp
import net.corda.core.DoNotImplement import net.corda.core.DoNotImplement
import net.corda.core.flows.FlowLogic import net.corda.core.flows.FlowLogic
import net.corda.core.schemas.MappedSchema import net.corda.core.schemas.MappedSchema
import net.corda.core.serialization.SerializationCustomSerializer
import net.corda.core.serialization.SerializationWhitelist import net.corda.core.serialization.SerializationWhitelist
import net.corda.core.serialization.SerializeAsToken import net.corda.core.serialization.SerializeAsToken
import java.net.URL import java.net.URL
@ -22,6 +23,7 @@ import java.net.URL
* @property schedulableFlows List of flows startable by the scheduler * @property schedulableFlows List of flows startable by the scheduler
* @property services List of RPC services * @property services List of RPC services
* @property serializationWhitelists List of Corda plugin registries * @property serializationWhitelists List of Corda plugin registries
* @property serializationCustomSerializers List of serializers
* @property customSchemas List of custom schemas * @property customSchemas List of custom schemas
* @property jarPath The path to the JAR for this CorDapp * @property jarPath The path to the JAR for this CorDapp
*/ */
@ -35,6 +37,7 @@ interface Cordapp {
val schedulableFlows: List<Class<out FlowLogic<*>>> val schedulableFlows: List<Class<out FlowLogic<*>>>
val services: List<Class<out SerializeAsToken>> val services: List<Class<out SerializeAsToken>>
val serializationWhitelists: List<SerializationWhitelist> val serializationWhitelists: List<SerializationWhitelist>
val serializationCustomSerializers: List<SerializationCustomSerializer<*, *>>
val customSchemas: Set<MappedSchema> val customSchemas: Set<MappedSchema>
val jarPath: URL val jarPath: URL
val cordappClasses: List<String> val cordappClasses: List<String>

View File

@ -3,6 +3,7 @@ package net.corda.core.internal.cordapp
import net.corda.core.cordapp.Cordapp import net.corda.core.cordapp.Cordapp
import net.corda.core.flows.FlowLogic import net.corda.core.flows.FlowLogic
import net.corda.core.schemas.MappedSchema import net.corda.core.schemas.MappedSchema
import net.corda.core.serialization.SerializationCustomSerializer
import net.corda.core.serialization.SerializationWhitelist import net.corda.core.serialization.SerializationWhitelist
import net.corda.core.serialization.SerializeAsToken import net.corda.core.serialization.SerializeAsToken
import java.io.File import java.io.File
@ -16,6 +17,7 @@ data class CordappImpl(
override val schedulableFlows: List<Class<out FlowLogic<*>>>, override val schedulableFlows: List<Class<out FlowLogic<*>>>,
override val services: List<Class<out SerializeAsToken>>, override val services: List<Class<out SerializeAsToken>>,
override val serializationWhitelists: List<SerializationWhitelist>, override val serializationWhitelists: List<SerializationWhitelist>,
override val serializationCustomSerializers: List<SerializationCustomSerializer<*, *>>,
override val customSchemas: Set<MappedSchema>, override val customSchemas: Set<MappedSchema>,
override val jarPath: URL) : Cordapp { override val jarPath: URL) : Cordapp {
override val name: String = File(jarPath.toURI()).name.removeSuffix(".jar") override val name: String = File(jarPath.toURI()).name.removeSuffix(".jar")

View File

@ -0,0 +1,24 @@
package net.corda.core.serialization
/**
* Allows CorDapps to provide custom serializers for third party libraries where those libraries cannot
* be recompiled with the -parameters flag rendering their classes natively serializable by Corda. In this case
* a proxy serializer can be written that extends this type whose purpose is to move between those an
* unserializable types and an intermediate representation.
*
* NOTE: The proxy object should be specified as a seperate class. However, this can be defined within the
* scope of the custom serializer.
*/
interface SerializationCustomSerializer<OBJ, PROXY> {
/**
* Should facilitate the conversion of the third party object into the serializable
* local class specified by [PROXY]
*/
fun toProxy(obj: OBJ) : PROXY
/**
* Should facilitate the conversion of the proxy object into a new instance of the
* unserializable type
*/
fun fromProxy(proxy: PROXY) : OBJ
}

View File

@ -0,0 +1,73 @@
Pluggable Serializers for CorDapps
==================================
.. contents::
To be serializable by Corda Java classes must be compiled with the -parameters switch to enable matching of its properties
to constructor parameters. This is important because Corda's internal AMQP serialization scheme will only construct
objects using their constructors. However, when recompilation isn't possible, or classes are built in such a way that
they cannot be easily modified for simple serialization, CorDapps can provide custom proxy serializers that Corda
can use to move from types it cannot serialize to an interim representation that it can with the transformation to and
from this proxy object being handled by the supplied serializer.
Serializer Location
-------------------
Custom serializer classes should follow the rules for including classes found in :doc:`cordapp-build-systems`
Writing a Custom Serializer
---------------------------
Serializers must
* Inherit from net.corda.core.serialization.SerializationCustomSerializer
* Provide a proxy class to transform the object to and from
* Implement the ``toProxy`` and ``fromProxy`` methods
Serializers inheriting from SerializationCustomSerializer have to implement two methods and two types.
Example
-------
Consider this example class
.. sourcecode:: java
public final class Example {
private final Int a
private final Int b
private Example(Int a, Int b) {
this.a = a;
this.b = b;
}
public static Example of (int[] a) { return Example(a[0], a[1]); }
public int getA() { return a; }
public int getB() { return b; }
}
Without a custom serializer we cannot serialize this class as there is no public constructor that facilitates the
initialisation of all of its properties.
To be serializable by Corda this would require a custom serializer as follows:
.. sourcecode:: kotlin
class ExampleSerializer : SerializationCustomSerializer<Example, ExampleSerializer.Proxy> {
data class Proxy(val a: Int, val b: Int)
override fun toProxy(obj: Example) = Proxy(obj.a, obj.b)
override fun fromProxy(proxy: Proxy) : Example {
val constructorArg = IntArray(2);
constructorArg[0] = proxy.a
constructorArg[1] = proxy.b
return Example.create(constructorArg)
}
}
Whitelisting
------------
By writing a custom serializer for a class it has the effect of adding that class to the whitelist, meaning such
classes don't need explicitly adding to the CorDapp's whitelist.

View File

@ -12,6 +12,20 @@ Unreleased
That is the ability to alter an enum constant and, as long as certain rules are followed and the correct That is the ability to alter an enum constant and, as long as certain rules are followed and the correct
annotations applied, have older and newer instances of that enumeration be understood. annotations applied, have older and newer instances of that enumeration be understood.
* **AMQP Enabled**
AMQP Serialization is now enabled for both peer to peer communication and writing states to the vault. This change
brings a stable format Corda can support internally throughout it's lifetime that meets the needs of Corda and our
users.
* **Custom Serializers**
To allow interop with third party libraries that cannot be recompiled we add functionality that allows custom serializers
to be written for those classes. If needed, a proxy object can be created as an interim step that allows Corda's internal
serializers to operate on those types.
A good example of this is the SIMM valuation demo which has a number of such serializers defined in the plugin/customserializers package
Release 2.0 Release 2.0
---------- ----------
Following quickly on the heels of the release of Corda 1.0, Corda version 2.0 consolidates Following quickly on the heels of the release of Corda 1.0, Corda version 2.0 consolidates

View File

@ -2,6 +2,7 @@
package net.corda.nodeapi.internal.serialization.amqp package net.corda.nodeapi.internal.serialization.amqp
import net.corda.core.cordapp.Cordapp
import net.corda.core.serialization.* import net.corda.core.serialization.*
import net.corda.core.utilities.ByteSequence import net.corda.core.utilities.ByteSequence
import net.corda.nodeapi.internal.serialization.DefaultWhitelist import net.corda.nodeapi.internal.serialization.DefaultWhitelist
@ -24,7 +25,8 @@ fun SerializerFactory.addToWhitelist(vararg types: Class<*>) {
} }
} }
abstract class AbstractAMQPSerializationScheme : SerializationScheme { abstract class AbstractAMQPSerializationScheme(val cordappLoader: List<Cordapp>) : SerializationScheme {
companion object { companion object {
private val serializationWhitelists: List<SerializationWhitelist> by lazy { private val serializationWhitelists: List<SerializationWhitelist> by lazy {
ServiceLoader.load(SerializationWhitelist::class.java, this::class.java.classLoader).toList() + DefaultWhitelist ServiceLoader.load(SerializationWhitelist::class.java, this::class.java.classLoader).toList() + DefaultWhitelist
@ -62,10 +64,17 @@ abstract class AbstractAMQPSerializationScheme : SerializationScheme {
register(net.corda.nodeapi.internal.serialization.amqp.custom.EnumSetSerializer(this)) register(net.corda.nodeapi.internal.serialization.amqp.custom.EnumSetSerializer(this))
register(net.corda.nodeapi.internal.serialization.amqp.custom.ContractAttachmentSerializer(this)) register(net.corda.nodeapi.internal.serialization.amqp.custom.ContractAttachmentSerializer(this))
} }
for (whitelistProvider in serializationWhitelists) for (whitelistProvider in serializationWhitelists) {
factory.addToWhitelist(*whitelistProvider.whitelist.toTypedArray()) factory.addToWhitelist(*whitelistProvider.whitelist.toTypedArray())
} }
for (loader in cordappLoader) {
for (schema in loader.serializationCustomSerializers) {
factory.registerExternal(CorDappCustomSerializer(schema, factory))
}
}
}
private val serializerFactoriesForContexts = ConcurrentHashMap<Pair<ClassWhitelist, ClassLoader>, SerializerFactory>() private val serializerFactoriesForContexts = ConcurrentHashMap<Pair<ClassWhitelist, ClassLoader>, SerializerFactory>()
protected abstract fun rpcClientSerializerFactory(context: SerializationContext): SerializerFactory protected abstract fun rpcClientSerializerFactory(context: SerializationContext): SerializerFactory
@ -97,11 +106,11 @@ abstract class AbstractAMQPSerializationScheme : SerializationScheme {
return SerializationOutput(serializerFactory).serialize(obj) return SerializationOutput(serializerFactory).serialize(obj)
} }
protected fun canDeserializeVersion(byteSequence: ByteSequence): Boolean = AMQP_ENABLED && byteSequence == AmqpHeaderV1_0 protected fun canDeserializeVersion(byteSequence: ByteSequence): Boolean = byteSequence == AmqpHeaderV1_0
} }
// TODO: This will eventually cover server RPC as well and move to node module, but for now this is not implemented // TODO: This will eventually cover server RPC as well and move to node module, but for now this is not implemented
class AMQPServerSerializationScheme : AbstractAMQPSerializationScheme() { class AMQPServerSerializationScheme(cordapps: List<Cordapp> = emptyList()) : AbstractAMQPSerializationScheme(cordapps) {
override fun rpcClientSerializerFactory(context: SerializationContext): SerializerFactory { override fun rpcClientSerializerFactory(context: SerializationContext): SerializerFactory {
throw UnsupportedOperationException() throw UnsupportedOperationException()
} }
@ -118,7 +127,7 @@ class AMQPServerSerializationScheme : AbstractAMQPSerializationScheme() {
} }
// TODO: This will eventually cover client RPC as well and move to client module, but for now this is not implemented // TODO: This will eventually cover client RPC as well and move to client module, but for now this is not implemented
class AMQPClientSerializationScheme : AbstractAMQPSerializationScheme() { class AMQPClientSerializationScheme(cordapps: List<Cordapp> = emptyList()) : AbstractAMQPSerializationScheme(cordapps) {
override fun rpcClientSerializerFactory(context: SerializationContext): SerializerFactory { override fun rpcClientSerializerFactory(context: SerializationContext): SerializerFactory {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates. TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
} }

View File

@ -0,0 +1,86 @@
package net.corda.nodeapi.internal.serialization.amqp
import net.corda.core.internal.uncheckedCast
import net.corda.core.serialization.SerializationCustomSerializer
import net.corda.nodeapi.internal.serialization.amqp.SerializerFactory.Companion.nameForType
import org.apache.qpid.proton.amqp.Symbol
import org.apache.qpid.proton.codec.Data
import java.io.NotSerializableException
import java.lang.reflect.Type
import kotlin.reflect.jvm.javaType
import kotlin.reflect.jvm.jvmErasure
/**
* Index into the types list of the parent type of the serializer object, should be the
* type that this object proxies for
*/
const val CORDAPP_TYPE = 0
/**
* Index into the types list of the parent type of the serializer object, should be the
* type of the proxy object that we're using to represent the object we're proxying for
*/
const val PROXY_TYPE = 1
/**
* Wrapper class for user provided serializers
*
* Through the CorDapp JAR scanner we will have a list of custom serializer types that implement
* the toProxy and fromProxy methods. This class takes an instance of one of those objects and
* embeds it within a serialization context associated with a serializer factory by creating
* and instance of this class and registering that with a [SerializerFactory]
*
* Proxy serializers should transform an unserializable class into a representation that we can serialize
*
* @property serializer in instance of a user written serialization proxy, normally scanned and loaded
* automatically
* @property type the Java [Type] of the class which this serializes, inferred via reflection of the
* [serializer]'s super type
* @property proxyType the Java [Type] of the class into which instances of [type] are proxied for use by
* the underlying serialization engine
*
* @param factory a [SerializerFactory] belonging to the context this serializer is being instantiated
* for
*/
class CorDappCustomSerializer(
private val serializer: SerializationCustomSerializer<*, *>,
factory: SerializerFactory) : AMQPSerializer<Any>, SerializerFor {
override val revealSubclassesInSchema: Boolean get() = false
private val types = serializer::class.supertypes.filter { it.jvmErasure == SerializationCustomSerializer::class }
.flatMap { it.arguments }
.map { it.type!!.javaType }
init {
if (types.size != 2) {
throw NotSerializableException("Unable to determine serializer parent types")
}
}
override val type = types[CORDAPP_TYPE]
val proxyType = types[PROXY_TYPE]
override val typeDescriptor = Symbol.valueOf("$DESCRIPTOR_DOMAIN:${nameForType(type)}")
val descriptor: Descriptor = Descriptor(typeDescriptor)
private val proxySerializer: ObjectSerializer by lazy { ObjectSerializer(proxyType, factory) }
override fun writeClassInfo(output: SerializationOutput) {}
override fun writeObject(obj: Any, data: Data, type: Type, output: SerializationOutput) {
val proxy = uncheckedCast<SerializationCustomSerializer<*, *>,
SerializationCustomSerializer<Any?, Any?>>(serializer).toProxy(obj)
data.withDescribed(descriptor) {
data.withList {
for (property in proxySerializer.propertySerializers) {
property.writeProperty(proxy, this, output)
}
}
}
}
override fun readObject(obj: Any, schemas: SerializationSchemas, input: DeserializationInput) =
uncheckedCast<SerializationCustomSerializer<*, *>, SerializationCustomSerializer<Any?, Any?>>(
serializer).fromProxy(uncheckedCast(proxySerializer.readObject(obj, schemas, input)))!!
override fun isSerializerFor(clazz: Class<*>) = clazz == type
}

View File

@ -6,22 +6,27 @@ import org.apache.qpid.proton.amqp.Symbol
import org.apache.qpid.proton.codec.Data import org.apache.qpid.proton.codec.Data
import java.lang.reflect.Type import java.lang.reflect.Type
interface SerializerFor {
/**
* This method should return true if the custom serializer can serialize an instance of the class passed as the
* parameter.
*/
fun isSerializerFor(clazz: Class<*>): Boolean
val revealSubclassesInSchema: Boolean
}
/** /**
* Base class for serializers of core platform types that do not conform to the usual serialization rules and thus * Base class for serializers of core platform types that do not conform to the usual serialization rules and thus
* cannot be automatically serialized. * cannot be automatically serialized.
*/ */
abstract class CustomSerializer<T : Any> : AMQPSerializer<T> { abstract class CustomSerializer<T : Any> : AMQPSerializer<T>, SerializerFor {
/** /**
* This is a collection of custom serializers that this custom serializer depends on. e.g. for proxy objects * This is a collection of custom serializers that this custom serializer depends on. e.g. for proxy objects
* that refer to other custom types etc. * that refer to other custom types etc.
*/ */
open val additionalSerializers: Iterable<CustomSerializer<out Any>> = emptyList() open val additionalSerializers: Iterable<CustomSerializer<out Any>> = emptyList()
/**
* This method should return true if the custom serializer can serialize an instance of the class passed as the
* parameter.
*/
abstract fun isSerializerFor(clazz: Class<*>): Boolean
protected abstract val descriptor: Descriptor protected abstract val descriptor: Descriptor
/** /**
@ -33,7 +38,7 @@ abstract class CustomSerializer<T : Any> : AMQPSerializer<T> {
/** /**
* Whether subclasses using this serializer via inheritance should have a mapping in the schema. * Whether subclasses using this serializer via inheritance should have a mapping in the schema.
*/ */
open val revealSubclassesInSchema: Boolean = false override val revealSubclassesInSchema: Boolean get() = false
override fun writeObject(obj: Any, data: Data, type: Type, output: SerializationOutput) { override fun writeObject(obj: Any, data: Data, type: Type, output: SerializationOutput) {
data.withDescribed(descriptor) { data.withDescribed(descriptor) {

View File

@ -82,11 +82,13 @@ private fun <T : Any> propertiesForSerializationFromConstructor(kotlinConstructo
for (param in kotlinConstructor.parameters) { for (param in kotlinConstructor.parameters) {
val name = param.name ?: throw NotSerializableException("Constructor parameter of $clazz has no name.") val name = param.name ?: throw NotSerializableException("Constructor parameter of $clazz has no name.")
val matchingProperty = properties[name] ?: val matchingProperty = properties[name] ?:
throw NotSerializableException("No property matching constructor parameter named '$name' of '$clazz'." + throw NotSerializableException("No property matching constructor parameter named '$name' of '$clazz'. " +
" If using Java, check that you have the -parameters option specified in the Java compiler.") "If using Java, check that you have the -parameters option specified in the Java compiler. " +
"Alternately, provide a proxy serializer (SerializationCustomSerializer) if recompiling isn't an option")
// Check that the method has a getter in java. // Check that the method has a getter in java.
val getter = matchingProperty.readMethod ?: throw NotSerializableException("Property has no getter method for $name of $clazz." + val getter = matchingProperty.readMethod ?: throw NotSerializableException("Property has no getter method for $name of $clazz. " +
" If using Java and the parameter name looks anonymous, check that you have the -parameters option specified in the Java compiler.") "If using Java and the parameter name looks anonymous, check that you have the -parameters option specified in the Java compiler." +
"Alternately, provide a proxy serializer (SerializationCustomSerializer) if recompiling isn't an option")
val returnType = resolveTypeVariables(getter.genericReturnType, type) val returnType = resolveTypeVariables(getter.genericReturnType, type)
if (constructorParamTakesReturnTypeOfGetter(returnType, getter.genericReturnType, param)) { if (constructorParamTakesReturnTypeOfGetter(returnType, getter.genericReturnType, param)) {
rc += PropertySerializer.make(name, getter, returnType, factory) rc += PropertySerializer.make(name, getter, returnType, factory)

View File

@ -36,7 +36,7 @@ data class FactorySchemaAndDescriptor(val schemas: SerializationSchemas, val typ
open class SerializerFactory(val whitelist: ClassWhitelist, cl: ClassLoader) { open class SerializerFactory(val whitelist: ClassWhitelist, cl: ClassLoader) {
private val serializersByType = ConcurrentHashMap<Type, AMQPSerializer<Any>>() private val serializersByType = ConcurrentHashMap<Type, AMQPSerializer<Any>>()
private val serializersByDescriptor = ConcurrentHashMap<Any, AMQPSerializer<Any>>() private val serializersByDescriptor = ConcurrentHashMap<Any, AMQPSerializer<Any>>()
private val customSerializers = CopyOnWriteArrayList<CustomSerializer<out Any>>() private val customSerializers = CopyOnWriteArrayList<SerializerFor>()
val transformsCache = ConcurrentHashMap<String, EnumMap<TransformTypes, MutableList<Transform>>>() val transformsCache = ConcurrentHashMap<String, EnumMap<TransformTypes, MutableList<Transform>>>()
open val classCarpenter = ClassCarpenter(cl, whitelist) open val classCarpenter = ClassCarpenter(cl, whitelist)
@ -196,9 +196,16 @@ open class SerializerFactory(val whitelist: ClassWhitelist, cl: ClassLoader) {
} }
} }
fun registerExternal(customSerializer: CorDappCustomSerializer) {
if (!serializersByDescriptor.containsKey(customSerializer.typeDescriptor)) {
customSerializers += customSerializer
serializersByDescriptor[customSerializer.typeDescriptor] = customSerializer
}
}
/** /**
* Iterate over an AMQP schema, for each type ascertain weather it's on ClassPath of [classloader] amd * Iterate over an AMQP schema, for each type ascertain whether it's on ClassPath of [classloader] and,
* if not use the [ClassCarpenter] to generate a class to use in it's place * if not, use the [ClassCarpenter] to generate a class to use in it's place.
*/ */
private fun processSchema(schemaAndDescriptor: FactorySchemaAndDescriptor, sentinel: Boolean = false) { private fun processSchema(schemaAndDescriptor: FactorySchemaAndDescriptor, sentinel: Boolean = false) {
val metaSchema = CarpenterMetaSchema.newInstance() val metaSchema = CarpenterMetaSchema.newInstance()
@ -267,11 +274,13 @@ open class SerializerFactory(val whitelist: ClassWhitelist, cl: ClassLoader) {
for (customSerializer in customSerializers) { for (customSerializer in customSerializers) {
if (customSerializer.isSerializerFor(clazz)) { if (customSerializer.isSerializerFor(clazz)) {
val declaredSuperClass = declaredType.asClass()?.superclass val declaredSuperClass = declaredType.asClass()?.superclass
if (declaredSuperClass == null || !customSerializer.isSerializerFor(declaredSuperClass) || !customSerializer.revealSubclassesInSchema) { return if (declaredSuperClass == null
return customSerializer || !customSerializer.isSerializerFor(declaredSuperClass)
|| !customSerializer.revealSubclassesInSchema) {
customSerializer as? AMQPSerializer<Any>
} else { } else {
// Make a subclass serializer for the subclass and return that... // Make a subclass serializer for the subclass and return that...
return CustomSerializer.SubClass(clazz, uncheckedCast(customSerializer)) CustomSerializer.SubClass(clazz, uncheckedCast(customSerializer))
} }
} }
} }

View File

@ -0,0 +1,148 @@
package net.corda.nodeapi.internal.serialization.amqp
import org.junit.Test
import net.corda.core.serialization.ClassWhitelist
import net.corda.core.serialization.SerializationCustomSerializer
import org.assertj.core.api.Assertions
import java.io.NotSerializableException
import kotlin.test.assertEquals
class CorDappSerializerTests {
data class NeedsProxy (val a: String)
class NeedsProxyProxySerializer : SerializationCustomSerializer<NeedsProxy, NeedsProxyProxySerializer.Proxy> {
data class Proxy(val proxy_a_: String)
override fun fromProxy(proxy: Proxy) = NeedsProxy(proxy.proxy_a_)
override fun toProxy(obj: NeedsProxy) = Proxy(obj.a)
}
// Standard proxy serializer used internally, here for comparison purposes
class InternalProxySerializer(factory: SerializerFactory) :
CustomSerializer.Proxy<NeedsProxy, InternalProxySerializer.Proxy> (
NeedsProxy::class.java,
InternalProxySerializer.Proxy::class.java,
factory) {
data class Proxy(val proxy_a_: String)
override fun toProxy(obj: NeedsProxy): Proxy {
return Proxy(obj.a)
}
override fun fromProxy(proxy: Proxy): NeedsProxy {
return NeedsProxy(proxy.proxy_a_)
}
}
@Test
fun `type uses proxy`() {
val internalProxyFactory = testDefaultFactory()
val proxyFactory = testDefaultFactory()
val defaultFactory = testDefaultFactory()
val msg = "help"
proxyFactory.registerExternal (CorDappCustomSerializer(NeedsProxyProxySerializer(), proxyFactory))
internalProxyFactory.register (InternalProxySerializer(internalProxyFactory))
val needsProxy = NeedsProxy(msg)
val bAndSProxy = SerializationOutput(proxyFactory).serializeAndReturnSchema (needsProxy)
val bAndSInternal = SerializationOutput(internalProxyFactory).serializeAndReturnSchema (needsProxy)
val bAndSDefault = SerializationOutput(defaultFactory).serializeAndReturnSchema (needsProxy)
val objFromDefault = DeserializationInput(defaultFactory).deserializeAndReturnEnvelope(bAndSDefault.obj)
val objFromInternal = DeserializationInput(internalProxyFactory).deserializeAndReturnEnvelope(bAndSInternal.obj)
val objFromProxy = DeserializationInput(proxyFactory).deserializeAndReturnEnvelope(bAndSProxy.obj)
assertEquals(msg, objFromDefault.obj.a)
assertEquals(msg, objFromInternal.obj.a)
assertEquals(msg, objFromProxy.obj.a)
}
@Test
fun proxiedTypeIsNested() {
data class A (val a: Int, val b: NeedsProxy)
val factory = testDefaultFactory()
factory.registerExternal (CorDappCustomSerializer(NeedsProxyProxySerializer(), factory))
val tv1 = 100
val tv2 = "pants schmants"
val bAndS = SerializationOutput(factory).serializeAndReturnSchema (A(tv1, NeedsProxy(tv2)))
val objFromDefault = DeserializationInput(factory).deserializeAndReturnEnvelope(bAndS.obj)
assertEquals(tv1, objFromDefault.obj.a)
assertEquals(tv2, objFromDefault.obj.b.a)
}
@Test
fun testWithWhitelistNotAllowed() {
data class A (val a: Int, val b: NeedsProxy)
class WL : ClassWhitelist {
private val allowedClasses = emptySet<String>()
override fun hasListed(type: Class<*>): Boolean = type.name in allowedClasses
}
val factory = SerializerFactory(WL(), ClassLoader.getSystemClassLoader())
factory.registerExternal (CorDappCustomSerializer(NeedsProxyProxySerializer(), factory))
val tv1 = 100
val tv2 = "pants schmants"
Assertions.assertThatThrownBy {
SerializationOutput(factory).serialize(A(tv1, NeedsProxy(tv2)))
}.isInstanceOf(NotSerializableException::class.java)
}
@Test
fun testWithWhitelistAllowed() {
data class A (val a: Int, val b: NeedsProxy)
class WL : ClassWhitelist {
private val allowedClasses = hashSetOf(
A::class.java.name,
NeedsProxy::class.java.name)
override fun hasListed(type: Class<*>): Boolean = type.name in allowedClasses
}
val factory = SerializerFactory(WL(), ClassLoader.getSystemClassLoader())
factory.registerExternal (CorDappCustomSerializer(NeedsProxyProxySerializer(), factory))
val tv1 = 100
val tv2 = "pants schmants"
val obj = DeserializationInput(factory).deserialize(
SerializationOutput(factory).serialize(A(tv1, NeedsProxy(tv2))))
assertEquals(tv1, obj.a)
assertEquals(tv2, obj.b.a)
}
// The custom type not being whitelisted won't matter here because the act of adding a
// custom serializer bypasses the whitelist
@Test
fun testWithWhitelistAllowedOuterOnly() {
data class A (val a: Int, val b: NeedsProxy)
class WL : ClassWhitelist {
// explicitly don't add NeedsProxy
private val allowedClasses = hashSetOf(A::class.java.name)
override fun hasListed(type: Class<*>): Boolean = type.name in allowedClasses
}
val factory = SerializerFactory(WL(), ClassLoader.getSystemClassLoader())
factory.registerExternal (CorDappCustomSerializer(NeedsProxyProxySerializer(), factory))
val tv1 = 100
val tv2 = "pants schmants"
val obj = DeserializationInput(factory).deserialize(
SerializationOutput(factory).serialize(A(tv1, NeedsProxy(tv2))))
assertEquals(tv1, obj.a)
assertEquals(tv2, obj.b.a)
}
}

View File

@ -25,7 +25,7 @@ class OverridePKSerializerTest {
get() = TODO("not implemented") //To change initializer of created properties use File | Settings | File Templates. get() = TODO("not implemented") //To change initializer of created properties use File | Settings | File Templates.
} }
class AMQPTestSerializationScheme : AbstractAMQPSerializationScheme() { class AMQPTestSerializationScheme : AbstractAMQPSerializationScheme(emptyList()) {
override fun rpcServerSerializerFactory(context: SerializationContext): SerializerFactory { override fun rpcServerSerializerFactory(context: SerializationContext): SerializerFactory {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates. TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
} }

View File

@ -593,7 +593,7 @@ class SerializationOutputTests {
fun `test transaction state`() { fun `test transaction state`() {
val state = TransactionState(FooState(), FOO_PROGRAM_ID, MEGA_CORP) val state = TransactionState(FooState(), FOO_PROGRAM_ID, MEGA_CORP)
val scheme = AMQPServerSerializationScheme() val scheme = AMQPServerSerializationScheme(emptyList())
val func = scheme::class.superclasses.single { it.simpleName == "AbstractAMQPSerializationScheme" } val func = scheme::class.superclasses.single { it.simpleName == "AbstractAMQPSerializationScheme" }
.java.getDeclaredMethod("registerCustomSerializers", SerializerFactory::class.java) .java.getDeclaredMethod("registerCustomSerializers", SerializerFactory::class.java)
func.isAccessible = true func.isAccessible = true

View File

@ -305,11 +305,11 @@ open class Node(configuration: NodeConfiguration,
nodeSerializationEnv = SerializationEnvironmentImpl( nodeSerializationEnv = SerializationEnvironmentImpl(
SerializationFactoryImpl().apply { SerializationFactoryImpl().apply {
registerScheme(KryoServerSerializationScheme()) registerScheme(KryoServerSerializationScheme())
registerScheme(AMQPServerSerializationScheme()) registerScheme(AMQPServerSerializationScheme(cordappLoader.cordapps))
}, },
KRYO_P2P_CONTEXT.withClassLoader(classloader), p2pContext = AMQP_P2P_CONTEXT.withClassLoader(classloader),
rpcServerContext = KRYO_RPC_SERVER_CONTEXT.withClassLoader(classloader), rpcServerContext = KRYO_RPC_SERVER_CONTEXT.withClassLoader(classloader),
storageContext = KRYO_STORAGE_CONTEXT.withClassLoader(classloader), storageContext = AMQP_STORAGE_CONTEXT.withClassLoader(classloader),
checkpointContext = KRYO_CHECKPOINT_CONTEXT.withClassLoader(classloader)) checkpointContext = KRYO_CHECKPOINT_CONTEXT.withClassLoader(classloader))
} }

View File

@ -10,6 +10,7 @@ import net.corda.core.internal.*
import net.corda.core.internal.cordapp.CordappImpl import net.corda.core.internal.cordapp.CordappImpl
import net.corda.core.node.services.CordaService import net.corda.core.node.services.CordaService
import net.corda.core.schemas.MappedSchema import net.corda.core.schemas.MappedSchema
import net.corda.core.serialization.SerializationCustomSerializer
import net.corda.core.serialization.SerializationWhitelist import net.corda.core.serialization.SerializationWhitelist
import net.corda.core.serialization.SerializeAsToken import net.corda.core.serialization.SerializeAsToken
import net.corda.core.utilities.contextLogger import net.corda.core.utilities.contextLogger
@ -175,15 +176,16 @@ class CordappLoader private constructor(private val cordappJarPaths: List<Restri
/** A Cordapp representing the core package which is not scanned automatically. */ /** A Cordapp representing the core package which is not scanned automatically. */
@VisibleForTesting @VisibleForTesting
internal val coreCordapp = CordappImpl( internal val coreCordapp = CordappImpl(
listOf(), contractClassNames = listOf(),
listOf(), initiatedFlows = listOf(),
coreRPCFlows, rpcFlows = coreRPCFlows,
listOf(), serviceFlows = listOf(),
listOf(), schedulableFlows = listOf(),
listOf(), services = listOf(),
listOf(), serializationWhitelists = listOf(),
setOf(), serializationCustomSerializers = listOf(),
ContractUpgradeFlow.javaClass.protectionDomain.codeSource.location // Core JAR location customSchemas = setOf(),
jarPath = ContractUpgradeFlow.javaClass.protectionDomain.codeSource.location // Core JAR location
) )
} }
@ -197,6 +199,7 @@ class CordappLoader private constructor(private val cordappJarPaths: List<Restri
findSchedulableFlows(scanResult), findSchedulableFlows(scanResult),
findServices(scanResult), findServices(scanResult),
findPlugins(it), findPlugins(it),
findSerializers(scanResult),
findCustomSchemas(scanResult), findCustomSchemas(scanResult),
it.url) it.url)
} }
@ -247,6 +250,10 @@ class CordappLoader private constructor(private val cordappJarPaths: List<Restri
} + DefaultWhitelist // Always add the DefaultWhitelist to the whitelist for an app. } + DefaultWhitelist // Always add the DefaultWhitelist to the whitelist for an app.
} }
private fun findSerializers(scanResult: RestrictedScanResult): List<SerializationCustomSerializer<*, *>> {
return scanResult.getClassesImplementing(SerializationCustomSerializer::class)
}
private fun findCustomSchemas(scanResult: RestrictedScanResult): Set<MappedSchema> { private fun findCustomSchemas(scanResult: RestrictedScanResult): Set<MappedSchema> {
return scanResult.getClassesWithSuperclass(MappedSchema::class).toSet() return scanResult.getClassesWithSuperclass(MappedSchema::class).toSet()
} }
@ -303,6 +310,14 @@ class CordappLoader private constructor(private val cordappJarPaths: List<Restri
.map { it.kotlin.objectOrNewInstance() } .map { it.kotlin.objectOrNewInstance() }
} }
fun <T : Any> getClassesImplementing(type: KClass<T>): List<T> {
return scanResult.getNamesOfClassesImplementing(type.java)
.filter { it.startsWith(qualifiedNamePrefix) }
.mapNotNull { loadClass(it, type) }
.filterNot { Modifier.isAbstract(it.modifiers) }
.map { it.kotlin.objectOrNewInstance() }
}
fun <T : Any> getClassesWithAnnotation(type: KClass<T>, annotation: KClass<out Annotation>): List<Class<out T>> { fun <T : Any> getClassesWithAnnotation(type: KClass<T>, annotation: KClass<out Annotation>): List<Class<out T>> {
return scanResult.getNamesOfClassesWithAnnotation(annotation.java) return scanResult.getNamesOfClassesWithAnnotation(annotation.java)
.filter { it.startsWith(qualifiedNamePrefix) } .filter { it.startsWith(qualifiedNamePrefix) }

View File

@ -27,7 +27,7 @@ class SimmValuationTest {
@Test @Test
fun `runs SIMM valuation demo`() { fun `runs SIMM valuation demo`() {
driver(isDebug = true, extraCordappPackagesToScan = listOf("net.corda.vega.contracts")) { driver(isDebug = true, extraCordappPackagesToScan = listOf("net.corda.vega.contracts", "net.corda.vega.plugin.customserializers")) {
val nodeAFuture = startNode(providedName = nodeALegalName) val nodeAFuture = startNode(providedName = nodeALegalName)
val nodeBFuture = startNode(providedName = nodeBLegalName) val nodeBFuture = startNode(providedName = nodeBLegalName)
val (nodeA, nodeB) = listOf(nodeAFuture, nodeBFuture).map { it.getOrThrow() } val (nodeA, nodeB) = listOf(nodeAFuture, nodeBFuture).map { it.getOrThrow() }

View File

@ -19,7 +19,7 @@ val PORTFOLIO_SWAP_PROGRAM_ID = "net.corda.vega.contracts.PortfolioSwap"
* given point in time. This state can be consumed to create a new state with a mutated valuation or portfolio. * given point in time. This state can be consumed to create a new state with a mutated valuation or portfolio.
*/ */
data class PortfolioState(val portfolio: List<StateRef>, data class PortfolioState(val portfolio: List<StateRef>,
private val _parties: Pair<AbstractParty, AbstractParty>, val _parties: Pair<AbstractParty, AbstractParty>,
val valuationDate: LocalDate, val valuationDate: LocalDate,
val valuation: PortfolioValuation? = null, val valuation: PortfolioValuation? = null,
override val linearId: UniqueIdentifier = UniqueIdentifier()) override val linearId: UniqueIdentifier = UniqueIdentifier())

View File

@ -10,6 +10,7 @@ import com.opengamma.strata.market.curve.CurveName
import com.opengamma.strata.market.param.CurrencyParameterSensitivities import com.opengamma.strata.market.param.CurrencyParameterSensitivities
import com.opengamma.strata.market.param.CurrencyParameterSensitivity import com.opengamma.strata.market.param.CurrencyParameterSensitivity
import com.opengamma.strata.market.param.TenorDateParameterMetadata import com.opengamma.strata.market.param.TenorDateParameterMetadata
import com.opengamma.strata.market.param.ParameterMetadata
import net.corda.core.serialization.SerializationWhitelist import net.corda.core.serialization.SerializationWhitelist
import net.corda.vega.analytics.CordaMarketData import net.corda.vega.analytics.CordaMarketData
import net.corda.vega.analytics.InitialMarginTriple import net.corda.vega.analytics.InitialMarginTriple
@ -34,6 +35,7 @@ class SimmPluginRegistry : SerializationWhitelist {
DoubleArray::class.java, DoubleArray::class.java,
CurveName::class.java, CurveName::class.java,
TenorDateParameterMetadata::class.java, TenorDateParameterMetadata::class.java,
Tenor::class.java Tenor::class.java,
ParameterMetadata::class.java
) )
} }

View File

@ -0,0 +1,14 @@
package net.corda.vega.plugin.customserializers
import com.opengamma.strata.market.param.CurrencyParameterSensitivities
import com.opengamma.strata.market.param.CurrencyParameterSensitivity
import net.corda.core.serialization.SerializationCustomSerializer
@Suppress("UNUSED")
class CurrencyParameterSensitivitiesSerializer :
SerializationCustomSerializer<CurrencyParameterSensitivities, CurrencyParameterSensitivitiesSerializer.Proxy> {
data class Proxy(val sensitivities: List<CurrencyParameterSensitivity>)
override fun fromProxy(proxy: Proxy) = CurrencyParameterSensitivities.of(proxy.sensitivities)
override fun toProxy(obj: CurrencyParameterSensitivities) = Proxy(obj.sensitivities)
}

View File

@ -0,0 +1,26 @@
package net.corda.vega.plugin.customserializers
import com.opengamma.strata.market.param.CurrencyParameterSensitivity
import com.opengamma.strata.market.param.ParameterMetadata
import com.opengamma.strata.data.MarketDataName
import com.opengamma.strata.collect.array.DoubleArray
import com.opengamma.strata.basics.currency.Currency
import net.corda.core.serialization.SerializationCustomSerializer
@Suppress("UNUSED")
class CurrencyParameterSensitivitySerializer :
SerializationCustomSerializer<CurrencyParameterSensitivity, CurrencyParameterSensitivitySerializer.Proxy> {
data class Proxy(val currency: Currency, val marketDataName: MarketDataName<*>,
val parameterMetadata: List<ParameterMetadata>,
val sensitivity: DoubleArray)
override fun fromProxy(proxy: CurrencyParameterSensitivitySerializer.Proxy) =
CurrencyParameterSensitivity.of(
proxy.marketDataName,
proxy.parameterMetadata,
proxy.currency,
proxy.sensitivity)
override fun toProxy(obj: CurrencyParameterSensitivity) = Proxy((obj as CurrencyParameterSensitivity).currency,
obj.marketDataName, obj.parameterMetadata, obj.sensitivity)
}

View File

@ -0,0 +1,12 @@
package net.corda.vega.plugin.customserializers
import com.opengamma.strata.basics.currency.Currency
import net.corda.core.serialization.SerializationCustomSerializer
@Suppress("UNUSED")
class CurrencySerializer : SerializationCustomSerializer<Currency, CurrencySerializer.Proxy> {
data class Proxy(val currency: String)
override fun fromProxy(proxy: Proxy) = Currency.parse(proxy.currency)
override fun toProxy(obj: Currency) = Proxy(obj.toString())
}

View File

@ -0,0 +1,12 @@
package net.corda.vega.plugin.customserializers
import net.corda.core.serialization.SerializationCustomSerializer
import com.opengamma.strata.collect.array.DoubleArray
@Suppress("UNUSED")
class DoubleArraySerializer : SerializationCustomSerializer<DoubleArray, DoubleArraySerializer.Proxy> {
data class Proxy(val amount: kotlin.DoubleArray)
override fun fromProxy(proxy: Proxy) = DoubleArray.copyOf(proxy.amount)
override fun toProxy(obj: DoubleArray) = Proxy(obj.toArray())
}

View File

@ -0,0 +1,17 @@
package net.corda.vega.plugin.customserializers
import com.opengamma.strata.basics.currency.MultiCurrencyAmount
import com.opengamma.strata.basics.currency.Currency
import net.corda.core.serialization.*
@Suppress("UNUSED")
class MultiCurrencyAmountSerializer :
SerializationCustomSerializer<MultiCurrencyAmount, MultiCurrencyAmountSerializer.Proxy> {
data class Proxy(val curencies : Map<Currency, Double>)
override fun toProxy(obj: MultiCurrencyAmount) = Proxy(obj.toMap())
override fun fromProxy(proxy: Proxy) = MultiCurrencyAmount.of(proxy.curencies)
}

View File

@ -0,0 +1,15 @@
package net.corda.vega.plugin.customserializers
import com.opengamma.strata.basics.date.Tenor
import com.opengamma.strata.market.param.TenorDateParameterMetadata
import net.corda.core.serialization.*
import java.time.LocalDate
@Suppress("UNUSED")
class TenorDateParameterMetadataSerializer :
SerializationCustomSerializer<TenorDateParameterMetadata, TenorDateParameterMetadataSerializer.Proxy> {
data class Proxy(val tenor: Tenor, val date: LocalDate, val identifier: Tenor, val label: String)
override fun toProxy(obj: TenorDateParameterMetadata) = Proxy(obj.tenor, obj.date, obj.identifier, obj.label)
override fun fromProxy(proxy: Proxy) = TenorDateParameterMetadata.of(proxy.date, proxy.tenor, proxy.label)
}

View File

@ -0,0 +1,13 @@
package net.corda.vega.plugin.customserializers
import com.opengamma.strata.basics.date.Tenor
import net.corda.core.serialization.*
import java.time.Period
@Suppress("UNUSED")
class TenorSerializer : SerializationCustomSerializer<Tenor, TenorSerializer.Proxy> {
data class Proxy(val years: Int, val months: Int, val days: Int, val name: String)
override fun toProxy(obj: Tenor) = Proxy(obj.period.years, obj.period.months, obj.period.days, obj.toString())
override fun fromProxy(proxy: Proxy) = Tenor.of (Period.of(proxy.years, proxy.months, proxy.days))
}

View File

@ -47,6 +47,8 @@ import net.corda.testing.testNodeConfiguration
import org.apache.activemq.artemis.utils.ReusableLatch import org.apache.activemq.artemis.utils.ReusableLatch
import org.apache.sshd.common.util.security.SecurityUtils import org.apache.sshd.common.util.security.SecurityUtils
import rx.internal.schedulers.CachedThreadScheduler import rx.internal.schedulers.CachedThreadScheduler
import org.slf4j.Logger
import java.io.Closeable
import java.math.BigInteger import java.math.BigInteger
import java.nio.file.Path import java.nio.file.Path
import java.security.KeyPair import java.security.KeyPair

View File

@ -62,7 +62,7 @@ fun <T> withTestSerialization(inheritable: Boolean = false, callable: (Serializa
/** /**
* For example your test class uses [SerializationEnvironmentRule] but you want to turn it off for one method. * For example your test class uses [SerializationEnvironmentRule] but you want to turn it off for one method.
* Use sparingly, ideally a test class shouldn't mix serialization init mechanisms. * Use sparingly, ideally a test class shouldn't mix serializers init mechanisms.
*/ */
fun <T> withoutTestSerialization(callable: () -> T): T { fun <T> withoutTestSerialization(callable: () -> T): T {
val (property, env) = listOf(_contextSerializationEnv, _inheritableContextSerializationEnv).map { Pair(it, it.get()) }.single { it.second != null } val (property, env) = listOf(_contextSerializationEnv, _inheritableContextSerializationEnv).map { Pair(it, it.get()) }.single { it.second != null }
@ -99,13 +99,13 @@ private fun createTestSerializationEnv(label: String) = object : SerializationEn
SerializationFactoryImpl().apply { SerializationFactoryImpl().apply {
registerScheme(KryoClientSerializationScheme()) registerScheme(KryoClientSerializationScheme())
registerScheme(KryoServerSerializationScheme()) registerScheme(KryoServerSerializationScheme())
registerScheme(AMQPClientSerializationScheme()) registerScheme(AMQPClientSerializationScheme(emptyList()))
registerScheme(AMQPServerSerializationScheme()) registerScheme(AMQPServerSerializationScheme(emptyList()))
}, },
if (isAmqpEnabled()) AMQP_P2P_CONTEXT else KRYO_P2P_CONTEXT, AMQP_P2P_CONTEXT,
KRYO_RPC_SERVER_CONTEXT, KRYO_RPC_SERVER_CONTEXT,
KRYO_RPC_CLIENT_CONTEXT, KRYO_RPC_CLIENT_CONTEXT,
if (isAmqpEnabled()) AMQP_STORAGE_CONTEXT else KRYO_STORAGE_CONTEXT, AMQP_STORAGE_CONTEXT,
KRYO_CHECKPOINT_CONTEXT) { KRYO_CHECKPOINT_CONTEXT) {
override fun toString() = "testSerializationEnv($label)" override fun toString() = "testSerializationEnv($label)"
} }

View File

@ -14,7 +14,17 @@ class MockCordappProvider(cordappLoader: CordappLoader, attachmentStorage: Attac
val cordappRegistry = mutableListOf<Pair<Cordapp, AttachmentId>>() val cordappRegistry = mutableListOf<Pair<Cordapp, AttachmentId>>()
fun addMockCordapp(contractClassName: ContractClassName, attachments: MockAttachmentStorage) { fun addMockCordapp(contractClassName: ContractClassName, attachments: MockAttachmentStorage) {
val cordapp = CordappImpl(listOf(contractClassName), emptyList(), emptyList(), emptyList(), emptyList(), emptyList(), emptyList(), emptySet(), Paths.get(".").toUri().toURL()) val cordapp = CordappImpl(
contractClassNames = listOf(contractClassName),
initiatedFlows = emptyList(),
rpcFlows = emptyList(),
serviceFlows = emptyList(),
schedulableFlows = emptyList(),
services = emptyList(),
serializationWhitelists = emptyList(),
serializationCustomSerializers = emptyList(),
customSchemas = emptySet(),
jarPath = Paths.get(".").toUri().toURL())
if (cordappRegistry.none { it.first.contractClassNames.contains(contractClassName) }) { if (cordappRegistry.none { it.first.contractClassNames.contains(contractClassName) }) {
cordappRegistry.add(Pair(cordapp, findOrImportAttachment(contractClassName.toByteArray(), attachments))) cordappRegistry.add(Pair(cordapp, findOrImportAttachment(contractClassName.toByteArray(), attachments)))
} }

View File

@ -91,11 +91,7 @@ class Verifier {
registerScheme(KryoVerifierSerializationScheme) registerScheme(KryoVerifierSerializationScheme)
registerScheme(AMQPVerifierSerializationScheme) registerScheme(AMQPVerifierSerializationScheme)
}, },
/** AMQP_P2P_CONTEXT)
* Even though default context is set to Kryo P2P, the encoding will be adjusted depending on the incoming
* request received, see use of [context] in [main] method.
*/
KRYO_P2P_CONTEXT)
} }
} }
@ -108,7 +104,7 @@ class Verifier {
override fun rpcServerKryoPool(context: SerializationContext) = throw UnsupportedOperationException() override fun rpcServerKryoPool(context: SerializationContext) = throw UnsupportedOperationException()
} }
private object AMQPVerifierSerializationScheme : AbstractAMQPSerializationScheme() { private object AMQPVerifierSerializationScheme : AbstractAMQPSerializationScheme(emptyList()) {
override fun canDeserializeVersion(byteSequence: ByteSequence, target: SerializationContext.UseCase): Boolean { override fun canDeserializeVersion(byteSequence: ByteSequence, target: SerializationContext.UseCase): Boolean {
return (byteSequence == AmqpHeaderV1_0 && (target == SerializationContext.UseCase.P2P)) return (byteSequence == AmqpHeaderV1_0 && (target == SerializationContext.UseCase.P2P))
} }