mirror of
https://github.com/corda/corda.git
synced 2025-05-04 01:33:01 +00:00
Merge pull request #4963 from corda/colljos-backport-secfix-serializer
(BACKPORT) ENT-3121 restrict custom serializers
This commit is contained in:
commit
add380b135
@ -88,7 +88,6 @@ public final class net.corda.core.concurrent.ConcurrencyUtils extends java.lang.
|
|||||||
@NotNull
|
@NotNull
|
||||||
public static final String shortCircuitedTaskFailedMessage = "Short-circuited task failed:"
|
public static final String shortCircuitedTaskFailedMessage = "Short-circuited task failed:"
|
||||||
##
|
##
|
||||||
@CordaSerializable
|
|
||||||
public interface net.corda.core.concurrent.CordaFuture extends java.util.concurrent.Future
|
public interface net.corda.core.concurrent.CordaFuture extends java.util.concurrent.Future
|
||||||
public abstract void then(kotlin.jvm.functions.Function1<? super net.corda.core.concurrent.CordaFuture<V>, ? extends W>)
|
public abstract void then(kotlin.jvm.functions.Function1<? super net.corda.core.concurrent.CordaFuture<V>, ? extends W>)
|
||||||
@NotNull
|
@NotNull
|
||||||
@ -571,7 +570,6 @@ public interface net.corda.core.contracts.Contract
|
|||||||
public abstract void verify(net.corda.core.transactions.LedgerTransaction)
|
public abstract void verify(net.corda.core.transactions.LedgerTransaction)
|
||||||
##
|
##
|
||||||
@DoNotImplement
|
@DoNotImplement
|
||||||
@CordaSerializable
|
|
||||||
public final class net.corda.core.contracts.ContractAttachment extends java.lang.Object implements net.corda.core.contracts.Attachment
|
public final class net.corda.core.contracts.ContractAttachment extends java.lang.Object implements net.corda.core.contracts.Attachment
|
||||||
public <init>(net.corda.core.contracts.Attachment, String)
|
public <init>(net.corda.core.contracts.Attachment, String)
|
||||||
public <init>(net.corda.core.contracts.Attachment, String, java.util.Set<String>)
|
public <init>(net.corda.core.contracts.Attachment, String, java.util.Set<String>)
|
||||||
@ -1013,7 +1011,6 @@ public final class net.corda.core.contracts.TransactionState extends java.lang.O
|
|||||||
##
|
##
|
||||||
public final class net.corda.core.contracts.TransactionStateKt extends java.lang.Object
|
public final class net.corda.core.contracts.TransactionStateKt extends java.lang.Object
|
||||||
##
|
##
|
||||||
@CordaSerializable
|
|
||||||
public abstract class net.corda.core.contracts.TransactionVerificationException extends net.corda.core.flows.FlowException
|
public abstract class net.corda.core.contracts.TransactionVerificationException extends net.corda.core.flows.FlowException
|
||||||
public <init>(net.corda.core.crypto.SecureHash, String, Throwable)
|
public <init>(net.corda.core.crypto.SecureHash, String, Throwable)
|
||||||
@NotNull
|
@NotNull
|
||||||
@ -1390,7 +1387,6 @@ public class net.corda.core.crypto.Base58 extends java.lang.Object
|
|||||||
public static java.math.BigInteger decodeToBigInteger(String)
|
public static java.math.BigInteger decodeToBigInteger(String)
|
||||||
public static String encode(byte[])
|
public static String encode(byte[])
|
||||||
##
|
##
|
||||||
@CordaSerializable
|
|
||||||
public final class net.corda.core.crypto.CompositeKey extends java.lang.Object implements java.security.PublicKey
|
public final class net.corda.core.crypto.CompositeKey extends java.lang.Object implements java.security.PublicKey
|
||||||
public <init>(int, java.util.List, kotlin.jvm.internal.DefaultConstructorMarker)
|
public <init>(int, java.util.List, kotlin.jvm.internal.DefaultConstructorMarker)
|
||||||
public final void checkValidity()
|
public final void checkValidity()
|
||||||
@ -1746,7 +1742,6 @@ public final class net.corda.core.crypto.NullKeys extends java.lang.Object
|
|||||||
public final net.corda.core.crypto.TransactionSignature getNULL_SIGNATURE()
|
public final net.corda.core.crypto.TransactionSignature getNULL_SIGNATURE()
|
||||||
public static final net.corda.core.crypto.NullKeys INSTANCE
|
public static final net.corda.core.crypto.NullKeys INSTANCE
|
||||||
##
|
##
|
||||||
@CordaSerializable
|
|
||||||
public static final class net.corda.core.crypto.NullKeys$NullPublicKey extends java.lang.Object implements java.security.PublicKey, java.lang.Comparable
|
public static final class net.corda.core.crypto.NullKeys$NullPublicKey extends java.lang.Object implements java.security.PublicKey, java.lang.Comparable
|
||||||
public int compareTo(java.security.PublicKey)
|
public int compareTo(java.security.PublicKey)
|
||||||
@NotNull
|
@NotNull
|
||||||
@ -6225,7 +6220,6 @@ public final class net.corda.core.transactions.SignedTransaction extends java.la
|
|||||||
public final net.corda.core.transactions.SignedTransaction withAdditionalSignatures(Iterable<net.corda.core.crypto.TransactionSignature>)
|
public final net.corda.core.transactions.SignedTransaction withAdditionalSignatures(Iterable<net.corda.core.crypto.TransactionSignature>)
|
||||||
public static final net.corda.core.transactions.SignedTransaction$Companion Companion
|
public static final net.corda.core.transactions.SignedTransaction$Companion Companion
|
||||||
##
|
##
|
||||||
@CordaSerializable
|
|
||||||
public static final class net.corda.core.transactions.SignedTransaction$SignaturesMissingException extends java.security.SignatureException implements net.corda.core.CordaThrowable, net.corda.core.contracts.NamedByHash
|
public static final class net.corda.core.transactions.SignedTransaction$SignaturesMissingException extends java.security.SignatureException implements net.corda.core.CordaThrowable, net.corda.core.contracts.NamedByHash
|
||||||
public <init>(java.util.Set<? extends java.security.PublicKey>, java.util.List<String>, net.corda.core.crypto.SecureHash)
|
public <init>(java.util.Set<? extends java.security.PublicKey>, java.util.List<String>, net.corda.core.crypto.SecureHash)
|
||||||
public void addSuppressed(Throwable[])
|
public void addSuppressed(Throwable[])
|
||||||
@ -6412,7 +6406,6 @@ public final class net.corda.core.utilities.ByteArrays extends java.lang.Object
|
|||||||
@NotNull
|
@NotNull
|
||||||
public static final String toHexString(byte[])
|
public static final String toHexString(byte[])
|
||||||
##
|
##
|
||||||
@CordaSerializable
|
|
||||||
public abstract class net.corda.core.utilities.ByteSequence extends java.lang.Object implements java.lang.Comparable
|
public abstract class net.corda.core.utilities.ByteSequence extends java.lang.Object implements java.lang.Comparable
|
||||||
public <init>(byte[], int, int, kotlin.jvm.internal.DefaultConstructorMarker)
|
public <init>(byte[], int, int, kotlin.jvm.internal.DefaultConstructorMarker)
|
||||||
public int compareTo(net.corda.core.utilities.ByteSequence)
|
public int compareTo(net.corda.core.utilities.ByteSequence)
|
||||||
@ -6628,7 +6621,6 @@ public static final class net.corda.core.utilities.OpaqueBytes$Companion extends
|
|||||||
@NotNull
|
@NotNull
|
||||||
public final net.corda.core.utilities.OpaqueBytes of(byte...)
|
public final net.corda.core.utilities.OpaqueBytes of(byte...)
|
||||||
##
|
##
|
||||||
@CordaSerializable
|
|
||||||
public final class net.corda.core.utilities.OpaqueBytesSubSequence extends net.corda.core.utilities.ByteSequence
|
public final class net.corda.core.utilities.OpaqueBytesSubSequence extends net.corda.core.utilities.ByteSequence
|
||||||
public <init>(byte[], int, int)
|
public <init>(byte[], int, int)
|
||||||
@NotNull
|
@NotNull
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
package net.corda.core.concurrent
|
package net.corda.core.concurrent
|
||||||
|
|
||||||
import net.corda.core.serialization.CordaSerializable
|
|
||||||
import java.util.concurrent.CompletableFuture
|
import java.util.concurrent.CompletableFuture
|
||||||
import java.util.concurrent.Future
|
import java.util.concurrent.Future
|
||||||
|
|
||||||
@ -8,7 +7,6 @@ import java.util.concurrent.Future
|
|||||||
* Same as [Future] with additional methods to provide some of the features of [java.util.concurrent.CompletableFuture] while minimising the API surface area.
|
* Same as [Future] with additional methods to provide some of the features of [java.util.concurrent.CompletableFuture] while minimising the API surface area.
|
||||||
* In Kotlin, to avoid compile errors, whenever CordaFuture is used in a parameter or extension method receiver type, its type parameter should be specified with out variance.
|
* In Kotlin, to avoid compile errors, whenever CordaFuture is used in a parameter or extension method receiver type, its type parameter should be specified with out variance.
|
||||||
*/
|
*/
|
||||||
@CordaSerializable
|
|
||||||
interface CordaFuture<V> : Future<V> {
|
interface CordaFuture<V> : Future<V> {
|
||||||
/**
|
/**
|
||||||
* Run the given callback when this future is done, on the completion thread.
|
* Run the given callback when this future is done, on the completion thread.
|
||||||
|
@ -14,7 +14,6 @@ import java.security.PublicKey
|
|||||||
* @property additionalContracts Additional contract names contained within the JAR.
|
* @property additionalContracts Additional contract names contained within the JAR.
|
||||||
*/
|
*/
|
||||||
@KeepForDJVM
|
@KeepForDJVM
|
||||||
@CordaSerializable
|
|
||||||
class ContractAttachment private constructor(
|
class ContractAttachment private constructor(
|
||||||
val attachment: Attachment,
|
val attachment: Attachment,
|
||||||
val contract: ContractClassName,
|
val contract: ContractClassName,
|
||||||
|
@ -46,7 +46,6 @@ class AttachmentResolutionException(val hash: SecureHash) : FlowException("Attac
|
|||||||
* @property txId the Merkle root hash (identifier) of the transaction that failed verification.
|
* @property txId the Merkle root hash (identifier) of the transaction that failed verification.
|
||||||
*/
|
*/
|
||||||
@Suppress("MemberVisibilityCanBePrivate")
|
@Suppress("MemberVisibilityCanBePrivate")
|
||||||
@CordaSerializable
|
|
||||||
abstract class TransactionVerificationException(val txId: SecureHash, message: String, cause: Throwable?)
|
abstract class TransactionVerificationException(val txId: SecureHash, message: String, cause: Throwable?)
|
||||||
: FlowException("$message, transaction: $txId", cause) {
|
: FlowException("$message, transaction: $txId", cause) {
|
||||||
|
|
||||||
|
@ -29,7 +29,6 @@ import java.util.*
|
|||||||
* signatures required) to satisfy the sub-tree rooted at this node.
|
* signatures required) to satisfy the sub-tree rooted at this node.
|
||||||
*/
|
*/
|
||||||
@KeepForDJVM
|
@KeepForDJVM
|
||||||
@CordaSerializable
|
|
||||||
class CompositeKey private constructor(val threshold: Int, children: List<NodeAndWeight>) : PublicKey {
|
class CompositeKey private constructor(val threshold: Int, children: List<NodeAndWeight>) : PublicKey {
|
||||||
companion object {
|
companion object {
|
||||||
const val KEY_ALGORITHM = "COMPOSITE"
|
const val KEY_ALGORITHM = "COMPOSITE"
|
||||||
|
@ -7,7 +7,6 @@ import java.security.PublicKey
|
|||||||
|
|
||||||
@KeepForDJVM
|
@KeepForDJVM
|
||||||
object NullKeys {
|
object NullKeys {
|
||||||
@CordaSerializable
|
|
||||||
object NullPublicKey : PublicKey, Comparable<PublicKey> {
|
object NullPublicKey : PublicKey, Comparable<PublicKey> {
|
||||||
override fun getAlgorithm() = "NULL"
|
override fun getAlgorithm() = "NULL"
|
||||||
override fun getEncoded() = byteArrayOf(0)
|
override fun getEncoded() = byteArrayOf(0)
|
||||||
|
@ -65,7 +65,6 @@ class ResolveTransactionsFlow(txHashesArg: Set<SecureHash>,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@CordaSerializable
|
|
||||||
class ExcessivelyLargeTransactionGraph : FlowException()
|
class ExcessivelyLargeTransactionGraph : FlowException()
|
||||||
|
|
||||||
// TODO: Figure out a more appropriate DOS limit here, 5000 is simply a very bad guess.
|
// TODO: Figure out a more appropriate DOS limit here, 5000 is simply a very bad guess.
|
||||||
|
@ -330,6 +330,7 @@ fun <T : Any> T.serialize(serializationFactory: SerializationFactory = Serializa
|
|||||||
*/
|
*/
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
@KeepForDJVM
|
@KeepForDJVM
|
||||||
|
@CordaSerializable
|
||||||
class SerializedBytes<T : Any>(bytes: ByteArray) : OpaqueBytes(bytes) {
|
class SerializedBytes<T : Any>(bytes: ByteArray) : OpaqueBytes(bytes) {
|
||||||
companion object {
|
companion object {
|
||||||
/**
|
/**
|
||||||
|
@ -347,7 +347,6 @@ data class SignedTransaction(val txBits: SerializedBytes<CoreTransaction>,
|
|||||||
}
|
}
|
||||||
|
|
||||||
@KeepForDJVM
|
@KeepForDJVM
|
||||||
@CordaSerializable
|
|
||||||
class SignaturesMissingException(val missing: Set<PublicKey>, val descriptions: List<String>, override val id: SecureHash)
|
class SignaturesMissingException(val missing: Set<PublicKey>, val descriptions: List<String>, override val id: SecureHash)
|
||||||
: NamedByHash, SignatureException(missingSignatureMsg(missing, descriptions, id)), CordaThrowable by CordaException(missingSignatureMsg(missing, descriptions, id))
|
: NamedByHash, SignatureException(missingSignatureMsg(missing, descriptions, id)), CordaThrowable by CordaException(missingSignatureMsg(missing, descriptions, id))
|
||||||
|
|
||||||
|
@ -20,7 +20,6 @@ import javax.xml.bind.DatatypeConverter
|
|||||||
* @property offset The start position of the sequence within the byte array.
|
* @property offset The start position of the sequence within the byte array.
|
||||||
* @property size The number of bytes this sequence represents.
|
* @property size The number of bytes this sequence represents.
|
||||||
*/
|
*/
|
||||||
@CordaSerializable
|
|
||||||
@KeepForDJVM
|
@KeepForDJVM
|
||||||
sealed class ByteSequence(private val _bytes: ByteArray, val offset: Int, val size: Int) : Comparable<ByteSequence> {
|
sealed class ByteSequence(private val _bytes: ByteArray, val offset: Int, val size: Int) : Comparable<ByteSequence> {
|
||||||
/**
|
/**
|
||||||
@ -145,6 +144,7 @@ sealed class ByteSequence(private val _bytes: ByteArray, val offset: Int, val si
|
|||||||
* functionality to Java, but it won't arrive for a few years yet!
|
* functionality to Java, but it won't arrive for a few years yet!
|
||||||
*/
|
*/
|
||||||
@KeepForDJVM
|
@KeepForDJVM
|
||||||
|
@CordaSerializable
|
||||||
open class OpaqueBytes(bytes: ByteArray) : ByteSequence(bytes, 0, bytes.size) {
|
open class OpaqueBytes(bytes: ByteArray) : ByteSequence(bytes, 0, bytes.size) {
|
||||||
companion object {
|
companion object {
|
||||||
/**
|
/**
|
||||||
|
@ -175,7 +175,6 @@ class NodeAttachmentService(
|
|||||||
* this will provide an additional safety check against user error.
|
* this will provide an additional safety check against user error.
|
||||||
*/
|
*/
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
@CordaSerializable
|
|
||||||
class HashCheckingStream(val expected: SecureHash.SHA256,
|
class HashCheckingStream(val expected: SecureHash.SHA256,
|
||||||
val expectedSize: Int,
|
val expectedSize: Int,
|
||||||
input: InputStream,
|
input: InputStream,
|
||||||
|
@ -17,5 +17,4 @@ interface NetworkRegistrationService {
|
|||||||
|
|
||||||
data class CertificateResponse(val pollInterval: Duration, val certificates: List<X509Certificate>?)
|
data class CertificateResponse(val pollInterval: Duration, val certificates: List<X509Certificate>?)
|
||||||
|
|
||||||
@CordaSerializable
|
|
||||||
class CertificateRequestException(message: String) : CordaException(message)
|
class CertificateRequestException(message: String) : CordaException(message)
|
||||||
|
@ -1,11 +1,30 @@
|
|||||||
package net.corda.serialization.internal.amqp
|
package net.corda.serialization.internal.amqp
|
||||||
|
|
||||||
|
import net.corda.core.CordaThrowable
|
||||||
import net.corda.core.internal.uncheckedCast
|
import net.corda.core.internal.uncheckedCast
|
||||||
|
import net.corda.core.serialization.CordaSerializable
|
||||||
import net.corda.core.utilities.contextLogger
|
import net.corda.core.utilities.contextLogger
|
||||||
import net.corda.serialization.internal.model.DefaultCacheProvider
|
import net.corda.serialization.internal.model.DefaultCacheProvider
|
||||||
import net.corda.serialization.internal.model.TypeIdentifier
|
import net.corda.serialization.internal.model.TypeIdentifier
|
||||||
import java.lang.reflect.Type
|
import java.lang.reflect.Type
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Thrown when a [CustomSerializer] offers to serialize a type for which custom serialization is not permitted, because
|
||||||
|
* it should be handled by standard serialisation methods (or not serialised at all) and there is no valid use case for
|
||||||
|
* a custom method.
|
||||||
|
*/
|
||||||
|
class IllegalCustomSerializerException(customSerializer: AMQPSerializer<*>, clazz: Class<*>) :
|
||||||
|
Exception("Custom serializer ${customSerializer::class.qualifiedName} registered " +
|
||||||
|
"to serialize non-custom-serializable type $clazz")
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Thrown when more than one [CustomSerializer] offers to serialize the same type, which may indicate a malicious attempt
|
||||||
|
* to override already-defined behaviour.
|
||||||
|
*/
|
||||||
|
class DuplicateCustomSerializerException(serializers: List<AMQPSerializer<*>>, clazz: Class<*>) :
|
||||||
|
Exception("Multiple custom serializers " + serializers.map { it::class.qualifiedName } +
|
||||||
|
" registered to serialize type $clazz")
|
||||||
|
|
||||||
interface CustomSerializerRegistry {
|
interface CustomSerializerRegistry {
|
||||||
/**
|
/**
|
||||||
* Register a custom serializer for any type that cannot be serialized or deserialized by the default serializer
|
* Register a custom serializer for any type that cannot be serialized or deserialized by the default serializer
|
||||||
@ -14,6 +33,20 @@ interface CustomSerializerRegistry {
|
|||||||
fun register(customSerializer: CustomSerializer<out Any>)
|
fun register(customSerializer: CustomSerializer<out Any>)
|
||||||
fun registerExternal(customSerializer: CorDappCustomSerializer)
|
fun registerExternal(customSerializer: CorDappCustomSerializer)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Try to find a custom serializer for the actual class, and declared type, of a value.
|
||||||
|
*
|
||||||
|
* @param clazz The actual class to look for a custom serializer for.
|
||||||
|
* @param declaredType The declared type to look for a custom serializer for.
|
||||||
|
* @return The custom serializer handing the class, if found, or `null`.
|
||||||
|
*
|
||||||
|
* @throws IllegalCustomSerializerException If a custom serializer identifies itself as the serializer for
|
||||||
|
* a class annotated with [CordaSerializable], since all such classes should be serializable via standard object
|
||||||
|
* serialization.
|
||||||
|
*
|
||||||
|
* @throws DuplicateCustomSerializerException If more than one custom serializer identifies itself as the serializer
|
||||||
|
* for the given class, as this creates an ambiguous situation.
|
||||||
|
*/
|
||||||
fun findCustomSerializer(clazz: Class<*>, declaredType: Type): AMQPSerializer<Any>?
|
fun findCustomSerializer(clazz: Class<*>, declaredType: Type): AMQPSerializer<Any>?
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -89,28 +122,43 @@ class CachingCustomSerializerRegistry(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun doFindCustomSerializer(clazz: Class<*>, declaredType: Type): AMQPSerializer<Any>? {
|
private fun doFindCustomSerializer(clazz: Class<*>, declaredType: Type): AMQPSerializer<Any>? {
|
||||||
// e.g. Imagine if we provided a Map serializer this way, then it won't work if the declared type is
|
val declaredSuperClass = declaredType.asClass().superclass
|
||||||
// AbstractMap, only Map. Otherwise it needs to inject additional schema for a RestrictedType source of the
|
|
||||||
// super type. Could be done, but do we need it?
|
|
||||||
for (customSerializer in customSerializers) {
|
|
||||||
if (customSerializer.isSerializerFor(clazz)) {
|
|
||||||
val declaredSuperClass = declaredType.asClass().superclass
|
|
||||||
|
|
||||||
return if (declaredSuperClass == null
|
val declaredSerializers = customSerializers.mapNotNull { customSerializer ->
|
||||||
|
when {
|
||||||
|
!customSerializer.isSerializerFor(clazz) -> null
|
||||||
|
(declaredSuperClass == null
|
||||||
|| !customSerializer.isSerializerFor(declaredSuperClass)
|
|| !customSerializer.isSerializerFor(declaredSuperClass)
|
||||||
|| !customSerializer.revealSubclassesInSchema
|
|| !customSerializer.revealSubclassesInSchema) -> {
|
||||||
) {
|
|
||||||
logger.debug("action=\"Using custom serializer\", class=${clazz.typeName}, " +
|
logger.debug("action=\"Using custom serializer\", class=${clazz.typeName}, " +
|
||||||
"declaredType=${declaredType.typeName}")
|
"declaredType=${declaredType.typeName}")
|
||||||
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
customSerializer as? AMQPSerializer<Any>
|
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...
|
||||||
CustomSerializer.SubClass(clazz, uncheckedCast(customSerializer))
|
CustomSerializer.SubClass(clazz, uncheckedCast(customSerializer))
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null
|
|
||||||
|
if (declaredSerializers.isEmpty()) return null
|
||||||
|
if (declaredSerializers.size > 1) {
|
||||||
|
logger.warn("Duplicate custom serializers detected for $clazz: ${declaredSerializers.map { it::class.qualifiedName }}")
|
||||||
|
throw DuplicateCustomSerializerException(declaredSerializers, clazz)
|
||||||
|
}
|
||||||
|
if (clazz.isCustomSerializationForbidden) {
|
||||||
|
logger.warn("Illegal custom serializer detected for $clazz: ${declaredSerializers.first()::class.qualifiedName}")
|
||||||
|
throw IllegalCustomSerializerException(declaredSerializers.first(), clazz)
|
||||||
|
}
|
||||||
|
|
||||||
|
return declaredSerializers.first()
|
||||||
|
}
|
||||||
|
|
||||||
|
private val Class<*>.isCustomSerializationForbidden: Boolean get() = when {
|
||||||
|
AMQPTypeIdentifiers.isPrimitive(this) -> true
|
||||||
|
isSubClassOf(CordaThrowable::class.java) -> false
|
||||||
|
isAnnotationPresent(CordaSerializable::class.java) -> true
|
||||||
|
else -> false
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -0,0 +1,96 @@
|
|||||||
|
package net.corda.serialization.internal.amqp
|
||||||
|
|
||||||
|
import net.corda.core.CordaException
|
||||||
|
import net.corda.core.serialization.CordaSerializable
|
||||||
|
import net.corda.core.serialization.SerializationContext
|
||||||
|
import net.corda.finance.contracts.asset.Cash
|
||||||
|
import org.apache.qpid.proton.amqp.Symbol
|
||||||
|
import org.apache.qpid.proton.codec.Data
|
||||||
|
import org.junit.Test
|
||||||
|
import java.lang.reflect.Type
|
||||||
|
import kotlin.test.assertFailsWith
|
||||||
|
import kotlin.test.assertSame
|
||||||
|
|
||||||
|
class CustomSerializerRegistryTests {
|
||||||
|
|
||||||
|
private val descriptorBasedRegistry = DefaultDescriptorBasedSerializerRegistry()
|
||||||
|
private val unit = CachingCustomSerializerRegistry(descriptorBasedRegistry)
|
||||||
|
|
||||||
|
class TestCustomSerializer(descriptorString: String, private val serializerFor: (Class<*>) -> Boolean): CustomSerializer<Any>() {
|
||||||
|
override fun isSerializerFor(clazz: Class<*>): Boolean = serializerFor(clazz)
|
||||||
|
|
||||||
|
override val descriptor: Descriptor get() = throw UnsupportedOperationException()
|
||||||
|
override val schemaForDocumentation: Schema get() = throw UnsupportedOperationException()
|
||||||
|
|
||||||
|
override fun writeDescribedObject(obj: Any, data: Data, type: Type, output: SerializationOutput, context: SerializationContext) {
|
||||||
|
throw UnsupportedOperationException()
|
||||||
|
}
|
||||||
|
|
||||||
|
override val type: Type get() = Any::class.java
|
||||||
|
override val typeDescriptor: Symbol = Symbol.valueOf(descriptorString)
|
||||||
|
override fun writeClassInfo(output: SerializationOutput) {
|
||||||
|
throw UnsupportedOperationException()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun readObject(obj: Any, schemas: SerializationSchemas, input: DeserializationInput, context: SerializationContext): Any {
|
||||||
|
throw UnsupportedOperationException()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `a custom serializer cannot register to serialize a type already annotated with CordaSerializable`() {
|
||||||
|
val serializerForEverything = TestCustomSerializer("a") { true }
|
||||||
|
unit.register(serializerForEverything)
|
||||||
|
|
||||||
|
@CordaSerializable
|
||||||
|
class AnnotatedWithCordaSerializable
|
||||||
|
class NotAnnotatedWithCordaSerializable
|
||||||
|
|
||||||
|
assertSame(
|
||||||
|
serializerForEverything,
|
||||||
|
unit.find(NotAnnotatedWithCordaSerializable::class.java))
|
||||||
|
|
||||||
|
assertFailsWith<IllegalCustomSerializerException> {
|
||||||
|
unit.find(AnnotatedWithCordaSerializable::class.java)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `exception types can have custom serializers`() {
|
||||||
|
@CordaSerializable
|
||||||
|
class MyCustomException : CordaException("Custom exception annotated with @CordaSerializable")
|
||||||
|
|
||||||
|
val customExceptionSerializer = TestCustomSerializer("a") { type -> type == MyCustomException::class.java }
|
||||||
|
unit.register(customExceptionSerializer)
|
||||||
|
|
||||||
|
assertSame(
|
||||||
|
customExceptionSerializer,
|
||||||
|
unit.find(MyCustomException::class.java))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `two custom serializers cannot register to serialize the same type`() {
|
||||||
|
val weSerializeCash = TestCustomSerializer("a") { type -> type == Cash::class.java }
|
||||||
|
val weMaliciouslySerializeCash = TestCustomSerializer("b") { type -> type == Cash::class.java }
|
||||||
|
|
||||||
|
unit.run {
|
||||||
|
register(weSerializeCash)
|
||||||
|
register(weMaliciouslySerializeCash)
|
||||||
|
}
|
||||||
|
|
||||||
|
assertFailsWith<DuplicateCustomSerializerException> {
|
||||||
|
unit.find(Cash::class.java)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `primitive types cannot have custom serializers`() {
|
||||||
|
unit.register(TestCustomSerializer("a") { type -> type == Float::class.java })
|
||||||
|
|
||||||
|
assertFailsWith<IllegalCustomSerializerException> {
|
||||||
|
unit.find(Float::class.java)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun CustomSerializerRegistry.find(clazz: Class<*>): AMQPSerializer<Any> = findCustomSerializer(clazz, clazz)!!
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user