mirror of
https://github.com/corda/corda.git
synced 2024-12-20 05:28:21 +00:00
CORDA-2147 Use serialization strict mode during transaction verification. (#4312)
* CORDA-2147 Use serialization strict mode during transaction verification. * CORDA-2147 Address code review comments. * CORDA-2147 Fix compilation error.
This commit is contained in:
parent
559932a581
commit
66e097b58d
@ -9,7 +9,6 @@ import net.corda.core.identity.CordaX500Name
|
|||||||
import net.corda.core.internal.PLATFORM_VERSION
|
import net.corda.core.internal.PLATFORM_VERSION
|
||||||
import net.corda.core.messaging.ClientRpcSslOptions
|
import net.corda.core.messaging.ClientRpcSslOptions
|
||||||
import net.corda.core.messaging.CordaRPCOps
|
import net.corda.core.messaging.CordaRPCOps
|
||||||
import net.corda.core.serialization.ClassWhitelist
|
|
||||||
import net.corda.core.serialization.internal.effectiveSerializationEnv
|
import net.corda.core.serialization.internal.effectiveSerializationEnv
|
||||||
import net.corda.core.utilities.NetworkHostAndPort
|
import net.corda.core.utilities.NetworkHostAndPort
|
||||||
import net.corda.core.utilities.days
|
import net.corda.core.utilities.days
|
||||||
@ -17,6 +16,7 @@ import net.corda.core.utilities.minutes
|
|||||||
import net.corda.core.utilities.seconds
|
import net.corda.core.utilities.seconds
|
||||||
import net.corda.nodeapi.internal.ArtemisTcpTransport.Companion.rpcConnectorTcpTransport
|
import net.corda.nodeapi.internal.ArtemisTcpTransport.Companion.rpcConnectorTcpTransport
|
||||||
import net.corda.serialization.internal.AMQP_RPC_CLIENT_CONTEXT
|
import net.corda.serialization.internal.AMQP_RPC_CLIENT_CONTEXT
|
||||||
|
import net.corda.serialization.internal.amqp.SerializationFactoryCacheKey
|
||||||
import net.corda.serialization.internal.amqp.SerializerFactory
|
import net.corda.serialization.internal.amqp.SerializerFactory
|
||||||
import java.time.Duration
|
import java.time.Duration
|
||||||
|
|
||||||
@ -296,7 +296,7 @@ class CordaRPCClient private constructor(
|
|||||||
effectiveSerializationEnv
|
effectiveSerializationEnv
|
||||||
} catch (e: IllegalStateException) {
|
} catch (e: IllegalStateException) {
|
||||||
try {
|
try {
|
||||||
AMQPClientSerializationScheme.initialiseSerialization(classLoader, Caffeine.newBuilder().maximumSize(128).build<Pair<ClassWhitelist, ClassLoader>, SerializerFactory>().asMap())
|
AMQPClientSerializationScheme.initialiseSerialization(classLoader, Caffeine.newBuilder().maximumSize(128).build<SerializationFactoryCacheKey, SerializerFactory>().asMap())
|
||||||
} catch (e: IllegalStateException) {
|
} catch (e: IllegalStateException) {
|
||||||
// Race e.g. two of these constructed in parallel, ignore.
|
// Race e.g. two of these constructed in parallel, ignore.
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,6 @@ package net.corda.client.rpc.internal.serialization.amqp
|
|||||||
|
|
||||||
import net.corda.core.cordapp.Cordapp
|
import net.corda.core.cordapp.Cordapp
|
||||||
import net.corda.core.internal.toSynchronised
|
import net.corda.core.internal.toSynchronised
|
||||||
import net.corda.core.serialization.ClassWhitelist
|
|
||||||
import net.corda.core.serialization.SerializationContext
|
import net.corda.core.serialization.SerializationContext
|
||||||
import net.corda.core.serialization.SerializationContext.UseCase
|
import net.corda.core.serialization.SerializationContext.UseCase
|
||||||
import net.corda.core.serialization.SerializationCustomSerializer
|
import net.corda.core.serialization.SerializationCustomSerializer
|
||||||
@ -18,21 +17,21 @@ import net.corda.serialization.internal.amqp.custom.RxNotificationSerializer
|
|||||||
*/
|
*/
|
||||||
class AMQPClientSerializationScheme(
|
class AMQPClientSerializationScheme(
|
||||||
cordappCustomSerializers: Set<SerializationCustomSerializer<*,*>>,
|
cordappCustomSerializers: Set<SerializationCustomSerializer<*,*>>,
|
||||||
serializerFactoriesForContexts: MutableMap<Pair<ClassWhitelist, ClassLoader>, SerializerFactory>
|
serializerFactoriesForContexts: MutableMap<SerializationFactoryCacheKey, SerializerFactory>
|
||||||
) : AbstractAMQPSerializationScheme(cordappCustomSerializers, serializerFactoriesForContexts) {
|
) : AbstractAMQPSerializationScheme(cordappCustomSerializers, serializerFactoriesForContexts) {
|
||||||
constructor(cordapps: List<Cordapp>) : this(cordapps.customSerializers, AccessOrderLinkedHashMap<Pair<ClassWhitelist, ClassLoader>, SerializerFactory>(128).toSynchronised())
|
constructor(cordapps: List<Cordapp>) : this(cordapps.customSerializers, AccessOrderLinkedHashMap<SerializationFactoryCacheKey, SerializerFactory>(128).toSynchronised())
|
||||||
constructor(cordapps: List<Cordapp>, serializerFactoriesForContexts: MutableMap<Pair<ClassWhitelist, ClassLoader>, SerializerFactory>) : this(cordapps.customSerializers, serializerFactoriesForContexts)
|
constructor(cordapps: List<Cordapp>, serializerFactoriesForContexts: MutableMap<SerializationFactoryCacheKey, SerializerFactory>) : this(cordapps.customSerializers, serializerFactoriesForContexts)
|
||||||
|
|
||||||
@Suppress("UNUSED")
|
@Suppress("UNUSED")
|
||||||
constructor() : this(emptySet(), AccessOrderLinkedHashMap<Pair<ClassWhitelist, ClassLoader>, SerializerFactory>(128).toSynchronised())
|
constructor() : this(emptySet(), AccessOrderLinkedHashMap<SerializationFactoryCacheKey, SerializerFactory>(128).toSynchronised())
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
/** Call from main only. */
|
/** Call from main only. */
|
||||||
fun initialiseSerialization(classLoader: ClassLoader? = null, serializerFactoriesForContexts: MutableMap<Pair<ClassWhitelist, ClassLoader>, SerializerFactory> = AccessOrderLinkedHashMap<Pair<ClassWhitelist, ClassLoader>, SerializerFactory>(128).toSynchronised()) {
|
fun initialiseSerialization(classLoader: ClassLoader? = null, serializerFactoriesForContexts: MutableMap<SerializationFactoryCacheKey, SerializerFactory> = AccessOrderLinkedHashMap<SerializationFactoryCacheKey, SerializerFactory>(128).toSynchronised()) {
|
||||||
nodeSerializationEnv = createSerializationEnv(classLoader, serializerFactoriesForContexts)
|
nodeSerializationEnv = createSerializationEnv(classLoader, serializerFactoriesForContexts)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun createSerializationEnv(classLoader: ClassLoader? = null, serializerFactoriesForContexts: MutableMap<Pair<ClassWhitelist, ClassLoader>, SerializerFactory> = AccessOrderLinkedHashMap<Pair<ClassWhitelist, ClassLoader>, SerializerFactory>(128).toSynchronised()): SerializationEnvironment {
|
fun createSerializationEnv(classLoader: ClassLoader? = null, serializerFactoriesForContexts: MutableMap<SerializationFactoryCacheKey, SerializerFactory> = AccessOrderLinkedHashMap<SerializationFactoryCacheKey, SerializerFactory>(128).toSynchronised()): SerializationEnvironment {
|
||||||
return SerializationEnvironment.with(
|
return SerializationEnvironment.with(
|
||||||
SerializationFactoryImpl().apply {
|
SerializationFactoryImpl().apply {
|
||||||
registerScheme(AMQPClientSerializationScheme(emptyList(), serializerFactoriesForContexts))
|
registerScheme(AMQPClientSerializationScheme(emptyList(), serializerFactoriesForContexts))
|
||||||
|
@ -7,10 +7,7 @@ import net.corda.core.serialization.SerializationCustomSerializer
|
|||||||
import net.corda.core.serialization.internal.SerializationEnvironment
|
import net.corda.core.serialization.internal.SerializationEnvironment
|
||||||
import net.corda.core.serialization.internal._contextSerializationEnv
|
import net.corda.core.serialization.internal._contextSerializationEnv
|
||||||
import net.corda.serialization.internal.*
|
import net.corda.serialization.internal.*
|
||||||
import net.corda.serialization.internal.amqp.AbstractAMQPSerializationScheme
|
import net.corda.serialization.internal.amqp.*
|
||||||
import net.corda.serialization.internal.amqp.AccessOrderLinkedHashMap
|
|
||||||
import net.corda.serialization.internal.amqp.SerializerFactory
|
|
||||||
import net.corda.serialization.internal.amqp.amqpMagic
|
|
||||||
import org.junit.rules.TestRule
|
import org.junit.rules.TestRule
|
||||||
import org.junit.runner.Description
|
import org.junit.runner.Description
|
||||||
import org.junit.runners.model.Statement
|
import org.junit.runners.model.Statement
|
||||||
@ -67,7 +64,7 @@ class LocalSerializationRule(private val label: String) : TestRule {
|
|||||||
|
|
||||||
private class AMQPSerializationScheme(
|
private class AMQPSerializationScheme(
|
||||||
cordappCustomSerializers: Set<SerializationCustomSerializer<*, *>>,
|
cordappCustomSerializers: Set<SerializationCustomSerializer<*, *>>,
|
||||||
serializerFactoriesForContexts: AccessOrderLinkedHashMap<Pair<ClassWhitelist, ClassLoader>, SerializerFactory>
|
serializerFactoriesForContexts: AccessOrderLinkedHashMap<SerializationFactoryCacheKey, SerializerFactory>
|
||||||
) : AbstractAMQPSerializationScheme(cordappCustomSerializers, serializerFactoriesForContexts) {
|
) : AbstractAMQPSerializationScheme(cordappCustomSerializers, serializerFactoriesForContexts) {
|
||||||
override fun rpcServerSerializerFactory(context: SerializationContext): SerializerFactory {
|
override fun rpcServerSerializerFactory(context: SerializationContext): SerializerFactory {
|
||||||
throw UnsupportedOperationException()
|
throw UnsupportedOperationException()
|
||||||
|
@ -150,6 +150,12 @@ interface SerializationContext {
|
|||||||
* The default is false.
|
* The default is false.
|
||||||
*/
|
*/
|
||||||
val lenientCarpenterEnabled: Boolean
|
val lenientCarpenterEnabled: Boolean
|
||||||
|
/**
|
||||||
|
* If true the carpenter will fail if the binary to be deserialized contains more fields then the current object from the classpath.
|
||||||
|
*
|
||||||
|
* The default is false.
|
||||||
|
*/
|
||||||
|
val preventDataLoss: Boolean
|
||||||
/**
|
/**
|
||||||
* The use case we are serializing or deserializing for. See [UseCase].
|
* The use case we are serializing or deserializing for. See [UseCase].
|
||||||
*/
|
*/
|
||||||
@ -171,6 +177,12 @@ interface SerializationContext {
|
|||||||
*/
|
*/
|
||||||
fun withLenientCarpenter(): SerializationContext
|
fun withLenientCarpenter(): SerializationContext
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a new context based on this one but with a strict evolution.
|
||||||
|
* @see preventDataLoss
|
||||||
|
*/
|
||||||
|
fun withPreventDataLoss(): SerializationContext
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper method to return a new context based on this context with the deserialization class loader changed.
|
* Helper method to return a new context based on this context with the deserialization class loader changed.
|
||||||
*/
|
*/
|
||||||
|
@ -123,7 +123,7 @@ internal object AttachmentsClassLoaderBuilder {
|
|||||||
val transactionClassLoader = AttachmentsClassLoaderBuilder.build(attachments)
|
val transactionClassLoader = AttachmentsClassLoaderBuilder.build(attachments)
|
||||||
|
|
||||||
// Create a new serializationContext for the current Transaction.
|
// Create a new serializationContext for the current Transaction.
|
||||||
val transactionSerializationContext = SerializationFactory.defaultFactory.defaultContext.withClassLoader(transactionClassLoader)
|
val transactionSerializationContext = SerializationFactory.defaultFactory.defaultContext.withPreventDataLoss().withClassLoader(transactionClassLoader)
|
||||||
|
|
||||||
// Deserialize all relevant classes in the transaction classloader.
|
// Deserialize all relevant classes in the transaction classloader.
|
||||||
return SerializationFactory.defaultFactory.withCurrentContext(transactionSerializationContext) {
|
return SerializationFactory.defaultFactory.withCurrentContext(transactionSerializationContext) {
|
||||||
|
@ -23,7 +23,6 @@ import net.corda.core.messaging.RPCOps
|
|||||||
import net.corda.core.node.NetworkParameters
|
import net.corda.core.node.NetworkParameters
|
||||||
import net.corda.core.node.NodeInfo
|
import net.corda.core.node.NodeInfo
|
||||||
import net.corda.core.node.ServiceHub
|
import net.corda.core.node.ServiceHub
|
||||||
import net.corda.core.serialization.ClassWhitelist
|
|
||||||
import net.corda.core.serialization.internal.SerializationEnvironment
|
import net.corda.core.serialization.internal.SerializationEnvironment
|
||||||
import net.corda.core.serialization.internal.nodeSerializationEnv
|
import net.corda.core.serialization.internal.nodeSerializationEnv
|
||||||
import net.corda.core.utilities.NetworkHostAndPort
|
import net.corda.core.utilities.NetworkHostAndPort
|
||||||
@ -56,6 +55,7 @@ import net.corda.nodeapi.internal.config.User
|
|||||||
import net.corda.nodeapi.internal.crypto.X509Utilities
|
import net.corda.nodeapi.internal.crypto.X509Utilities
|
||||||
import net.corda.nodeapi.internal.persistence.CouldNotCreateDataSourceException
|
import net.corda.nodeapi.internal.persistence.CouldNotCreateDataSourceException
|
||||||
import net.corda.serialization.internal.*
|
import net.corda.serialization.internal.*
|
||||||
|
import net.corda.serialization.internal.amqp.SerializationFactoryCacheKey
|
||||||
import net.corda.serialization.internal.amqp.SerializerFactory
|
import net.corda.serialization.internal.amqp.SerializerFactory
|
||||||
import org.apache.commons.lang.SystemUtils
|
import org.apache.commons.lang.SystemUtils
|
||||||
import org.h2.jdbc.JdbcSQLException
|
import org.h2.jdbc.JdbcSQLException
|
||||||
@ -474,8 +474,8 @@ open class Node(configuration: NodeConfiguration,
|
|||||||
val classloader = cordappLoader.appClassLoader
|
val classloader = cordappLoader.appClassLoader
|
||||||
nodeSerializationEnv = SerializationEnvironment.with(
|
nodeSerializationEnv = SerializationEnvironment.with(
|
||||||
SerializationFactoryImpl().apply {
|
SerializationFactoryImpl().apply {
|
||||||
registerScheme(AMQPServerSerializationScheme(cordappLoader.cordapps, Caffeine.newBuilder().maximumSize(128).build<Pair<ClassWhitelist, ClassLoader>, SerializerFactory>().asMap()))
|
registerScheme(AMQPServerSerializationScheme(cordappLoader.cordapps, Caffeine.newBuilder().maximumSize(128).build<SerializationFactoryCacheKey, SerializerFactory>().asMap()))
|
||||||
registerScheme(AMQPClientSerializationScheme(cordappLoader.cordapps, Caffeine.newBuilder().maximumSize(128).build<Pair<ClassWhitelist, ClassLoader>, SerializerFactory>().asMap()))
|
registerScheme(AMQPClientSerializationScheme(cordappLoader.cordapps, Caffeine.newBuilder().maximumSize(128).build<SerializationFactoryCacheKey, SerializerFactory>().asMap()))
|
||||||
},
|
},
|
||||||
p2pContext = AMQP_P2P_CONTEXT.withClassLoader(classloader),
|
p2pContext = AMQP_P2P_CONTEXT.withClassLoader(classloader),
|
||||||
rpcServerContext = AMQP_RPC_SERVER_CONTEXT.withClassLoader(classloader),
|
rpcServerContext = AMQP_RPC_SERVER_CONTEXT.withClassLoader(classloader),
|
||||||
|
@ -2,14 +2,10 @@ package net.corda.node.serialization.amqp
|
|||||||
|
|
||||||
import net.corda.core.cordapp.Cordapp
|
import net.corda.core.cordapp.Cordapp
|
||||||
import net.corda.core.internal.toSynchronised
|
import net.corda.core.internal.toSynchronised
|
||||||
import net.corda.core.serialization.ClassWhitelist
|
|
||||||
import net.corda.core.serialization.SerializationContext
|
import net.corda.core.serialization.SerializationContext
|
||||||
import net.corda.core.serialization.SerializationCustomSerializer
|
import net.corda.core.serialization.SerializationCustomSerializer
|
||||||
import net.corda.serialization.internal.CordaSerializationMagic
|
import net.corda.serialization.internal.CordaSerializationMagic
|
||||||
import net.corda.serialization.internal.amqp.AbstractAMQPSerializationScheme
|
import net.corda.serialization.internal.amqp.*
|
||||||
import net.corda.serialization.internal.amqp.AccessOrderLinkedHashMap
|
|
||||||
import net.corda.serialization.internal.amqp.SerializerFactory
|
|
||||||
import net.corda.serialization.internal.amqp.SerializerFactoryBuilder
|
|
||||||
import net.corda.serialization.internal.amqp.custom.RxNotificationSerializer
|
import net.corda.serialization.internal.amqp.custom.RxNotificationSerializer
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -18,12 +14,12 @@ import net.corda.serialization.internal.amqp.custom.RxNotificationSerializer
|
|||||||
*/
|
*/
|
||||||
class AMQPServerSerializationScheme(
|
class AMQPServerSerializationScheme(
|
||||||
cordappCustomSerializers: Set<SerializationCustomSerializer<*, *>>,
|
cordappCustomSerializers: Set<SerializationCustomSerializer<*, *>>,
|
||||||
serializerFactoriesForContexts: MutableMap<Pair<ClassWhitelist, ClassLoader>, SerializerFactory>
|
serializerFactoriesForContexts: MutableMap<SerializationFactoryCacheKey, SerializerFactory>
|
||||||
) : AbstractAMQPSerializationScheme(cordappCustomSerializers, serializerFactoriesForContexts) {
|
) : AbstractAMQPSerializationScheme(cordappCustomSerializers, serializerFactoriesForContexts) {
|
||||||
constructor(cordapps: List<Cordapp>) : this(cordapps.customSerializers, AccessOrderLinkedHashMap<Pair<ClassWhitelist, ClassLoader>, SerializerFactory>(128).toSynchronised())
|
constructor(cordapps: List<Cordapp>) : this(cordapps.customSerializers, AccessOrderLinkedHashMap<SerializationFactoryCacheKey, SerializerFactory>(128).toSynchronised())
|
||||||
constructor(cordapps: List<Cordapp>, serializerFactoriesForContexts: MutableMap<Pair<ClassWhitelist, ClassLoader>, SerializerFactory>) : this(cordapps.customSerializers, serializerFactoriesForContexts)
|
constructor(cordapps: List<Cordapp>, serializerFactoriesForContexts: MutableMap<SerializationFactoryCacheKey, SerializerFactory>) : this(cordapps.customSerializers, serializerFactoriesForContexts)
|
||||||
|
|
||||||
constructor() : this(emptySet(), AccessOrderLinkedHashMap<Pair<ClassWhitelist, ClassLoader>, SerializerFactory>(128).toSynchronised() )
|
constructor() : this(emptySet(), AccessOrderLinkedHashMap<SerializationFactoryCacheKey, SerializerFactory>(128).toSynchronised() )
|
||||||
|
|
||||||
override fun rpcClientSerializerFactory(context: SerializationContext): SerializerFactory {
|
override fun rpcClientSerializerFactory(context: SerializationContext): SerializerFactory {
|
||||||
throw UnsupportedOperationException()
|
throw UnsupportedOperationException()
|
||||||
|
@ -9,16 +9,12 @@ import net.corda.client.rpc.internal.serialization.amqp.RpcClientObservableDeSer
|
|||||||
import net.corda.core.context.Trace
|
import net.corda.core.context.Trace
|
||||||
import net.corda.core.internal.ThreadBox
|
import net.corda.core.internal.ThreadBox
|
||||||
import net.corda.core.internal.toSynchronised
|
import net.corda.core.internal.toSynchronised
|
||||||
import net.corda.core.serialization.ClassWhitelist
|
|
||||||
import net.corda.node.internal.serialization.testutils.AMQPRoundTripRPCSerializationScheme
|
import net.corda.node.internal.serialization.testutils.AMQPRoundTripRPCSerializationScheme
|
||||||
import net.corda.node.internal.serialization.testutils.serializationContext
|
import net.corda.node.internal.serialization.testutils.serializationContext
|
||||||
import net.corda.node.serialization.amqp.RpcServerObservableSerializer
|
import net.corda.node.serialization.amqp.RpcServerObservableSerializer
|
||||||
import net.corda.node.services.messaging.ObservableSubscription
|
import net.corda.node.services.messaging.ObservableSubscription
|
||||||
import net.corda.nodeapi.RPCApi
|
import net.corda.nodeapi.RPCApi
|
||||||
import net.corda.serialization.internal.amqp.AccessOrderLinkedHashMap
|
import net.corda.serialization.internal.amqp.*
|
||||||
import net.corda.serialization.internal.amqp.DeserializationInput
|
|
||||||
import net.corda.serialization.internal.amqp.SerializationOutput
|
|
||||||
import net.corda.serialization.internal.amqp.SerializerFactory
|
|
||||||
import org.apache.activemq.artemis.api.core.SimpleString
|
import org.apache.activemq.artemis.api.core.SimpleString
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import rx.Notification
|
import rx.Notification
|
||||||
@ -63,7 +59,7 @@ class RoundTripObservableSerializerTests {
|
|||||||
@Test
|
@Test
|
||||||
fun roundTripTest1() {
|
fun roundTripTest1() {
|
||||||
val serializationScheme = AMQPRoundTripRPCSerializationScheme(
|
val serializationScheme = AMQPRoundTripRPCSerializationScheme(
|
||||||
serializationContext, emptySet(), AccessOrderLinkedHashMap<Pair<ClassWhitelist, ClassLoader>, SerializerFactory>(128).toSynchronised())
|
serializationContext, emptySet(), AccessOrderLinkedHashMap<SerializationFactoryCacheKey, SerializerFactory>(128).toSynchronised())
|
||||||
|
|
||||||
// Fake up a message ID, needs to be used on both "sides". The server setting it in the subscriptionMap,
|
// Fake up a message ID, needs to be used on both "sides". The server setting it in the subscriptionMap,
|
||||||
// the client as a property of the deserializer which, in the actual RPC client, is pulled off of
|
// the client as a property of the deserializer which, in the actual RPC client, is pulled off of
|
||||||
|
@ -2,17 +2,13 @@ package net.corda.node.internal.serialization.testutils
|
|||||||
|
|
||||||
import net.corda.client.rpc.internal.serialization.amqp.RpcClientObservableDeSerializer
|
import net.corda.client.rpc.internal.serialization.amqp.RpcClientObservableDeSerializer
|
||||||
import net.corda.core.context.Trace
|
import net.corda.core.context.Trace
|
||||||
import net.corda.core.serialization.ClassWhitelist
|
|
||||||
import net.corda.core.serialization.SerializationContext
|
import net.corda.core.serialization.SerializationContext
|
||||||
import net.corda.core.serialization.SerializationCustomSerializer
|
import net.corda.core.serialization.SerializationCustomSerializer
|
||||||
import net.corda.node.serialization.amqp.RpcServerObservableSerializer
|
import net.corda.node.serialization.amqp.RpcServerObservableSerializer
|
||||||
import net.corda.nodeapi.RPCApi
|
import net.corda.nodeapi.RPCApi
|
||||||
import net.corda.serialization.internal.CordaSerializationMagic
|
import net.corda.serialization.internal.CordaSerializationMagic
|
||||||
import net.corda.serialization.internal.amqp.AbstractAMQPSerializationScheme
|
|
||||||
import net.corda.serialization.internal.amqp.SerializerFactory
|
|
||||||
import net.corda.serialization.internal.AllWhitelist
|
import net.corda.serialization.internal.AllWhitelist
|
||||||
import net.corda.serialization.internal.amqp.AccessOrderLinkedHashMap
|
import net.corda.serialization.internal.amqp.*
|
||||||
import net.corda.serialization.internal.amqp.SerializerFactoryBuilder
|
|
||||||
import net.corda.client.rpc.internal.ObservableContext as ClientObservableContext
|
import net.corda.client.rpc.internal.ObservableContext as ClientObservableContext
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -24,7 +20,7 @@ import net.corda.client.rpc.internal.ObservableContext as ClientObservableContex
|
|||||||
class AMQPRoundTripRPCSerializationScheme(
|
class AMQPRoundTripRPCSerializationScheme(
|
||||||
private val serializationContext: SerializationContext,
|
private val serializationContext: SerializationContext,
|
||||||
cordappCustomSerializers: Set<SerializationCustomSerializer<*, *>>,
|
cordappCustomSerializers: Set<SerializationCustomSerializer<*, *>>,
|
||||||
serializerFactoriesForContexts: MutableMap<Pair<ClassWhitelist, ClassLoader>, SerializerFactory>)
|
serializerFactoriesForContexts: MutableMap<SerializationFactoryCacheKey, SerializerFactory>)
|
||||||
: AbstractAMQPSerializationScheme(
|
: AbstractAMQPSerializationScheme(
|
||||||
cordappCustomSerializers, serializerFactoriesForContexts
|
cordappCustomSerializers, serializerFactoriesForContexts
|
||||||
) {
|
) {
|
||||||
|
@ -1,23 +1,16 @@
|
|||||||
package net.corda.serialization.internal
|
package net.corda.serialization.internal
|
||||||
|
|
||||||
import com.github.benmanes.caffeine.cache.Cache
|
|
||||||
import com.github.benmanes.caffeine.cache.Caffeine
|
|
||||||
import net.corda.core.DeleteForDJVM
|
import net.corda.core.DeleteForDJVM
|
||||||
import net.corda.core.KeepForDJVM
|
import net.corda.core.KeepForDJVM
|
||||||
import net.corda.core.contracts.Attachment
|
|
||||||
import net.corda.core.crypto.SecureHash
|
import net.corda.core.crypto.SecureHash
|
||||||
import net.corda.core.internal.copyBytes
|
import net.corda.core.internal.copyBytes
|
||||||
import net.corda.core.serialization.*
|
import net.corda.core.serialization.*
|
||||||
import net.corda.core.serialization.internal.AttachmentsClassLoader
|
|
||||||
import net.corda.core.utilities.ByteSequence
|
import net.corda.core.utilities.ByteSequence
|
||||||
import net.corda.serialization.internal.amqp.amqpMagic
|
import net.corda.serialization.internal.amqp.amqpMagic
|
||||||
import org.slf4j.LoggerFactory
|
import org.slf4j.LoggerFactory
|
||||||
import java.io.NotSerializableException
|
import java.io.NotSerializableException
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.concurrent.ConcurrentHashMap
|
import java.util.concurrent.ConcurrentHashMap
|
||||||
import java.util.concurrent.ExecutionException
|
|
||||||
|
|
||||||
const val attachmentsClassLoaderEnabledPropertyName = "attachments.class.loader.enabled"
|
|
||||||
|
|
||||||
internal object NullEncodingWhitelist : EncodingWhitelist {
|
internal object NullEncodingWhitelist : EncodingWhitelist {
|
||||||
override fun acceptEncoding(encoding: SerializationEncoding) = false
|
override fun acceptEncoding(encoding: SerializationEncoding) = false
|
||||||
@ -32,7 +25,8 @@ data class SerializationContextImpl @JvmOverloads constructor(override val prefe
|
|||||||
override val useCase: SerializationContext.UseCase,
|
override val useCase: SerializationContext.UseCase,
|
||||||
override val encoding: SerializationEncoding?,
|
override val encoding: SerializationEncoding?,
|
||||||
override val encodingWhitelist: EncodingWhitelist = NullEncodingWhitelist,
|
override val encodingWhitelist: EncodingWhitelist = NullEncodingWhitelist,
|
||||||
override val lenientCarpenterEnabled: Boolean = false) : SerializationContext {
|
override val lenientCarpenterEnabled: Boolean = false,
|
||||||
|
override val preventDataLoss: Boolean = false) : SerializationContext {
|
||||||
/**
|
/**
|
||||||
* {@inheritDoc}
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
@ -50,6 +44,8 @@ data class SerializationContextImpl @JvmOverloads constructor(override val prefe
|
|||||||
|
|
||||||
override fun withLenientCarpenter(): SerializationContext = copy(lenientCarpenterEnabled = true)
|
override fun withLenientCarpenter(): SerializationContext = copy(lenientCarpenterEnabled = true)
|
||||||
|
|
||||||
|
override fun withPreventDataLoss(): SerializationContext = copy(preventDataLoss = true)
|
||||||
|
|
||||||
override fun withClassLoader(classLoader: ClassLoader): SerializationContext {
|
override fun withClassLoader(classLoader: ClassLoader): SerializationContext {
|
||||||
return copy(deserializationClassLoader = classLoader)
|
return copy(deserializationClassLoader = classLoader)
|
||||||
}
|
}
|
||||||
@ -67,8 +63,8 @@ data class SerializationContextImpl @JvmOverloads constructor(override val prefe
|
|||||||
|
|
||||||
@KeepForDJVM
|
@KeepForDJVM
|
||||||
open class SerializationFactoryImpl(
|
open class SerializationFactoryImpl(
|
||||||
// TODO: This is read-mostly. Probably a faster implementation to be found.
|
// TODO: This is read-mostly. Probably a faster implementation to be found.
|
||||||
private val schemes: MutableMap<Pair<CordaSerializationMagic, SerializationContext.UseCase>, SerializationScheme>
|
private val schemes: MutableMap<Pair<CordaSerializationMagic, SerializationContext.UseCase>, SerializationScheme>
|
||||||
) : SerializationFactory() {
|
) : SerializationFactory() {
|
||||||
@DeleteForDJVM
|
@DeleteForDJVM
|
||||||
constructor() : this(ConcurrentHashMap())
|
constructor() : this(ConcurrentHashMap())
|
||||||
@ -132,7 +128,6 @@ open class SerializationFactoryImpl(
|
|||||||
override fun hashCode(): Int = registeredSchemes.hashCode()
|
override fun hashCode(): Int = registeredSchemes.hashCode()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@KeepForDJVM
|
@KeepForDJVM
|
||||||
interface SerializationScheme {
|
interface SerializationScheme {
|
||||||
fun canDeserializeVersion(magic: CordaSerializationMagic, target: SerializationContext.UseCase): Boolean
|
fun canDeserializeVersion(magic: CordaSerializationMagic, target: SerializationContext.UseCase): Boolean
|
||||||
|
@ -22,6 +22,8 @@ import java.util.*
|
|||||||
|
|
||||||
val AMQP_ENABLED get() = SerializationDefaults.P2P_CONTEXT.preferredSerializationVersion == amqpMagic
|
val AMQP_ENABLED get() = SerializationDefaults.P2P_CONTEXT.preferredSerializationVersion == amqpMagic
|
||||||
|
|
||||||
|
data class SerializationFactoryCacheKey(val classWhitelist: ClassWhitelist, val deserializationClassLoader: ClassLoader, val preventDataLoss: Boolean)
|
||||||
|
|
||||||
fun SerializerFactory.addToWhitelist(vararg types: Class<*>) {
|
fun SerializerFactory.addToWhitelist(vararg types: Class<*>) {
|
||||||
require(types.toSet().size == types.size) {
|
require(types.toSet().size == types.size) {
|
||||||
val duplicates = types.toMutableList()
|
val duplicates = types.toMutableList()
|
||||||
@ -41,14 +43,14 @@ interface SerializerFactoryFactory {
|
|||||||
@KeepForDJVM
|
@KeepForDJVM
|
||||||
abstract class AbstractAMQPSerializationScheme(
|
abstract class AbstractAMQPSerializationScheme(
|
||||||
private val cordappCustomSerializers: Set<SerializationCustomSerializer<*, *>>,
|
private val cordappCustomSerializers: Set<SerializationCustomSerializer<*, *>>,
|
||||||
maybeNotConcurrentSerializerFactoriesForContexts: MutableMap<Pair<ClassWhitelist, ClassLoader>, SerializerFactory>,
|
maybeNotConcurrentSerializerFactoriesForContexts: MutableMap<SerializationFactoryCacheKey, SerializerFactory>,
|
||||||
val sff: SerializerFactoryFactory = createSerializerFactoryFactory()
|
val sff: SerializerFactoryFactory = createSerializerFactoryFactory()
|
||||||
) : SerializationScheme {
|
) : SerializationScheme {
|
||||||
@DeleteForDJVM
|
@DeleteForDJVM
|
||||||
constructor(cordapps: List<Cordapp>) : this(cordapps.customSerializers, AccessOrderLinkedHashMap<Pair<ClassWhitelist, ClassLoader>, SerializerFactory>(128).toSynchronised())
|
constructor(cordapps: List<Cordapp>) : this(cordapps.customSerializers, AccessOrderLinkedHashMap<SerializationFactoryCacheKey, SerializerFactory>(128).toSynchronised())
|
||||||
|
|
||||||
// This is a bit gross but a broader check for ConcurrentMap is not allowed inside DJVM.
|
// This is a bit gross but a broader check for ConcurrentMap is not allowed inside DJVM.
|
||||||
private val serializerFactoriesForContexts: MutableMap<Pair<ClassWhitelist, ClassLoader>, SerializerFactory> = if (maybeNotConcurrentSerializerFactoriesForContexts is AccessOrderLinkedHashMap<*, *>) {
|
private val serializerFactoriesForContexts: MutableMap<SerializationFactoryCacheKey, SerializerFactory> = if (maybeNotConcurrentSerializerFactoriesForContexts is AccessOrderLinkedHashMap<*, *>) {
|
||||||
Collections.synchronizedMap(maybeNotConcurrentSerializerFactoriesForContexts)
|
Collections.synchronizedMap(maybeNotConcurrentSerializerFactoriesForContexts)
|
||||||
} else {
|
} else {
|
||||||
maybeNotConcurrentSerializerFactoriesForContexts
|
maybeNotConcurrentSerializerFactoriesForContexts
|
||||||
@ -174,19 +176,19 @@ abstract class AbstractAMQPSerializationScheme(
|
|||||||
open val publicKeySerializer: CustomSerializer<*> = net.corda.serialization.internal.amqp.custom.PublicKeySerializer
|
open val publicKeySerializer: CustomSerializer<*> = net.corda.serialization.internal.amqp.custom.PublicKeySerializer
|
||||||
|
|
||||||
fun getSerializerFactory(context: SerializationContext): SerializerFactory {
|
fun getSerializerFactory(context: SerializationContext): SerializerFactory {
|
||||||
val key = Pair(context.whitelist, context.deserializationClassLoader)
|
val key = SerializationFactoryCacheKey(context.whitelist, context.deserializationClassLoader, context.preventDataLoss)
|
||||||
// ConcurrentHashMap.get() is lock free, but computeIfAbsent is not, even if the key is in the map already.
|
// ConcurrentHashMap.get() is lock free, but computeIfAbsent is not, even if the key is in the map already.
|
||||||
return serializerFactoriesForContexts[key] ?: serializerFactoriesForContexts.computeIfAbsent(key) {
|
return serializerFactoriesForContexts[key] ?: serializerFactoriesForContexts.computeIfAbsent(key) {
|
||||||
when (context.useCase) {
|
when (context.useCase) {
|
||||||
SerializationContext.UseCase.RPCClient ->
|
SerializationContext.UseCase.RPCClient ->
|
||||||
rpcClientSerializerFactory(context)
|
rpcClientSerializerFactory(context)
|
||||||
SerializationContext.UseCase.RPCServer ->
|
SerializationContext.UseCase.RPCServer ->
|
||||||
rpcServerSerializerFactory(context)
|
rpcServerSerializerFactory(context)
|
||||||
else -> sff.make(context)
|
else -> sff.make(context)
|
||||||
}.also {
|
}.also {
|
||||||
registerCustomSerializers(context, it)
|
registerCustomSerializers(context, it)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun <T : Any> deserialize(byteSequence: ByteSequence, clazz: Class<T>, context: SerializationContext): T {
|
override fun <T : Any> deserialize(byteSequence: ByteSequence, clazz: Class<T>, context: SerializationContext): T {
|
||||||
|
@ -10,7 +10,8 @@ fun createSerializerFactoryFactory(): SerializerFactoryFactory = SerializerFacto
|
|||||||
open class SerializerFactoryFactoryImpl : SerializerFactoryFactory {
|
open class SerializerFactoryFactoryImpl : SerializerFactoryFactory {
|
||||||
override fun make(context: SerializationContext): SerializerFactory {
|
override fun make(context: SerializationContext): SerializerFactory {
|
||||||
return SerializerFactoryBuilder.build(context.whitelist,
|
return SerializerFactoryBuilder.build(context.whitelist,
|
||||||
ClassCarpenterImpl(context.whitelist, context.deserializationClassLoader, context.lenientCarpenterEnabled)
|
ClassCarpenterImpl(context.whitelist, context.deserializationClassLoader, context.lenientCarpenterEnabled),
|
||||||
|
mustPreserveDataWhenEvolving = context.preventDataLoss
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package net.corda.serialization.internal.amqp
|
package net.corda.serialization.internal.amqp
|
||||||
|
|
||||||
import net.corda.core.internal.toSynchronised
|
import net.corda.core.internal.toSynchronised
|
||||||
import net.corda.core.serialization.ClassWhitelist
|
|
||||||
import net.corda.core.serialization.SerializationContext
|
import net.corda.core.serialization.SerializationContext
|
||||||
import net.corda.core.serialization.deserialize
|
import net.corda.core.serialization.deserialize
|
||||||
import net.corda.core.utilities.ByteSequence
|
import net.corda.core.utilities.ByteSequence
|
||||||
@ -40,7 +39,7 @@ class AbstractAMQPSerializationSchemeTest {
|
|||||||
|
|
||||||
val factory = SerializerFactoryBuilder.build(TESTING_CONTEXT.whitelist, TESTING_CONTEXT.deserializationClassLoader)
|
val factory = SerializerFactoryBuilder.build(TESTING_CONTEXT.whitelist, TESTING_CONTEXT.deserializationClassLoader)
|
||||||
val maxFactories = 512
|
val maxFactories = 512
|
||||||
val backingMap = AccessOrderLinkedHashMap<Pair<ClassWhitelist, ClassLoader>, SerializerFactory>({ maxFactories }).toSynchronised()
|
val backingMap = AccessOrderLinkedHashMap<SerializationFactoryCacheKey, SerializerFactory>({ maxFactories }).toSynchronised()
|
||||||
val scheme = object : AbstractAMQPSerializationScheme(emptySet(), backingMap, createSerializerFactoryFactory()) {
|
val scheme = object : AbstractAMQPSerializationScheme(emptySet(), backingMap, createSerializerFactoryFactory()) {
|
||||||
override fun rpcClientSerializerFactory(context: SerializationContext): SerializerFactory {
|
override fun rpcClientSerializerFactory(context: SerializationContext): SerializerFactory {
|
||||||
return factory
|
return factory
|
||||||
|
Loading…
Reference in New Issue
Block a user