diff --git a/core/src/main/kotlin/net/corda/core/crypto/CompositeKey.kt b/core/src/main/kotlin/net/corda/core/crypto/CompositeKey.kt index cdf400e7ea..84e8e2acf5 100644 --- a/core/src/main/kotlin/net/corda/core/crypto/CompositeKey.kt +++ b/core/src/main/kotlin/net/corda/core/crypto/CompositeKey.kt @@ -124,9 +124,8 @@ sealed class CompositeKey { * Builds the [CompositeKey.Node]. If [threshold] is not specified, it will default to * the size of the children, effectively generating an "N of N" requirement. */ - fun build(threshold: Int? = null): CompositeKey { - return if (children.size == 1) children.first() - else Node(threshold ?: children.size, children.toList(), weights.toList()) + fun build(threshold: Int? = null): CompositeKey.Node { + return Node(threshold ?: children.size, children.toList(), weights.toList()) } } diff --git a/core/src/main/kotlin/net/corda/core/serialization/Kryo.kt b/core/src/main/kotlin/net/corda/core/serialization/Kryo.kt index 999b7b29bc..0834ca2b2f 100644 --- a/core/src/main/kotlin/net/corda/core/serialization/Kryo.kt +++ b/core/src/main/kotlin/net/corda/core/serialization/Kryo.kt @@ -31,6 +31,7 @@ import java.io.ObjectOutputStream import java.lang.reflect.InvocationTargetException import java.nio.file.Files import java.nio.file.Path +import java.security.PublicKey import java.time.Instant import java.util.* import javax.annotation.concurrent.ThreadSafe @@ -303,6 +304,41 @@ object Ed25519PublicKeySerializer : Serializer() { } } +/** For serialising composite keys */ +@ThreadSafe +object CompositeKeyLeafSerializer : Serializer() { + override fun write(kryo: Kryo, output: Output, obj: CompositeKey.Leaf) { + val key = obj.publicKey + kryo.writeClassAndObject(output, key) + } + + override fun read(kryo: Kryo, input: Input, type: Class): CompositeKey.Leaf { + val key = kryo.readClassAndObject(input) as PublicKey + return CompositeKey.Leaf(key) + } +} + +@ThreadSafe +object CompositeKeyNodeSerializer : Serializer() { + override fun write(kryo: Kryo, output: Output, obj: CompositeKey.Node) { + output.writeInt(obj.threshold) + output.writeInt(obj.children.size) + obj.children.forEach { kryo.writeClassAndObject(output, it) } + output.writeInts(obj.weights.toIntArray()) + } + + override fun read(kryo: Kryo, input: Input, type: Class): CompositeKey.Node { + val threshold = input.readInt() + val childCount = input.readInt() + val children = (1..childCount).map { kryo.readClassAndObject(input) as CompositeKey } + val weights = input.readInts(childCount) + + val builder = CompositeKey.Builder() + weights.zip(children).forEach { builder.addKey(it.second, it.first) } + return builder.build(threshold) + } +} + /** Marker interface for kotlin object definitions so that they are deserialized as the singleton instance. */ interface DeserializeAsKotlinObjectDef @@ -344,6 +380,10 @@ fun createKryo(k: Kryo = Kryo()): Kryo { register(keyPair.private.javaClass, Ed25519PrivateKeySerializer) register(Instant::class.java, ReferencesAwareJavaSerializer) + // Using a custom serializer for compactness + register(CompositeKey.Node::class.java, CompositeKeyNodeSerializer) + register(CompositeKey.Leaf::class.java, CompositeKeyLeafSerializer) + // Some classes have to be handled with the ImmutableClassSerializer because they need to have their // constructors be invoked (typically for lazy members). register(SignedTransaction::class.java, ImmutableClassSerializer(SignedTransaction::class))