Simple deserialisation fixes. (#1243)

* Fix typo: prefered -> preferred
* Simplify KryoVerifierSerializationScheme and resolve warning.
* Add a custom serialiser for PrivacySeed so that we can avoid invoking RNG.
This commit is contained in:
Chris Rankin
2017-08-17 11:15:04 +01:00
committed by GitHub
parent e453fcfdf1
commit 1672b4aa0a
7 changed files with 55 additions and 18 deletions

View File

@ -39,7 +39,7 @@ interface SerializationContext {
/** /**
* When serializing, use the format this header sequence represents. * When serializing, use the format this header sequence represents.
*/ */
val preferedSerializationVersion: ByteSequence val preferredSerializationVersion: ByteSequence
/** /**
* The class loader to use for deserialization. * The class loader to use for deserialization.
*/ */

View File

@ -11,7 +11,7 @@ import net.corda.nodeapi.internal.serialization.amqp.SerializationOutput
import net.corda.nodeapi.internal.serialization.amqp.SerializerFactory import net.corda.nodeapi.internal.serialization.amqp.SerializerFactory
import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.ConcurrentHashMap
internal val AMQP_ENABLED get() = SerializationDefaults.P2P_CONTEXT.preferedSerializationVersion == AmqpHeaderV1_0 internal val AMQP_ENABLED get() = SerializationDefaults.P2P_CONTEXT.preferredSerializationVersion == AmqpHeaderV1_0
abstract class AbstractAMQPSerializationScheme : SerializationScheme { abstract class AbstractAMQPSerializationScheme : SerializationScheme {
internal companion object { internal companion object {

View File

@ -6,11 +6,11 @@ import com.esotericsoftware.kryo.io.Input
import com.esotericsoftware.kryo.io.Output import com.esotericsoftware.kryo.io.Output
import com.esotericsoftware.kryo.serializers.CompatibleFieldSerializer import com.esotericsoftware.kryo.serializers.CompatibleFieldSerializer
import com.esotericsoftware.kryo.serializers.FieldSerializer import com.esotericsoftware.kryo.serializers.FieldSerializer
import com.esotericsoftware.kryo.util.MapReferenceResolver
import de.javakaffee.kryoserializers.ArraysAsListSerializer import de.javakaffee.kryoserializers.ArraysAsListSerializer
import de.javakaffee.kryoserializers.BitSetSerializer import de.javakaffee.kryoserializers.BitSetSerializer
import de.javakaffee.kryoserializers.UnmodifiableCollectionsSerializer import de.javakaffee.kryoserializers.UnmodifiableCollectionsSerializer
import de.javakaffee.kryoserializers.guava.* import de.javakaffee.kryoserializers.guava.*
import net.corda.core.contracts.PrivacySalt
import net.corda.core.crypto.composite.CompositeKey import net.corda.core.crypto.composite.CompositeKey
import net.corda.core.identity.PartyAndCertificate import net.corda.core.identity.PartyAndCertificate
import net.corda.core.node.CordaPluginRegistry import net.corda.core.node.CordaPluginRegistry
@ -110,6 +110,9 @@ object DefaultKryoCustomizer {
register(NotaryChangeWireTransaction::class.java, NotaryChangeWireTransactionSerializer) register(NotaryChangeWireTransaction::class.java, NotaryChangeWireTransactionSerializer)
register(PartyAndCertificate::class.java, PartyAndCertificateSerializer) register(PartyAndCertificate::class.java, PartyAndCertificateSerializer)
// Don't deserialize PrivacySalt via its default constructor.
register(PrivacySalt::class.java, PrivacySaltSerializer)
val customization = KryoSerializationCustomization(this) val customization = KryoSerializationCustomization(this)
pluginRegistries.forEach { it.customizeSerialization(customization) } pluginRegistries.forEach { it.customizeSerialization(customization) }
} }
@ -153,4 +156,18 @@ object DefaultKryoCustomizer {
return list.toNonEmptySet() return list.toNonEmptySet()
} }
} }
}
/*
* Avoid deserialising PrivacySalt via its default constructor
* because the random number generator may not be available.
*/
private object PrivacySaltSerializer : Serializer<PrivacySalt>() {
override fun write(kryo: Kryo, output: Output, obj: PrivacySalt) {
output.writeBytesWithLength(obj.bytes)
}
override fun read(kryo: Kryo, input: Input, type: Class<PrivacySalt>): PrivacySalt {
return PrivacySalt(input.readBytesWithLength())
}
}
}

View File

@ -28,7 +28,7 @@ object NotSupportedSeralizationScheme : SerializationScheme {
override fun <T : Any> serialize(obj: T, context: SerializationContext): SerializedBytes<T> = doThrow() override fun <T : Any> serialize(obj: T, context: SerializationContext): SerializedBytes<T> = doThrow()
} }
data class SerializationContextImpl(override val preferedSerializationVersion: ByteSequence, data class SerializationContextImpl(override val preferredSerializationVersion: ByteSequence,
override val deserializationClassLoader: ClassLoader, override val deserializationClassLoader: ClassLoader,
override val whitelist: ClassWhitelist, override val whitelist: ClassWhitelist,
override val properties: Map<Any, Any>, override val properties: Map<Any, Any>,
@ -52,7 +52,7 @@ data class SerializationContextImpl(override val preferedSerializationVersion: B
}) })
} }
override fun withPreferredSerializationVersion(versionHeader: ByteSequence) = copy(preferedSerializationVersion = versionHeader) override fun withPreferredSerializationVersion(versionHeader: ByteSequence) = copy(preferredSerializationVersion = versionHeader)
} }
private const val HEADER_SIZE: Int = 8 private const val HEADER_SIZE: Int = 8
@ -81,7 +81,7 @@ open class SerializationFactoryImpl : SerializationFactory {
override fun <T : Any> deserialize(byteSequence: ByteSequence, clazz: Class<T>, context: SerializationContext): T = schemeFor(byteSequence, context.useCase).deserialize(byteSequence, clazz, context) override fun <T : Any> deserialize(byteSequence: ByteSequence, clazz: Class<T>, context: SerializationContext): T = schemeFor(byteSequence, context.useCase).deserialize(byteSequence, clazz, context)
override fun <T : Any> serialize(obj: T, context: SerializationContext): SerializedBytes<T> { override fun <T : Any> serialize(obj: T, context: SerializationContext): SerializedBytes<T> {
return schemeFor(context.preferedSerializationVersion, context.useCase).serialize(obj, context) return schemeFor(context.preferredSerializationVersion, context.useCase).serialize(obj, context)
} }
fun registerScheme(scheme: SerializationScheme) { fun registerScheme(scheme: SerializationScheme) {

View File

@ -5,6 +5,7 @@ import com.esotericsoftware.kryo.KryoSerializable
import com.esotericsoftware.kryo.io.Input import com.esotericsoftware.kryo.io.Input
import com.esotericsoftware.kryo.io.Output import com.esotericsoftware.kryo.io.Output
import com.google.common.primitives.Ints import com.google.common.primitives.Ints
import net.corda.core.contracts.PrivacySalt
import net.corda.core.crypto.* import net.corda.core.crypto.*
import net.corda.core.serialization.* import net.corda.core.serialization.*
import net.corda.core.utilities.ProgressTracker import net.corda.core.utilities.ProgressTracker
@ -16,7 +17,9 @@ import net.corda.testing.TestDependencyInjectionBase
import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThat
import org.assertj.core.api.Assertions.assertThatThrownBy import org.assertj.core.api.Assertions.assertThatThrownBy
import org.junit.Before import org.junit.Before
import org.junit.Rule
import org.junit.Test import org.junit.Test
import org.junit.rules.ExpectedException
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import java.io.ByteArrayInputStream import java.io.ByteArrayInputStream
import java.io.InputStream import java.io.InputStream
@ -28,6 +31,9 @@ class KryoTests : TestDependencyInjectionBase() {
private lateinit var factory: SerializationFactory private lateinit var factory: SerializationFactory
private lateinit var context: SerializationContext private lateinit var context: SerializationContext
@get:Rule
val expectedEx: ExpectedException = ExpectedException.none()
@Before @Before
fun setup() { fun setup() {
factory = SerializationFactoryImpl().apply { registerScheme(KryoServerSerializationScheme(this)) } factory = SerializationFactoryImpl().apply { registerScheme(KryoServerSerializationScheme(this)) }
@ -157,6 +163,26 @@ class KryoTests : TestDependencyInjectionBase() {
override fun toString(): String = "Cyclic($value)" override fun toString(): String = "Cyclic($value)"
} }
@Test
fun `serialize - deserialize PrivacySalt`() {
val expected = PrivacySalt(byteArrayOf(
1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
21, 22, 23, 24, 25, 26, 27, 28, 29, 30,
31, 32
))
val serializedBytes = expected.serialize(factory, context)
val actual = serializedBytes.deserialize(factory, context)
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 @CordaSerializable
private object TestSingleton private object TestSingleton

View File

@ -111,8 +111,8 @@ class TestSerializationContext : SerializationContext {
override fun toString(): String = stackTrace?.joinToString("\n") ?: "null" override fun toString(): String = stackTrace?.joinToString("\n") ?: "null"
override val preferedSerializationVersion: ByteSequence override val preferredSerializationVersion: ByteSequence
get() = delegate!!.preferedSerializationVersion get() = delegate!!.preferredSerializationVersion
override val deserializationClassLoader: ClassLoader override val deserializationClassLoader: ClassLoader
get() = delegate!!.deserializationClassLoader get() = delegate!!.deserializationClassLoader
override val whitelist: ClassWhitelist override val whitelist: ClassWhitelist

View File

@ -1,6 +1,5 @@
package net.corda.verifier package net.corda.verifier
import com.esotericsoftware.kryo.pool.KryoPool
import com.typesafe.config.Config import com.typesafe.config.Config
import com.typesafe.config.ConfigFactory import com.typesafe.config.ConfigFactory
import com.typesafe.config.ConfigParseOptions import com.typesafe.config.ConfigParseOptions
@ -98,15 +97,10 @@ class Verifier {
class KryoVerifierSerializationScheme(serializationFactory: SerializationFactory) : AbstractKryoSerializationScheme(serializationFactory) { class KryoVerifierSerializationScheme(serializationFactory: SerializationFactory) : AbstractKryoSerializationScheme(serializationFactory) {
override fun canDeserializeVersion(byteSequence: ByteSequence, target: SerializationContext.UseCase): Boolean { override fun canDeserializeVersion(byteSequence: ByteSequence, target: SerializationContext.UseCase): Boolean {
return byteSequence.equals(KryoHeaderV0_1) && target == SerializationContext.UseCase.P2P return byteSequence == KryoHeaderV0_1 && target == SerializationContext.UseCase.P2P
} }
override fun rpcClientKryoPool(context: SerializationContext): KryoPool { override fun rpcClientKryoPool(context: SerializationContext) = throw UnsupportedOperationException()
throw UnsupportedOperationException() override fun rpcServerKryoPool(context: SerializationContext) = throw UnsupportedOperationException()
}
override fun rpcServerKryoPool(context: SerializationContext): KryoPool {
throw UnsupportedOperationException()
}
} }
} }