mirror of
https://github.com/corda/corda.git
synced 2025-06-18 15:18:16 +00:00
Various cleanups to AMQP and Kryo serialisation:
* PartyAndCertificate serialiser has been converted to deal with just CertPath * Removed X500Name serialiser as we no longer use the BC implementation and have our own CordaX500Name * Converted X509 cert serialiser to use Java certs and not BC X509 cert holder * Added to test to AMQP serialisation to make sure c'tor is invoked on deserialisation
This commit is contained in:
@ -21,13 +21,14 @@ import org.assertj.core.api.Assertions.assertThatThrownBy
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.rules.ExpectedException
|
||||
import org.slf4j.LoggerFactory
|
||||
import java.io.ByteArrayInputStream
|
||||
import java.io.InputStream
|
||||
import java.time.Instant
|
||||
import java.util.Collections
|
||||
import kotlin.test.*
|
||||
import java.util.*
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertNotNull
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
class KryoTests {
|
||||
@Rule
|
||||
@ -36,9 +37,6 @@ class KryoTests {
|
||||
private lateinit var factory: SerializationFactory
|
||||
private lateinit var context: SerializationContext
|
||||
|
||||
@get:Rule
|
||||
val expectedEx: ExpectedException = ExpectedException.none()
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
factory = SerializationFactoryImpl().apply { registerScheme(KryoServerSerializationScheme()) }
|
||||
@ -51,7 +49,7 @@ class KryoTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun ok() {
|
||||
fun `simple data class`() {
|
||||
val birthday = Instant.parse("1984-04-17T00:30:00.00Z")
|
||||
val mike = Person("mike", birthday)
|
||||
val bits = mike.serialize(factory, context)
|
||||
@ -59,7 +57,7 @@ class KryoTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun nullables() {
|
||||
fun `null values`() {
|
||||
val bob = Person("bob", null)
|
||||
val bits = bob.serialize(factory, context)
|
||||
assertThat(bits.deserialize(factory, context)).isEqualTo(Person("bob", null))
|
||||
@ -202,13 +200,6 @@ class KryoTests {
|
||||
assertEquals(expected, actual)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `all-zero PrivacySalt not allowed`() {
|
||||
expectedEx.expect(IllegalArgumentException::class.java)
|
||||
expectedEx.expectMessage("Privacy salt should not be all zeros.")
|
||||
PrivacySalt(ByteArray(32))
|
||||
}
|
||||
|
||||
@CordaSerializable
|
||||
private object TestSingleton
|
||||
|
||||
|
@ -2,16 +2,16 @@ package net.corda.nodeapi.internal.serialization
|
||||
|
||||
import com.esotericsoftware.kryo.Kryo
|
||||
import com.esotericsoftware.kryo.util.DefaultClassResolver
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.serialization.CordaSerializable
|
||||
import net.corda.core.serialization.deserialize
|
||||
import net.corda.core.serialization.serialize
|
||||
import net.corda.node.services.statemachine.SessionData
|
||||
import net.corda.nodeapi.internal.serialization.kryo.KryoHeaderV0_1
|
||||
import net.corda.testing.SerializationEnvironmentRule
|
||||
import net.corda.testing.amqpSpecific
|
||||
import net.corda.testing.kryoSpecific
|
||||
import net.corda.testing.SerializationEnvironmentRule
|
||||
import org.assertj.core.api.Assertions
|
||||
import org.bouncycastle.asn1.x500.X500Name
|
||||
import org.assertj.core.api.Assertions.assertThatThrownBy
|
||||
import org.junit.Assert.assertArrayEquals
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
@ -53,7 +53,7 @@ class MapsSerializationTest {
|
||||
fun `check throws for forbidden declared type`() = amqpSpecific("Such exceptions are not expected in Kryo mode.") {
|
||||
val payload = HashMap<String, String>(smallMap)
|
||||
val wrongPayloadType = WrongPayloadType(payload)
|
||||
Assertions.assertThatThrownBy { wrongPayloadType.serialize() }
|
||||
assertThatThrownBy { wrongPayloadType.serialize() }
|
||||
.isInstanceOf(IllegalArgumentException::class.java).hasMessageContaining(
|
||||
"Map type class java.util.HashMap is unstable under iteration. Suggested fix: use java.util.LinkedHashMap instead.")
|
||||
}
|
||||
@ -62,27 +62,29 @@ class MapsSerializationTest {
|
||||
data class MyKey(val keyContent: Double)
|
||||
|
||||
@CordaSerializable
|
||||
data class MyValue(val valueContent: X500Name)
|
||||
data class MyValue(val valueContent: CordaX500Name)
|
||||
|
||||
@Test
|
||||
fun `check map serialization works with custom types`() {
|
||||
val myMap = mapOf(
|
||||
MyKey(1.0) to MyValue(X500Name("CN=one")),
|
||||
MyKey(10.0) to MyValue(X500Name("CN=ten")))
|
||||
MyKey(1.0) to MyValue(CordaX500Name("OOO", "LLL", "CC")),
|
||||
MyKey(10.0) to MyValue(CordaX500Name("OO", "LL", "CC")))
|
||||
assertEqualAfterRoundTripSerialization(myMap)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `check empty map serialises as Java emptyMap`() = kryoSpecific("Specifically checks Kryo serialization") {
|
||||
val nameID = 0
|
||||
val serializedForm = emptyMap<Int, Int>().serialize()
|
||||
val output = ByteArrayOutputStream().apply {
|
||||
write(KryoHeaderV0_1.bytes)
|
||||
write(DefaultClassResolver.NAME + 2)
|
||||
write(nameID)
|
||||
write(javaEmptyMapClass.name.toAscii())
|
||||
write(Kryo.NOT_NULL.toInt())
|
||||
fun `check empty map serialises as Java emptyMap`() {
|
||||
kryoSpecific("Specifically checks Kryo serialization") {
|
||||
val nameID = 0
|
||||
val serializedForm = emptyMap<Int, Int>().serialize()
|
||||
val output = ByteArrayOutputStream().apply {
|
||||
write(KryoHeaderV0_1.bytes)
|
||||
write(DefaultClassResolver.NAME + 2)
|
||||
write(nameID)
|
||||
write(javaEmptyMapClass.name.toAscii())
|
||||
write(Kryo.NOT_NULL.toInt())
|
||||
}
|
||||
assertArrayEquals(output.toByteArray(), serializedForm.bytes)
|
||||
}
|
||||
assertArrayEquals(output.toByteArray(), serializedForm.bytes)
|
||||
}
|
||||
}
|
||||
|
@ -1,17 +1,20 @@
|
||||
@file:Suppress("unused", "MemberVisibilityCanPrivate")
|
||||
|
||||
package net.corda.nodeapi.internal.serialization.amqp
|
||||
|
||||
import net.corda.client.rpc.RPCException
|
||||
import net.corda.core.CordaRuntimeException
|
||||
import net.corda.core.contracts.*
|
||||
import net.corda.core.crypto.SecureHash
|
||||
import net.corda.core.crypto.secureRandomBytes
|
||||
import net.corda.core.flows.FlowException
|
||||
import net.corda.core.identity.AbstractParty
|
||||
import net.corda.core.internal.toX509CertHolder
|
||||
import net.corda.core.internal.AbstractAttachment
|
||||
import net.corda.core.serialization.CordaSerializable
|
||||
import net.corda.core.serialization.MissingAttachmentsException
|
||||
import net.corda.core.serialization.SerializationFactory
|
||||
import net.corda.core.transactions.LedgerTransaction
|
||||
import net.corda.client.rpc.RPCException
|
||||
import net.corda.core.contracts.*
|
||||
import net.corda.core.internal.AbstractAttachment
|
||||
import net.corda.core.serialization.MissingAttachmentsException
|
||||
import net.corda.core.utilities.OpaqueBytes
|
||||
import net.corda.nodeapi.internal.serialization.AllWhitelist
|
||||
import net.corda.nodeapi.internal.serialization.EmptyWhitelist
|
||||
import net.corda.nodeapi.internal.serialization.GeneratedAttachment
|
||||
@ -25,10 +28,8 @@ import org.apache.activemq.artemis.api.core.SimpleString
|
||||
import org.apache.qpid.proton.amqp.*
|
||||
import org.apache.qpid.proton.codec.DecoderImpl
|
||||
import org.apache.qpid.proton.codec.EncoderImpl
|
||||
import org.junit.Assert.assertArrayEquals
|
||||
import org.junit.Assert.assertNotSame
|
||||
import org.junit.Assert.assertSame
|
||||
import org.assertj.core.api.Assertions.assertThatThrownBy
|
||||
import org.assertj.core.api.Assertions.*
|
||||
import org.junit.Assert.*
|
||||
import org.junit.Ignore
|
||||
import org.junit.Test
|
||||
import java.io.ByteArrayInputStream
|
||||
@ -39,10 +40,7 @@ import java.nio.ByteBuffer
|
||||
import java.time.*
|
||||
import java.time.temporal.ChronoUnit
|
||||
import java.util.*
|
||||
import kotlin.reflect.full.declaredFunctions
|
||||
import kotlin.reflect.full.declaredMemberFunctions
|
||||
import kotlin.reflect.full.superclasses
|
||||
import kotlin.reflect.jvm.javaMethod
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertNotNull
|
||||
import kotlin.test.assertTrue
|
||||
@ -79,7 +77,6 @@ class SerializationOutputTests {
|
||||
}
|
||||
|
||||
data class Woo(val fred: Int) {
|
||||
@Suppress("unused")
|
||||
val bob = "Bob"
|
||||
}
|
||||
|
||||
@ -89,7 +86,6 @@ class SerializationOutputTests {
|
||||
|
||||
@CordaSerializable
|
||||
data class AnnotatedWoo(val fred: Int) {
|
||||
@Suppress("unused")
|
||||
val bob = "Bob"
|
||||
}
|
||||
|
||||
@ -151,6 +147,13 @@ class SerializationOutputTests {
|
||||
|
||||
data class PolymorphicProperty(val foo: FooInterface?)
|
||||
|
||||
@CordaSerializable
|
||||
class NonZeroByte(val value: Byte) {
|
||||
init {
|
||||
require(value.toInt() != 0) { "Zero not allowed" }
|
||||
}
|
||||
}
|
||||
|
||||
private inline fun <reified T : Any> serdes(obj: T,
|
||||
factory: SerializerFactory = SerializerFactory(
|
||||
AllWhitelist, ClassLoader.getSystemClassLoader()),
|
||||
@ -406,6 +409,32 @@ class SerializationOutputTests {
|
||||
serdes(obj)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `class constructor is invoked on deserialisation`() {
|
||||
val ser = SerializationOutput(SerializerFactory(AllWhitelist, ClassLoader.getSystemClassLoader()))
|
||||
val des = DeserializationInput(ser.serializerFactory)
|
||||
|
||||
val serialisedOne = ser.serialize(NonZeroByte(1)).bytes
|
||||
val serialisedTwo = ser.serialize(NonZeroByte(2)).bytes
|
||||
|
||||
// Find the index that holds the value byte
|
||||
val valueIndex = serialisedOne.zip(serialisedTwo).mapIndexedNotNull { index, (oneByte, twoByte) ->
|
||||
if (oneByte.toInt() == 1 && twoByte.toInt() == 2) index else null
|
||||
}.single()
|
||||
|
||||
val copy = serialisedTwo.clone()
|
||||
|
||||
// Double check
|
||||
copy[valueIndex] = 0x03
|
||||
assertThat(des.deserialize(OpaqueBytes(copy), NonZeroByte::class.java).value).isEqualTo(3)
|
||||
|
||||
// Now use the forbidden value
|
||||
copy[valueIndex] = 0x00
|
||||
assertThatExceptionOfType(NotSerializableException::class.java).isThrownBy {
|
||||
des.deserialize(OpaqueBytes(copy), NonZeroByte::class.java)
|
||||
}.withMessageContaining("Zero not allowed")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test custom serializers on public key`() {
|
||||
val factory = SerializerFactory(AllWhitelist, ClassLoader.getSystemClassLoader())
|
||||
@ -762,26 +791,32 @@ class SerializationOutputTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test certificate holder serialize`() {
|
||||
fun `test privacy salt serialize`() {
|
||||
serdes(PrivacySalt())
|
||||
serdes(PrivacySalt(secureRandomBytes(32)))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test X509 certificate serialize`() {
|
||||
val factory = SerializerFactory(AllWhitelist, ClassLoader.getSystemClassLoader())
|
||||
factory.register(net.corda.nodeapi.internal.serialization.amqp.custom.X509CertificateHolderSerializer)
|
||||
factory.register(net.corda.nodeapi.internal.serialization.amqp.custom.X509CertificateSerializer)
|
||||
|
||||
val factory2 = SerializerFactory(AllWhitelist, ClassLoader.getSystemClassLoader())
|
||||
factory2.register(net.corda.nodeapi.internal.serialization.amqp.custom.X509CertificateHolderSerializer)
|
||||
factory2.register(net.corda.nodeapi.internal.serialization.amqp.custom.X509CertificateSerializer)
|
||||
|
||||
val obj = BOB_IDENTITY.certificate.toX509CertHolder()
|
||||
val obj = BOB_IDENTITY.certificate
|
||||
serdes(obj, factory, factory2)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test party and certificate serialize`() {
|
||||
fun `test cert path serialize`() {
|
||||
val factory = SerializerFactory(AllWhitelist, ClassLoader.getSystemClassLoader())
|
||||
factory.register(net.corda.nodeapi.internal.serialization.amqp.custom.PartyAndCertificateSerializer(factory))
|
||||
factory.register(net.corda.nodeapi.internal.serialization.amqp.custom.CertPathSerializer(factory))
|
||||
|
||||
val factory2 = SerializerFactory(AllWhitelist, ClassLoader.getSystemClassLoader())
|
||||
factory2.register(net.corda.nodeapi.internal.serialization.amqp.custom.PartyAndCertificateSerializer(factory2))
|
||||
factory2.register(net.corda.nodeapi.internal.serialization.amqp.custom.CertPathSerializer(factory2))
|
||||
|
||||
val obj = BOB_IDENTITY
|
||||
val obj = BOB_IDENTITY.certPath
|
||||
serdes(obj, factory, factory2)
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user