mirror of
https://github.com/corda/corda.git
synced 2024-12-20 21:43:14 +00:00
Add a header to all serialised data & switch to compatibility serializer. (#294)
Add a header to all serialised data & switch to compatibility serializer
This commit is contained in:
parent
6b4950290e
commit
6d375351bd
@ -1,6 +1,8 @@
|
|||||||
package net.corda.core.serialization
|
package net.corda.core.serialization
|
||||||
|
|
||||||
import com.esotericsoftware.kryo.Kryo
|
import com.esotericsoftware.kryo.Kryo
|
||||||
|
import com.esotericsoftware.kryo.serializers.CompatibleFieldSerializer
|
||||||
|
import com.esotericsoftware.kryo.serializers.FieldSerializer
|
||||||
import com.esotericsoftware.kryo.util.MapReferenceResolver
|
import com.esotericsoftware.kryo.util.MapReferenceResolver
|
||||||
import de.javakaffee.kryoserializers.ArraysAsListSerializer
|
import de.javakaffee.kryoserializers.ArraysAsListSerializer
|
||||||
import de.javakaffee.kryoserializers.UnmodifiableCollectionsSerializer
|
import de.javakaffee.kryoserializers.UnmodifiableCollectionsSerializer
|
||||||
@ -25,9 +27,14 @@ object DefaultKryoCustomizer {
|
|||||||
ServiceLoader.load(CordaPluginRegistry::class.java).toList().filter { it.customizeSerialization(customization) }
|
ServiceLoader.load(CordaPluginRegistry::class.java).toList().filter { it.customizeSerialization(customization) }
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: move all register() to addDefaultSerializer()
|
|
||||||
fun customize(kryo: Kryo): Kryo {
|
fun customize(kryo: Kryo): Kryo {
|
||||||
return kryo.apply {
|
return kryo.apply {
|
||||||
|
// Store a little schema of field names in the stream the first time a class is used which increases tolerance
|
||||||
|
// for change to a class.
|
||||||
|
setDefaultSerializer(CompatibleFieldSerializer::class.java)
|
||||||
|
// Take the safest route here and allow subclasses to have fields named the same as super classes.
|
||||||
|
fieldSerializerConfig.setCachedFieldNameStrategy(FieldSerializer.CachedFieldNameStrategy.EXTENDED)
|
||||||
|
|
||||||
// Allow construction of objects using a JVM backdoor that skips invoking the constructors, if there is no
|
// Allow construction of objects using a JVM backdoor that skips invoking the constructors, if there is no
|
||||||
// no-arg constructor available.
|
// no-arg constructor available.
|
||||||
instantiatorStrategy = Kryo.DefaultInstantiatorStrategy(StdInstantiatorStrategy())
|
instantiatorStrategy = Kryo.DefaultInstantiatorStrategy(StdInstantiatorStrategy())
|
||||||
|
@ -76,10 +76,19 @@ class SerializedBytes<T : Any>(bytes: ByteArray, val internalOnly: Boolean = fal
|
|||||||
fun writeToFile(path: Path): Path = Files.write(path, bytes)
|
fun writeToFile(path: Path): Path = Files.write(path, bytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// "corda" + majorVersionByte + minorVersionMSB + minorVersionLSB
|
||||||
|
private val KryoHeaderV0_1: OpaqueBytes = OpaqueBytes("corda\u0000\u0000\u0001".toByteArray())
|
||||||
|
|
||||||
// Some extension functions that make deserialisation convenient and provide auto-casting of the result.
|
// Some extension functions that make deserialisation convenient and provide auto-casting of the result.
|
||||||
fun <T : Any> ByteArray.deserialize(kryo: Kryo = threadLocalP2PKryo()): T {
|
fun <T : Any> ByteArray.deserialize(kryo: Kryo = threadLocalP2PKryo()): T {
|
||||||
@Suppress("UNCHECKED_CAST")
|
Input(this).use {
|
||||||
return kryo.readClassAndObject(Input(this)) as T
|
val header = OpaqueBytes(it.readBytes(8))
|
||||||
|
if (header != KryoHeaderV0_1) {
|
||||||
|
throw KryoException("Serialized bytes header does not match any known format.")
|
||||||
|
}
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
return kryo.readClassAndObject(it) as T
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun <T : Any> OpaqueBytes.deserialize(kryo: Kryo = threadLocalP2PKryo()): T {
|
fun <T : Any> OpaqueBytes.deserialize(kryo: Kryo = threadLocalP2PKryo()): T {
|
||||||
@ -114,6 +123,7 @@ object SerializedBytesSerializer : Serializer<SerializedBytes<Any>>() {
|
|||||||
fun <T : Any> T.serialize(kryo: Kryo = threadLocalP2PKryo(), internalOnly: Boolean = false): SerializedBytes<T> {
|
fun <T : Any> T.serialize(kryo: Kryo = threadLocalP2PKryo(), internalOnly: Boolean = false): SerializedBytes<T> {
|
||||||
val stream = ByteArrayOutputStream()
|
val stream = ByteArrayOutputStream()
|
||||||
Output(stream).use {
|
Output(stream).use {
|
||||||
|
it.writeBytes(KryoHeaderV0_1.bytes)
|
||||||
kryo.writeClassAndObject(it, this)
|
kryo.writeClassAndObject(it, this)
|
||||||
}
|
}
|
||||||
return SerializedBytes(stream.toByteArray(), internalOnly)
|
return SerializedBytes(stream.toByteArray(), internalOnly)
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
package net.corda.core.flows
|
package net.corda.core.flows
|
||||||
|
|
||||||
import com.esotericsoftware.kryo.io.Input
|
|
||||||
import com.pholser.junit.quickcheck.From
|
import com.pholser.junit.quickcheck.From
|
||||||
import com.pholser.junit.quickcheck.Property
|
import com.pholser.junit.quickcheck.Property
|
||||||
import com.pholser.junit.quickcheck.generator.GenerationStatus
|
import com.pholser.junit.quickcheck.generator.GenerationStatus
|
||||||
@ -8,7 +7,7 @@ import com.pholser.junit.quickcheck.generator.Generator
|
|||||||
import com.pholser.junit.quickcheck.random.SourceOfRandomness
|
import com.pholser.junit.quickcheck.random.SourceOfRandomness
|
||||||
import com.pholser.junit.quickcheck.runner.JUnitQuickcheck
|
import com.pholser.junit.quickcheck.runner.JUnitQuickcheck
|
||||||
import net.corda.contracts.testing.SignedTransactionGenerator
|
import net.corda.contracts.testing.SignedTransactionGenerator
|
||||||
import net.corda.core.serialization.createKryo
|
import net.corda.core.serialization.deserialize
|
||||||
import net.corda.core.serialization.serialize
|
import net.corda.core.serialization.serialize
|
||||||
import net.corda.flows.BroadcastTransactionFlow.NotifyTxRequest
|
import net.corda.flows.BroadcastTransactionFlow.NotifyTxRequest
|
||||||
import org.junit.runner.RunWith
|
import org.junit.runner.RunWith
|
||||||
@ -25,9 +24,8 @@ class BroadcastTransactionFlowTest {
|
|||||||
|
|
||||||
@Property
|
@Property
|
||||||
fun serialiseDeserialiseOfNotifyMessageWorks(@From(NotifyTxRequestMessageGenerator::class) message: NotifyTxRequest) {
|
fun serialiseDeserialiseOfNotifyMessageWorks(@From(NotifyTxRequestMessageGenerator::class) message: NotifyTxRequest) {
|
||||||
val kryo = createKryo()
|
|
||||||
val serialized = message.serialize().bytes
|
val serialized = message.serialize().bytes
|
||||||
val deserialized = kryo.readClassAndObject(Input(serialized))
|
val deserialized = serialized.deserialize<NotifyTxRequest>()
|
||||||
assertEquals(deserialized, message)
|
assertEquals(deserialized, message)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,6 @@ package net.corda.node.services.vault.schemas
|
|||||||
import io.requery.*
|
import io.requery.*
|
||||||
import net.corda.core.node.services.Vault
|
import net.corda.core.node.services.Vault
|
||||||
import net.corda.core.schemas.requery.Requery
|
import net.corda.core.schemas.requery.Requery
|
||||||
import net.corda.core.schemas.requery.converters.InstantConverter
|
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
|
|
||||||
object VaultSchema {
|
object VaultSchema {
|
||||||
@ -50,7 +49,7 @@ object VaultSchema {
|
|||||||
|
|
||||||
/** refers to serialized transaction Contract State */
|
/** refers to serialized transaction Contract State */
|
||||||
// TODO: define contract state size maximum size and adjust length accordingly
|
// TODO: define contract state size maximum size and adjust length accordingly
|
||||||
@get:Column(name = "contract_state", length = 10000)
|
@get:Column(name = "contract_state", length = 100000)
|
||||||
var contractState: ByteArray
|
var contractState: ByteArray
|
||||||
|
|
||||||
/** state lifecycle: unconsumed, consumed */
|
/** state lifecycle: unconsumed, consumed */
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"fixedLeg": {
|
"fixedLeg": {
|
||||||
"fixedRatePayer": "2eFzn8gRQq7nNgypMCjKik4w8i565TM3xBmp85eefhG1c24VSj5",
|
"fixedRatePayer": "3ThWzJauCq7qLrcX4KuKHxKnxZ6HoxnxU7pFL1HwfCkCJLUfTJ9zN92oxRLxnw",
|
||||||
"notional": {
|
"notional": {
|
||||||
"quantity": 2500000000,
|
"quantity": 2500000000,
|
||||||
"token": "USD"
|
"token": "USD"
|
||||||
@ -25,7 +25,7 @@
|
|||||||
"interestPeriodAdjustment": "Adjusted"
|
"interestPeriodAdjustment": "Adjusted"
|
||||||
},
|
},
|
||||||
"floatingLeg": {
|
"floatingLeg": {
|
||||||
"floatingRatePayer": "2eFzn8gJj7xcdBxExC7XEiiX36dw6HfG3MCpjMt2CaejwUnfAxb",
|
"floatingRatePayer": "3ThWzJauCq7qLrcX4KndaoA3TXWtoLzUSmQ9ia8xVWxHTmVGjXXXFXm9R9Wh2T",
|
||||||
"notional": {
|
"notional": {
|
||||||
"quantity": 2500000000,
|
"quantity": 2500000000,
|
||||||
"token": "USD"
|
"token": "USD"
|
||||||
|
Loading…
Reference in New Issue
Block a user