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.
*/
val preferedSerializationVersion: ByteSequence
val preferredSerializationVersion: ByteSequence
/**
* 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 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 {
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.serializers.CompatibleFieldSerializer
import com.esotericsoftware.kryo.serializers.FieldSerializer
import com.esotericsoftware.kryo.util.MapReferenceResolver
import de.javakaffee.kryoserializers.ArraysAsListSerializer
import de.javakaffee.kryoserializers.BitSetSerializer
import de.javakaffee.kryoserializers.UnmodifiableCollectionsSerializer
import de.javakaffee.kryoserializers.guava.*
import net.corda.core.contracts.PrivacySalt
import net.corda.core.crypto.composite.CompositeKey
import net.corda.core.identity.PartyAndCertificate
import net.corda.core.node.CordaPluginRegistry
@ -110,6 +110,9 @@ object DefaultKryoCustomizer {
register(NotaryChangeWireTransaction::class.java, NotaryChangeWireTransactionSerializer)
register(PartyAndCertificate::class.java, PartyAndCertificateSerializer)
// Don't deserialize PrivacySalt via its default constructor.
register(PrivacySalt::class.java, PrivacySaltSerializer)
val customization = KryoSerializationCustomization(this)
pluginRegistries.forEach { it.customizeSerialization(customization) }
}
@ -153,4 +156,18 @@ object DefaultKryoCustomizer {
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()
}
data class SerializationContextImpl(override val preferedSerializationVersion: ByteSequence,
data class SerializationContextImpl(override val preferredSerializationVersion: ByteSequence,
override val deserializationClassLoader: ClassLoader,
override val whitelist: ClassWhitelist,
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
@ -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> 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) {

View File

@ -5,6 +5,7 @@ import com.esotericsoftware.kryo.KryoSerializable
import com.esotericsoftware.kryo.io.Input
import com.esotericsoftware.kryo.io.Output
import com.google.common.primitives.Ints
import net.corda.core.contracts.PrivacySalt
import net.corda.core.crypto.*
import net.corda.core.serialization.*
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.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
@ -28,6 +31,9 @@ class KryoTests : TestDependencyInjectionBase() {
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(this)) }
@ -157,6 +163,26 @@ class KryoTests : TestDependencyInjectionBase() {
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
private object TestSingleton

View File

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

View File

@ -1,6 +1,5 @@
package net.corda.verifier
import com.esotericsoftware.kryo.pool.KryoPool
import com.typesafe.config.Config
import com.typesafe.config.ConfigFactory
import com.typesafe.config.ConfigParseOptions
@ -98,15 +97,10 @@ class Verifier {
class KryoVerifierSerializationScheme(serializationFactory: SerializationFactory) : AbstractKryoSerializationScheme(serializationFactory) {
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 {
throw UnsupportedOperationException()
}
override fun rpcServerKryoPool(context: SerializationContext): KryoPool {
throw UnsupportedOperationException()
}
override fun rpcClientKryoPool(context: SerializationContext) = throw UnsupportedOperationException()
override fun rpcServerKryoPool(context: SerializationContext) = throw UnsupportedOperationException()
}
}