CORDA-2876: Integrate the DJVM with the Corda Node. (#5633)

* * CORDA-2876: Migrate DJVM serialization modules into Corda.
* Pre-generate Corda classes for DJVM sandbox when node boots in production mode.
* Ensure that all DJVM test contract CorDapps are signed.
* Test examining attachments within DJVM sandbox.
* Test Contract.verify() using cryptographic verify function.
* Add test cases for more non-determinism in Contract.verify().
* Update node-driver to support testing nodes with DJVM support.
* Modify Node to allow alternative DJVM configurations for testing.
* Refactor DeterministicVerifierFactoryService for default use-case.
* Small whitespace and code-style refactors.
* Create and activate a DJVM execution profile for the Node.
* Revert making Verifier implement AutoCloseable.
* Allow the node to cache sandboxed Corda byte-code for reuse.
* Use updated Quasar agent that knows not to touch DJVM classloaders.
* Fix Quasar's package exclusions globs for DJVM.
* Deserialise LedgerTransaction into the sandbox for Contract.verify().
* Add the DJVM's serialisation modules to the Corda node.
* Update the node for the latest DJVM API, and preserve the ConstructorForDeserialization annotation on user contract classes.
* Add corda-dev to repositories while DJVM is SNAPSHOT.
* Migrate DJVM specialisation into AbstractNode's ServiceHubInternalImpl.
* Exclude sandbox.** and shaded djvm.** classes from Quasar agent.
* Add the corda-dev repository to :node for the deterministic runtime.
* Turn Verifier into an abstract base class that is specialised by BasicVerifier and DeterministicVerifier.
* Add the Corda deterministic libraries to the Node, and split the DJVM sandbox across two SandboxClassLoader instances.
* Add DJVM to contract verification path inside Corda Node.
* Minor lambda simplifications and removing unused import.
* CORDA-2871: Remove @CordaSerializable from LedgerTransaction.
* CORDA-2871: Add a callback to ServicesForResolution to allow the Node to modify a LedgerTransaction object.
* CORDA-2871: Refactor the contract verification code into a separate class,
 and allow LedgerTransaction to choose different Verifier objects.
* Update DJVM to use Corda 4.4-SNAPSHOT. (#95)
* CORDA-3330: Allow DJVM to preload / pregenerate classes from selected jars. (#92)
* Add support for SourceClassLoader.getResources() to DJVM.
* Allow a SandboxConfiguration to preload sandbox byte-code for all classes inside jars containing META-INF/DJVM-preload.
* CORDA-3309: Remove explicit try-catch in favour of UncaughtExceptionHandler. (#91)
* CORDA-3309: Install UncaughtExceptionHandler for DJVM tasks. (#88)
* Fix tests broken by Windows line endings. (#82)
* CORDA-3292: Reimplement ExecutionProfile as a data class. (#80)
* CORDA-2877: Refactor how we create child SandboxConfiguration objects. (#76)
* CORDA-2877: Load bytecode from a persistent cache to prevent repeated rewriting. (#75)
* Refactor byte-code cache to SandboxConfiguration instead of AnalysisConfiguration. We cannot "mix and match" byte-code generated by different sets of rules.
* CORDA-3137: Enhance annotation handling so that we can allow some annotations to be mapped into the sandbox without also needing to be stitched. (#72)
* CORDA-2871: Minor cosmetic fixes. (#69)
* CORDA-3218: Align DJVM with internal Corda Serialisation API. (#68)
* Ensure we get the latest SNAPSHOT of the serialisation code.
* CORDA-2871: Refactor SourceClassLoader to define source classes. (#66)
* Rewrite SourceClassLoader to support parent/child relationships.
* Revert catching TypNotPresebtException - it was a symptom of a bigger problem.
* Remove AutoCloseable from AnalysisConfiguration and SourceClassLoader.
* SourceClassLoader.getResource() must delegate to its parent first.
* CORDA-2871: Ensure ClassLoader.loadClass() throws ClassNotFoundException for all cases where the class cannot be found. (#64)
* CORDA-2871: Modify sandbox tasks to implement both java.Function and sandbox.Function (#62)
* Make TaskExecutors implement BiFunction to make them composable.
* Create ImportTask to wrap a java.Function inside a sandbox.Function.
* Add createExecutor() and createRawExecutor() APIs to SandboxClassLoader.
* Update serialization to use SandboxClassLoader.toSandboxClass().
* Remove a layer of lambdas from the serialisation code.
* Update SandboxExecutor and SandboxRawExecutor.
* Rename Executor to TaskFactory.
* Rename dangling executor -> taskFactory.
* CORDA-2871: Sanity fixes! (#63)
* Improve message for SandboxClassLoadingException.
* Fix serialisation API for using sandboxed environment.
* CORDA-3174: Extend serialisation to include InputStream and OpaqueBytesSubSequence. (#60)
* Update DJVM Example project for serialisation.
* Add serializers for InputStream and OpaqueBytesSubSequence.
* Support ZIP Inflater and CRC32 inside the sandbox.
* Allow the DJVM to wrap java.io.InputStream as sandbox.java.io.InputStream.
* Configure tests also to preserve @DeprecatedConstructorForDeserialization.
* CORDA-3174: Implement Corda serialization modules. (#59)
* Create DJVM serialization modules.
* Create test cases for Array<T>, List<T> and List<Array<T>>.
* Refactor SandboxPrimiveSerializer for all primitive types.
* Implement SandboxCollectionSerializer to support Collection types.
* Implement SandboxMapSerializer to support Map types.
* Attempt to fix infinite loop when computing Collection and Map fingerprints.
* Apply special handling when deserialising sandbox.java.lang.Character.
* Remap Java primitive types to sandbox Java object types to deter evolution.
* Use Class.getPackage().getName() to determine sandbox package name.
* Implement SandboxEnumSerializer to support Enum types.
* Implement SandboxPublicKeySerializer to support Java security keys.
* Add serialization projects to the composite example project.
* Implement serializers for BigInteger, BigDecimal, Currency and StringBuffer.
* Test that deserialising does not instantiate the untrusted user classes.
* Implement serializers for java.time.* types.
* Add serialiser for BitSet - currently disabled until BitSet itself is supported.
* Add serialisers for EnumSet and Class.
* Include support for EnumMap in the SandboxMapSerializer.
* Ensure the DJVM Example project's tests preserve @CordaSerializable.
* Add support for UUID as a primitive type.
* Use common abortReadOnly() method for declaring serialization as unsupported.
* Streamline the API for deserialising into the sandbox.
* Add preliminary support for deserialising X.509 certificates.
* Implement serializer for java.util.Optional.
* Refactor configuration of the sandbox serialization scheme.
* Add tests for deserialising arrays of basic types.
* Include method annotations in annotation stitching. This ensures that `@ConstructorForDeserialization` is not dropped.
* Enable test for SandboxBitSetSerializer.
* Enable tests for X.509 serializers.
* Implement serializers for ProtonJ primitive types.
* Serialize java.util.Date as a primitive type.
* Add the bintray Gradle plugin to the serialisation modules.
* Do not publish serialisation modules - they will become part of Corda itself.

* CORDA-2876: Only apply DJVM sources to Node Driver when devMode=true.

* Resolve DeteKT warnings.

* Require Node's JVM to set -Dnet.corda.djvm=true in order to enable DJVM.

* Enable DJVM for DemoBench nodes.

* Disable Quasar instrumentation verification for DemoBench nodes.

* Upgrade to DJVM 1.0-RC01.

* Try to modify DriverParameters in a more "ABI friendly" way.

* Refactor and simplify sandbox deserialisation of primitive objects.

* Review fixes.

* Update EvolutionSerializerFactory to handle sandboxed primitive boxed types.
This commit is contained in:
Chris Rankin
2019-11-05 13:44:18 +00:00
committed by Matthew Nesbit
parent f9d8084d44
commit f226ddc4f2
189 changed files with 7862 additions and 161 deletions

View File

@ -0,0 +1,69 @@
plugins {
id 'org.jetbrains.kotlin.jvm'
id 'net.corda.plugins.publish-utils'
id 'com.jfrog.artifactory'
id 'java-library'
id 'idea'
}
description 'Serialization support for the DJVM'
configurations {
sandboxTesting
jdkRt.resolutionStrategy {
// Always check the repository for a newer SNAPSHOT.
cacheChangingModulesFor 0, 'seconds'
}
}
dependencies {
api project(':core')
api project(':serialization')
api "net.corda.djvm:corda-djvm:$djvm_version"
implementation(project(':serialization-djvm:deserializers')) {
transitive = false
}
testImplementation "org.junit.jupiter:junit-jupiter-api:$junit_jupiter_version"
testImplementation "org.junit.jupiter:junit-jupiter-params:$junit_jupiter_version"
testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:$junit_jupiter_version"
// Test utilities
testImplementation "org.assertj:assertj-core:$assertj_version"
testRuntimeOnly "org.apache.logging.log4j:log4j-slf4j-impl:$log4j_version"
jdkRt "net.corda:deterministic-rt:latest.integration"
// The DJVM will need this classpath to run the unit tests.
sandboxTesting files(sourceSets.getByName("test").output)
sandboxTesting project(':serialization-djvm:deserializers')
sandboxTesting project(path: ':serialization-deterministic', configuration: 'deterministicArtifacts')
sandboxTesting "org.slf4j:slf4j-nop:$slf4j_version"
}
jar {
archiveBaseName = 'corda-serialization-djvm'
manifest {
attributes('Automatic-Module-Name': 'net.corda.serialization.djvm')
attributes('Sealed': true)
}
}
tasks.withType(Test) {
useJUnitPlatform()
systemProperty 'deterministic-rt.path', configurations.jdkRt.asPath
systemProperty 'sandbox-libraries.path', configurations.sandboxTesting.asPath
// Configure the host timezone to match the DJVM's.
systemProperty 'user.timezone', 'UTC'
}
publish {
name jar.archiveBaseName.get()
}
idea {
module {
downloadJavadoc = true
downloadSources = true
}
}

View File

@ -0,0 +1,34 @@
plugins {
id 'org.jetbrains.kotlin.jvm'
id 'net.corda.plugins.publish-utils'
id 'com.jfrog.artifactory'
id 'java-library'
id 'idea'
}
apply from: '../../deterministic.gradle'
description 'Deserializers for the DJVM'
dependencies {
api project(path: ':core-deterministic', configuration: 'deterministicArtifacts')
api project(path: ':serialization-deterministic', configuration: 'deterministicArtifacts')
}
jar {
archiveBaseName = 'corda-deserializers-djvm'
manifest {
attributes('Automatic-Module-Name': 'net.corda.serialization.djvm.deserializers')
attributes('Sealed': true)
}
}
publish {
name jar.archiveBaseName.get()
}
idea {
module {
downloadJavadoc = true
downloadSources = true
}
}

View File

@ -0,0 +1,11 @@
package net.corda.serialization.djvm.deserializers
import net.corda.serialization.internal.amqp.custom.BitSetSerializer.BitSetProxy
import java.util.BitSet
import java.util.function.Function
class BitSetDeserializer : Function<BitSetProxy, BitSet> {
override fun apply(proxy: BitSetProxy): BitSet {
return BitSet.valueOf(proxy.bytes)
}
}

View File

@ -0,0 +1,13 @@
package net.corda.serialization.djvm.deserializers
import net.corda.serialization.internal.amqp.custom.CertPathSerializer.CertPathProxy
import java.security.cert.CertPath
import java.security.cert.CertificateFactory
import java.util.function.Function
class CertPathDeserializer : Function<CertPathProxy, CertPath> {
override fun apply(proxy: CertPathProxy): CertPath {
val factory = CertificateFactory.getInstance(proxy.type)
return factory.generateCertPath(proxy.encoded.inputStream())
}
}

View File

@ -0,0 +1,11 @@
package net.corda.serialization.djvm.deserializers
import net.corda.serialization.internal.amqp.custom.ClassSerializer.ClassProxy
import java.util.function.Function
class ClassDeserializer : Function<ClassProxy, Class<*>> {
override fun apply(proxy: ClassProxy): Class<*> {
return Class.forName(proxy.className)
}
}

View File

@ -0,0 +1,50 @@
package net.corda.serialization.djvm.deserializers
import net.corda.core.utilities.NonEmptySet
import java.util.Collections
import java.util.NavigableSet
import java.util.SortedSet
import java.util.TreeSet
import java.util.function.Function
class CreateCollection : Function<Array<Any?>, Collection<Any?>> {
private val concreteConstructors: Map<Class<out Collection<*>>, (Array<Any?>) -> Collection<Any?>> = mapOf(
List::class.java to ::createList,
Set::class.java to ::createSet,
SortedSet::class.java to ::createSortedSet,
NavigableSet::class.java to ::createNavigableSet,
Collection::class.java to ::createCollection,
NonEmptySet::class.java to ::createNonEmptySet
)
private fun createList(values: Array<Any?>): List<Any?> {
return Collections.unmodifiableList(values.toCollection(ArrayList()))
}
private fun createSet(values: Array<Any?>): Set<Any?> {
return Collections.unmodifiableSet(values.toCollection(LinkedHashSet()))
}
private fun createSortedSet(values: Array<Any?>): SortedSet<Any?> {
return Collections.unmodifiableSortedSet(values.toCollection(TreeSet()))
}
private fun createNavigableSet(values: Array<Any?>): NavigableSet<Any?> {
return Collections.unmodifiableNavigableSet(values.toCollection(TreeSet()))
}
private fun createCollection(values: Array<Any?>): Collection<Any?> {
return Collections.unmodifiableCollection(values.toCollection(ArrayList()))
}
private fun createNonEmptySet(values: Array<Any?>): NonEmptySet<Any?> {
return NonEmptySet.copyOf(values.toCollection(ArrayList()))
}
@Suppress("unchecked_cast")
override fun apply(inputs: Array<Any?>): Collection<Any?> {
val collectionClass = inputs[0] as Class<out Collection<Any?>>
val args = inputs[1] as Array<Any?>
return concreteConstructors[collectionClass]?.invoke(args)!!
}
}

View File

@ -0,0 +1,10 @@
package net.corda.serialization.djvm.deserializers
import java.util.Currency
import java.util.function.Function
class CreateCurrency : Function<String, Currency> {
override fun apply(currencyCode: String): Currency {
return Currency.getInstance(currencyCode)
}
}

View File

@ -0,0 +1,10 @@
package net.corda.serialization.djvm.deserializers
import java.lang.reflect.Constructor
import java.util.function.Function
class CreateFromString(private val factory: Constructor<Any>) : Function<String, Any> {
override fun apply(text: String): Any {
return factory.newInstance(text)
}
}

View File

@ -0,0 +1,52 @@
package net.corda.serialization.djvm.deserializers
import java.util.Collections
import java.util.EnumMap
import java.util.NavigableMap
import java.util.SortedMap
import java.util.TreeMap
import java.util.function.Function
class CreateMap : Function<Array<Any>, Map<Any?, Any?>> {
private val concreteConstructors: Map<Class<out Map<*, *>>, (Array<Array<Any>>) -> Map<Any?, Any?>> = mapOf(
Map::class.java to ::createMap,
SortedMap::class.java to ::createSortedMap,
LinkedHashMap::class.java to ::createLinkedHashMap,
NavigableMap::class.java to ::createNavigableMap,
TreeMap::class.java to ::createTreeMap,
EnumMap::class.java to ::createEnumMap
)
private fun createMap(values: Array<Array<Any>>): Map<Any?, Any?> {
return Collections.unmodifiableMap(values.map { it[0] to it[1] }.toMap())
}
private fun createSortedMap(values: Array<Array<Any>>): SortedMap<Any?, out Any?> {
return Collections.unmodifiableSortedMap(createTreeMap(values))
}
private fun createNavigableMap(values: Array<Array<Any>>): NavigableMap<Any?, out Any?> {
return Collections.unmodifiableNavigableMap(createTreeMap(values))
}
private fun createLinkedHashMap(values: Array<Array<Any>>): LinkedHashMap<Any?, out Any?> {
return values.map { it[0] to it[1] }.toMap(LinkedHashMap())
}
private fun createTreeMap(values: Array<Array<Any>>): TreeMap<Any?, out Any?> {
return values.map { it[0] to it[1] }.toMap(TreeMap())
}
private fun createEnumMap(values: Array<Array<Any>>): Map<Any?, Any?> {
val map = values.map { it[0] to it[1] }.toMap()
@Suppress("unchecked_cast")
return EnumMap(map as Map<JustForCasting, Any?>) as Map<Any?, Any?>
}
@Suppress("unchecked_cast")
override fun apply(inputs: Array<Any>): Map<Any?, Any?> {
val mapClass = inputs[0] as Class<out Map<Any?, Any?>>
val args = inputs[1] as Array<Array<Any>>
return concreteConstructors[mapClass]?.invoke(args)!!
}
}

View File

@ -0,0 +1,10 @@
package net.corda.serialization.djvm.deserializers
import org.apache.qpid.proton.amqp.Decimal128
import java.util.function.Function
class Decimal128Deserializer : Function<LongArray, Decimal128> {
override fun apply(underlying: LongArray): Decimal128 {
return Decimal128(underlying[0], underlying[1])
}
}

View File

@ -0,0 +1,10 @@
package net.corda.serialization.djvm.deserializers
import org.apache.qpid.proton.amqp.Decimal32
import java.util.function.Function
class Decimal32Deserializer : Function<IntArray, Decimal32> {
override fun apply(underlying: IntArray): Decimal32 {
return Decimal32(underlying[0])
}
}

View File

@ -0,0 +1,10 @@
package net.corda.serialization.djvm.deserializers
import org.apache.qpid.proton.amqp.Decimal64
import java.util.function.Function
class Decimal64Deserializer : Function<LongArray, Decimal64> {
override fun apply(underlying: LongArray): Decimal64 {
return Decimal64(underlying[0])
}
}

View File

@ -0,0 +1,9 @@
package net.corda.serialization.djvm.deserializers
import java.util.function.Function
class DescribeEnum : Function<Class<*>, Array<out Any?>> {
override fun apply(enumClass: Class<*>): Array<out Any?> {
return enumClass.enumConstants
}
}

View File

@ -0,0 +1,11 @@
package net.corda.serialization.djvm.deserializers
import net.corda.serialization.internal.amqp.custom.DurationSerializer.DurationProxy
import java.time.Duration
import java.util.function.Function
class DurationDeserializer : Function<DurationProxy, Duration> {
override fun apply(proxy: DurationProxy): Duration {
return Duration.ofSeconds(proxy.seconds, proxy.nanos.toLong())
}
}

View File

@ -0,0 +1,16 @@
package net.corda.serialization.djvm.deserializers
import net.corda.core.internal.uncheckedCast
import net.corda.serialization.internal.amqp.custom.EnumSetSerializer.EnumSetProxy
import java.util.EnumSet
import java.util.function.Function
class EnumSetDeserializer : Function<EnumSetProxy, EnumSet<*>> {
override fun apply(proxy: EnumSetProxy): EnumSet<*> {
return if (proxy.elements.isEmpty()) {
EnumSet.noneOf(uncheckedCast<Class<*>, Class<JustForCasting>>(proxy.clazz))
} else {
EnumSet.copyOf(uncheckedCast<List<Any>, List<JustForCasting>>(proxy.elements))
}
}
}

View File

@ -0,0 +1,11 @@
package net.corda.serialization.djvm.deserializers
import java.io.ByteArrayInputStream
import java.io.InputStream
import java.util.function.Function
class InputStreamDeserializer : Function<ByteArray, InputStream?> {
override fun apply(bytes: ByteArray): InputStream? {
return ByteArrayInputStream(bytes)
}
}

View File

@ -0,0 +1,11 @@
package net.corda.serialization.djvm.deserializers
import net.corda.serialization.internal.amqp.custom.InstantSerializer.InstantProxy
import java.time.Instant
import java.util.function.Function
class InstantDeserializer : Function<InstantProxy, Instant> {
override fun apply(proxy: InstantProxy): Instant {
return Instant.ofEpochSecond(proxy.epochSeconds, proxy.nanos.toLong())
}
}

View File

@ -0,0 +1,6 @@
package net.corda.serialization.djvm.deserializers
@Suppress("unused")
enum class JustForCasting {
UNUSED
}

View File

@ -0,0 +1,11 @@
package net.corda.serialization.djvm.deserializers
import net.corda.serialization.internal.amqp.custom.LocalDateSerializer.LocalDateProxy
import java.time.LocalDate
import java.util.function.Function
class LocalDateDeserializer : Function<LocalDateProxy, LocalDate> {
override fun apply(proxy: LocalDateProxy): LocalDate {
return LocalDate.of(proxy.year, proxy.month.toInt(), proxy.day.toInt())
}
}

View File

@ -0,0 +1,11 @@
package net.corda.serialization.djvm.deserializers
import net.corda.serialization.internal.amqp.custom.LocalDateTimeSerializer.LocalDateTimeProxy
import java.time.LocalDateTime
import java.util.function.Function
class LocalDateTimeDeserializer : Function<LocalDateTimeProxy, LocalDateTime> {
override fun apply(proxy: LocalDateTimeProxy): LocalDateTime {
return LocalDateTime.of(proxy.date, proxy.time)
}
}

View File

@ -0,0 +1,11 @@
package net.corda.serialization.djvm.deserializers
import net.corda.serialization.internal.amqp.custom.LocalTimeSerializer.LocalTimeProxy
import java.time.LocalTime
import java.util.function.Function
class LocalTimeDeserializer : Function<LocalTimeProxy, LocalTime> {
override fun apply(proxy: LocalTimeProxy): LocalTime {
return LocalTime.of(proxy.hour.toInt(), proxy.minute.toInt(), proxy.second.toInt(), proxy.nano)
}
}

View File

@ -0,0 +1,11 @@
package net.corda.serialization.djvm.deserializers
import net.corda.serialization.internal.amqp.custom.MonthDaySerializer.MonthDayProxy
import java.time.MonthDay
import java.util.function.Function
class MonthDayDeserializer : Function<MonthDayProxy, MonthDay> {
override fun apply(proxy: MonthDayProxy): MonthDay {
return MonthDay.of(proxy.month.toInt(), proxy.day.toInt())
}
}

View File

@ -0,0 +1,11 @@
package net.corda.serialization.djvm.deserializers
import net.corda.serialization.internal.amqp.custom.OffsetDateTimeSerializer.OffsetDateTimeProxy
import java.time.OffsetDateTime
import java.util.function.Function
class OffsetDateTimeDeserializer : Function<OffsetDateTimeProxy, OffsetDateTime> {
override fun apply(proxy: OffsetDateTimeProxy): OffsetDateTime {
return OffsetDateTime.of(proxy.dateTime, proxy.offset)
}
}

View File

@ -0,0 +1,11 @@
package net.corda.serialization.djvm.deserializers
import net.corda.serialization.internal.amqp.custom.OffsetTimeSerializer.OffsetTimeProxy
import java.time.OffsetTime
import java.util.function.Function
class OffsetTimeDeserializer : Function<OffsetTimeProxy, OffsetTime> {
override fun apply(proxy: OffsetTimeProxy): OffsetTime {
return OffsetTime.of(proxy.time, proxy.offset)
}
}

View File

@ -0,0 +1,11 @@
package net.corda.serialization.djvm.deserializers
import net.corda.core.utilities.OpaqueBytes
import net.corda.core.utilities.OpaqueBytesSubSequence
import java.util.function.Function
class OpaqueBytesSubSequenceDeserializer : Function<OpaqueBytes, OpaqueBytesSubSequence> {
override fun apply(proxy: OpaqueBytes): OpaqueBytesSubSequence {
return OpaqueBytesSubSequence(proxy.bytes, proxy.offset, proxy.size)
}
}

View File

@ -0,0 +1,11 @@
package net.corda.serialization.djvm.deserializers
import net.corda.serialization.internal.amqp.custom.OptionalSerializer.OptionalProxy
import java.util.Optional
import java.util.function.Function
class OptionalDeserializer : Function<OptionalProxy, Optional<Any>> {
override fun apply(proxy: OptionalProxy): Optional<Any> {
return Optional.ofNullable(proxy.item)
}
}

View File

@ -0,0 +1,11 @@
package net.corda.serialization.djvm.deserializers
import net.corda.serialization.internal.amqp.custom.PeriodSerializer.PeriodProxy
import java.time.Period
import java.util.function.Function
class PeriodDeserializer : Function<PeriodProxy, Period> {
override fun apply(proxy: PeriodProxy): Period {
return Period.of(proxy.years, proxy.months, proxy.days)
}
}

View File

@ -0,0 +1,11 @@
package net.corda.serialization.djvm.deserializers
import net.corda.core.crypto.Crypto
import java.security.PublicKey
import java.util.function.Function
class PublicKeyDecoder : Function<ByteArray, PublicKey> {
override fun apply(encoded: ByteArray): PublicKey {
return Crypto.decodePublicKey(encoded)
}
}

View File

@ -0,0 +1,10 @@
package net.corda.serialization.djvm.deserializers
import org.apache.qpid.proton.amqp.Symbol
import java.util.function.Function
class SymbolDeserializer : Function<String, Symbol> {
override fun apply(value: String): Symbol {
return Symbol.valueOf(value)
}
}

View File

@ -0,0 +1,10 @@
package net.corda.serialization.djvm.deserializers
import org.apache.qpid.proton.amqp.UnsignedByte
import java.util.function.Function
class UnsignedByteDeserializer : Function<ByteArray, UnsignedByte> {
override fun apply(underlying: ByteArray): UnsignedByte {
return UnsignedByte(underlying[0])
}
}

View File

@ -0,0 +1,10 @@
package net.corda.serialization.djvm.deserializers
import org.apache.qpid.proton.amqp.UnsignedInteger
import java.util.function.Function
class UnsignedIntegerDeserializer : Function<IntArray, UnsignedInteger> {
override fun apply(underlying: IntArray): UnsignedInteger {
return UnsignedInteger(underlying[0])
}
}

View File

@ -0,0 +1,10 @@
package net.corda.serialization.djvm.deserializers
import org.apache.qpid.proton.amqp.UnsignedLong
import java.util.function.Function
class UnsignedLongDeserializer : Function<LongArray, UnsignedLong> {
override fun apply(underlying: LongArray): UnsignedLong {
return UnsignedLong(underlying[0])
}
}

View File

@ -0,0 +1,10 @@
package net.corda.serialization.djvm.deserializers
import org.apache.qpid.proton.amqp.UnsignedShort
import java.util.function.Function
class UnsignedShortDeserializer : Function<ShortArray, UnsignedShort> {
override fun apply(underlying: ShortArray): UnsignedShort {
return UnsignedShort(underlying[0])
}
}

View File

@ -0,0 +1,12 @@
package net.corda.serialization.djvm.deserializers
import java.security.cert.CertificateFactory
import java.security.cert.X509CRL
import java.util.function.Function
class X509CRLDeserializer : Function<ByteArray, X509CRL> {
override fun apply(bytes: ByteArray): X509CRL {
val factory = CertificateFactory.getInstance("X.509")
return factory.generateCRL(bytes.inputStream()) as X509CRL
}
}

View File

@ -0,0 +1,12 @@
package net.corda.serialization.djvm.deserializers
import java.security.cert.CertificateFactory
import java.security.cert.X509Certificate
import java.util.function.Function
class X509CertificateDeserializer : Function<ByteArray, X509Certificate> {
override fun apply(bits: ByteArray): X509Certificate {
val factory = CertificateFactory.getInstance("X.509")
return factory.generateCertificate(bits.inputStream()) as X509Certificate
}
}

View File

@ -0,0 +1,11 @@
package net.corda.serialization.djvm.deserializers
import net.corda.serialization.internal.amqp.custom.YearSerializer.YearProxy
import java.time.Year
import java.util.function.Function
class YearDeserializer : Function<YearProxy, Year> {
override fun apply(proxy: YearProxy): Year {
return Year.of(proxy.year)
}
}

View File

@ -0,0 +1,11 @@
package net.corda.serialization.djvm.deserializers
import net.corda.serialization.internal.amqp.custom.YearMonthSerializer.YearMonthProxy
import java.time.YearMonth
import java.util.function.Function
class YearMonthDeserializer : Function<YearMonthProxy, YearMonth> {
override fun apply(proxy: YearMonthProxy): YearMonth {
return YearMonth.of(proxy.year, proxy.month.toInt())
}
}

View File

@ -0,0 +1,11 @@
package net.corda.serialization.djvm.deserializers
import net.corda.serialization.internal.amqp.custom.ZoneIdSerializer.ZoneIdProxy
import java.time.ZoneId
import java.util.function.Function
class ZoneIdDeserializer : Function<ZoneIdProxy, ZoneId> {
override fun apply(proxy: ZoneIdProxy): ZoneId {
return ZoneId.of(proxy.id)
}
}

View File

@ -0,0 +1,10 @@
package net.corda.serialization.djvm.deserializers
import net.corda.serialization.internal.amqp.custom.ZonedDateTimeSerializer.ZonedDateTimeProxy
import java.util.function.Function
class ZonedDateTimeDeserializer : Function<ZonedDateTimeProxy, Array<out Any>?> {
override fun apply(proxy: ZonedDateTimeProxy): Array<out Any>? {
return arrayOf(proxy.dateTime, proxy.offset, proxy.zone)
}
}

View File

@ -0,0 +1,131 @@
package net.corda.serialization.djvm
import net.corda.core.serialization.SerializationContext
import net.corda.core.serialization.SerializationContext.UseCase
import net.corda.core.serialization.SerializedBytes
import net.corda.core.utilities.ByteSequence
import net.corda.djvm.rewiring.SandboxClassLoader
import net.corda.serialization.djvm.serializers.SandboxBitSetSerializer
import net.corda.serialization.djvm.serializers.SandboxCertPathSerializer
import net.corda.serialization.djvm.serializers.SandboxCharacterSerializer
import net.corda.serialization.djvm.serializers.SandboxCollectionSerializer
import net.corda.serialization.djvm.serializers.SandboxCurrencySerializer
import net.corda.serialization.djvm.serializers.SandboxDecimal128Serializer
import net.corda.serialization.djvm.serializers.SandboxDecimal32Serializer
import net.corda.serialization.djvm.serializers.SandboxDecimal64Serializer
import net.corda.serialization.djvm.serializers.SandboxDurationSerializer
import net.corda.serialization.djvm.serializers.SandboxEnumSerializer
import net.corda.serialization.djvm.serializers.SandboxEnumSetSerializer
import net.corda.serialization.djvm.serializers.SandboxInputStreamSerializer
import net.corda.serialization.djvm.serializers.SandboxInstantSerializer
import net.corda.serialization.djvm.serializers.SandboxLocalDateSerializer
import net.corda.serialization.djvm.serializers.SandboxLocalDateTimeSerializer
import net.corda.serialization.djvm.serializers.SandboxLocalTimeSerializer
import net.corda.serialization.djvm.serializers.SandboxMapSerializer
import net.corda.serialization.djvm.serializers.SandboxMonthDaySerializer
import net.corda.serialization.djvm.serializers.SandboxOffsetDateTimeSerializer
import net.corda.serialization.djvm.serializers.SandboxOffsetTimeSerializer
import net.corda.serialization.djvm.serializers.SandboxOpaqueBytesSubSequenceSerializer
import net.corda.serialization.djvm.serializers.SandboxOptionalSerializer
import net.corda.serialization.djvm.serializers.SandboxPeriodSerializer
import net.corda.serialization.djvm.serializers.SandboxPrimitiveSerializer
import net.corda.serialization.djvm.serializers.SandboxPublicKeySerializer
import net.corda.serialization.djvm.serializers.SandboxSymbolSerializer
import net.corda.serialization.djvm.serializers.SandboxToStringSerializer
import net.corda.serialization.djvm.serializers.SandboxUnsignedByteSerializer
import net.corda.serialization.djvm.serializers.SandboxUnsignedIntegerSerializer
import net.corda.serialization.djvm.serializers.SandboxUnsignedLongSerializer
import net.corda.serialization.djvm.serializers.SandboxUnsignedShortSerializer
import net.corda.serialization.djvm.serializers.SandboxX509CRLSerializer
import net.corda.serialization.djvm.serializers.SandboxX509CertificateSerializer
import net.corda.serialization.djvm.serializers.SandboxYearMonthSerializer
import net.corda.serialization.djvm.serializers.SandboxYearSerializer
import net.corda.serialization.djvm.serializers.SandboxZoneIdSerializer
import net.corda.serialization.djvm.serializers.SandboxZonedDateTimeSerializer
import net.corda.serialization.internal.CordaSerializationMagic
import net.corda.serialization.internal.SerializationScheme
import net.corda.serialization.internal.amqp.DeserializationInput
import net.corda.serialization.internal.amqp.SerializationOutput
import net.corda.serialization.internal.amqp.SerializerFactory
import net.corda.serialization.internal.amqp.SerializerFactoryFactory
import net.corda.serialization.internal.amqp.amqpMagic
import java.math.BigDecimal
import java.math.BigInteger
import java.util.Date
import java.util.UUID
import java.util.function.Function
class AMQPSerializationScheme(
private val classLoader: SandboxClassLoader,
private val sandboxBasicInput: Function<in Any?, out Any?>,
private val taskFactory: Function<in Any, out Function<in Any?, out Any?>>,
private val serializerFactoryFactory: SerializerFactoryFactory
) : SerializationScheme {
private fun getSerializerFactory(context: SerializationContext): SerializerFactory {
return serializerFactoryFactory.make(context).apply {
register(SandboxBitSetSerializer(classLoader, taskFactory, this))
register(SandboxCertPathSerializer(classLoader, taskFactory, this))
register(SandboxDurationSerializer(classLoader, taskFactory, this))
register(SandboxEnumSetSerializer(classLoader, taskFactory, this))
register(SandboxInputStreamSerializer(classLoader, taskFactory))
register(SandboxInstantSerializer(classLoader, taskFactory, this))
register(SandboxLocalDateSerializer(classLoader, taskFactory, this))
register(SandboxLocalDateTimeSerializer(classLoader, taskFactory, this))
register(SandboxLocalTimeSerializer(classLoader, taskFactory, this))
register(SandboxMonthDaySerializer(classLoader, taskFactory, this))
register(SandboxOffsetDateTimeSerializer(classLoader, taskFactory, this))
register(SandboxOffsetTimeSerializer(classLoader, taskFactory, this))
register(SandboxPeriodSerializer(classLoader, taskFactory, this))
register(SandboxYearMonthSerializer(classLoader, taskFactory, this))
register(SandboxYearSerializer(classLoader, taskFactory, this))
register(SandboxZonedDateTimeSerializer(classLoader, taskFactory, this))
register(SandboxZoneIdSerializer(classLoader, taskFactory, this))
register(SandboxOpaqueBytesSubSequenceSerializer(classLoader, taskFactory, this))
register(SandboxOptionalSerializer(classLoader, taskFactory, this))
register(SandboxPrimitiveSerializer(UUID::class.java, classLoader, sandboxBasicInput))
register(SandboxPrimitiveSerializer(String::class.java, classLoader, sandboxBasicInput))
register(SandboxPrimitiveSerializer(Byte::class.javaObjectType, classLoader, sandboxBasicInput))
register(SandboxPrimitiveSerializer(Short::class.javaObjectType, classLoader, sandboxBasicInput))
register(SandboxPrimitiveSerializer(Int::class.javaObjectType, classLoader, sandboxBasicInput))
register(SandboxPrimitiveSerializer(Long::class.javaObjectType, classLoader, sandboxBasicInput))
register(SandboxPrimitiveSerializer(Float::class.javaObjectType, classLoader, sandboxBasicInput))
register(SandboxPrimitiveSerializer(Double::class.javaObjectType, classLoader, sandboxBasicInput))
register(SandboxPrimitiveSerializer(Boolean::class.javaObjectType, classLoader, sandboxBasicInput))
register(SandboxPrimitiveSerializer(Date::class.javaObjectType, classLoader, sandboxBasicInput))
register(SandboxCharacterSerializer(classLoader, sandboxBasicInput))
register(SandboxCollectionSerializer(classLoader, taskFactory, this))
register(SandboxMapSerializer(classLoader, taskFactory, this))
register(SandboxEnumSerializer(classLoader, taskFactory, this))
register(SandboxPublicKeySerializer(classLoader, taskFactory))
register(SandboxToStringSerializer(BigDecimal::class.java, classLoader, taskFactory, sandboxBasicInput))
register(SandboxToStringSerializer(BigInteger::class.java, classLoader, taskFactory, sandboxBasicInput))
register(SandboxToStringSerializer(StringBuffer::class.java, classLoader, taskFactory, sandboxBasicInput))
register(SandboxCurrencySerializer(classLoader, taskFactory, sandboxBasicInput))
register(SandboxX509CertificateSerializer(classLoader, taskFactory))
register(SandboxX509CRLSerializer(classLoader, taskFactory))
register(SandboxUnsignedLongSerializer(classLoader, taskFactory))
register(SandboxUnsignedIntegerSerializer(classLoader, taskFactory))
register(SandboxUnsignedShortSerializer(classLoader, taskFactory))
register(SandboxUnsignedByteSerializer(classLoader, taskFactory))
register(SandboxDecimal128Serializer(classLoader, taskFactory))
register(SandboxDecimal64Serializer(classLoader, taskFactory))
register(SandboxDecimal32Serializer(classLoader, taskFactory))
register(SandboxSymbolSerializer(classLoader, taskFactory, sandboxBasicInput))
}
}
override fun <T : Any> deserialize(byteSequence: ByteSequence, clazz: Class<T>, context: SerializationContext): T {
val serializerFactory = getSerializerFactory(context)
return DeserializationInput(serializerFactory).deserialize(byteSequence, clazz, context)
}
override fun <T : Any> serialize(obj: T, context: SerializationContext): SerializedBytes<T> {
val serializerFactory = getSerializerFactory(context)
return SerializationOutput(serializerFactory).serialize(obj, context)
}
override fun canDeserializeVersion(magic: CordaSerializationMagic, target: UseCase): Boolean {
return magic == amqpMagic && target == UseCase.P2P
}
}

View File

@ -0,0 +1,10 @@
package net.corda.serialization.djvm
import net.corda.djvm.rewiring.SandboxClassLoader
class DelegatingClassLoader(private val delegate: SandboxClassLoader) : ClassLoader(null) {
@Throws(ClassNotFoundException::class)
override fun loadClass(name: String, resolve: Boolean): Class<*> {
return delegate.loadForSandbox(name).type
}
}

View File

@ -0,0 +1,108 @@
@file:Suppress("platform_class_mapped_to_kotlin")
package net.corda.serialization.djvm
import net.corda.core.serialization.SerializationContext
import net.corda.serialization.internal.amqp.AMQPRemoteTypeModel
import net.corda.serialization.internal.amqp.AMQPSerializer
import net.corda.serialization.internal.amqp.CachingCustomSerializerRegistry
import net.corda.serialization.internal.amqp.ComposedSerializerFactory
import net.corda.serialization.internal.amqp.DefaultDescriptorBasedSerializerRegistry
import net.corda.serialization.internal.amqp.DefaultEvolutionSerializerFactory
import net.corda.serialization.internal.amqp.DefaultLocalSerializerFactory
import net.corda.serialization.internal.amqp.DefaultRemoteSerializerFactory
import net.corda.serialization.internal.amqp.SerializerFactory
import net.corda.serialization.internal.amqp.SerializerFactoryFactory
import net.corda.serialization.internal.amqp.WhitelistBasedTypeModelConfiguration
import net.corda.serialization.internal.amqp.createClassCarpenter
import net.corda.serialization.internal.model.ClassCarpentingTypeLoader
import net.corda.serialization.internal.model.ConfigurableLocalTypeModel
import net.corda.serialization.internal.model.SchemaBuildingRemoteTypeCarpenter
import net.corda.serialization.internal.model.TypeLoader
import net.corda.serialization.internal.model.TypeModellingFingerPrinter
import java.lang.Boolean
import java.lang.Byte
import java.lang.Double
import java.lang.Float
import java.lang.Long
import java.lang.Short
import java.util.Collections.singleton
import java.util.Collections.unmodifiableMap
import java.util.Date
import java.util.UUID
import java.util.function.Function
import java.util.function.Predicate
/**
* This has all been lovingly copied from [SerializerFactoryBuilder].
*/
class SandboxSerializerFactoryFactory(
private val primitiveSerializerFactory: Function<Class<*>, AMQPSerializer<Any>>
) : SerializerFactoryFactory {
override fun make(context: SerializationContext): SerializerFactory {
val classLoader = context.deserializationClassLoader
val primitiveTypes = unmodifiableMap(mapOf<Class<*>, Class<*>>(
classLoader.loadClass("sandbox.java.lang.Boolean") to Boolean.TYPE,
classLoader.loadClass("sandbox.java.lang.Byte") to Byte.TYPE,
classLoader.loadClass("sandbox.java.lang.Character") to Character.TYPE,
classLoader.loadClass("sandbox.java.lang.Double") to Double.TYPE,
classLoader.loadClass("sandbox.java.lang.Float") to Float.TYPE,
classLoader.loadClass("sandbox.java.lang.Integer") to Integer.TYPE,
classLoader.loadClass("sandbox.java.lang.Long") to Long.TYPE,
classLoader.loadClass("sandbox.java.lang.Short") to Short.TYPE,
classLoader.loadClass("sandbox.java.lang.String") to String::class.java,
classLoader.loadClass("sandbox.java.util.Date") to Date::class.java,
classLoader.loadClass("sandbox.java.util.UUID") to UUID::class.java,
Void::class.java to Void.TYPE
))
val classCarpenter = createClassCarpenter(context)
val descriptorBasedSerializerRegistry = DefaultDescriptorBasedSerializerRegistry()
val customSerializerRegistry = CachingCustomSerializerRegistry(
descriptorBasedSerializerRegistry = descriptorBasedSerializerRegistry,
allowedFor = singleton(classLoader.loadClass("sandbox.java.lang.Object"))
)
val localTypeModel = ConfigurableLocalTypeModel(
WhitelistBasedTypeModelConfiguration(context.whitelist, customSerializerRegistry)
)
val fingerPrinter = TypeModellingFingerPrinter(customSerializerRegistry)
val localSerializerFactory = DefaultLocalSerializerFactory(
whitelist = context.whitelist,
typeModel = localTypeModel,
fingerPrinter = fingerPrinter,
classloader = classLoader,
descriptorBasedSerializerRegistry = descriptorBasedSerializerRegistry,
primitiveSerializerFactory = primitiveSerializerFactory,
isPrimitiveType = Predicate { clazz -> clazz.isPrimitive || clazz in primitiveTypes.keys },
customSerializerRegistry = customSerializerRegistry,
onlyCustomSerializers = false
)
val typeLoader: TypeLoader = ClassCarpentingTypeLoader(
carpenter = SchemaBuildingRemoteTypeCarpenter(classCarpenter),
classLoader = classLoader
)
val evolutionSerializerFactory = DefaultEvolutionSerializerFactory(
localSerializerFactory = localSerializerFactory,
classLoader = classLoader,
mustPreserveDataWhenEvolving = context.preventDataLoss,
primitiveTypes = primitiveTypes
)
val remoteSerializerFactory = DefaultRemoteSerializerFactory(
evolutionSerializerFactory = evolutionSerializerFactory,
descriptorBasedSerializerRegistry = descriptorBasedSerializerRegistry,
remoteTypeModel = AMQPRemoteTypeModel(),
localTypeModel = localTypeModel,
typeLoader = typeLoader,
localSerializerFactory = localSerializerFactory
)
return ComposedSerializerFactory(localSerializerFactory, remoteSerializerFactory, customSerializerRegistry)
}
}

View File

@ -0,0 +1,13 @@
package net.corda.serialization.djvm
import net.corda.core.serialization.ClassWhitelist
class SandboxWhitelist : ClassWhitelist {
companion object {
private val packageName = "^sandbox\\.(?:java|kotlin)(?:[.]|$)".toRegex()
}
override fun hasListed(type: Class<*>): Boolean {
return packageName.containsMatchIn(type.`package`.name)
}
}

View File

@ -0,0 +1,74 @@
@file:JvmName("Serialization")
package net.corda.serialization.djvm
import net.corda.core.serialization.SerializationContext
import net.corda.core.serialization.SerializationContext.UseCase
import net.corda.core.serialization.SerializationFactory
import net.corda.core.serialization.SerializedBytes
import net.corda.core.serialization.internal.SerializationEnvironment
import net.corda.core.utilities.ByteSequence
import net.corda.djvm.rewiring.SandboxClassLoader
import net.corda.serialization.djvm.serializers.PrimitiveSerializer
import net.corda.serialization.internal.GlobalTransientClassWhiteList
import net.corda.serialization.internal.SerializationContextImpl
import net.corda.serialization.internal.SerializationFactoryImpl
import net.corda.serialization.internal.amqp.AMQPSerializer
import net.corda.serialization.internal.amqp.amqpMagic
import java.util.function.Function
@Suppress("NOTHING_TO_INLINE")
inline fun SandboxClassLoader.toSandboxAnyClass(clazz: Class<*>): Class<Any> {
@Suppress("unchecked_cast")
return toSandboxClass(clazz) as Class<Any>
}
fun createSandboxSerializationEnv(classLoader: SandboxClassLoader): SerializationEnvironment {
val p2pContext: SerializationContext = SerializationContextImpl(
preferredSerializationVersion = amqpMagic,
deserializationClassLoader = DelegatingClassLoader(classLoader),
whitelist = GlobalTransientClassWhiteList(SandboxWhitelist()),
properties = emptyMap(),
objectReferencesEnabled = true,
carpenterDisabled = true,
useCase = UseCase.P2P,
encoding = null
)
val taskFactory = classLoader.createRawTaskFactory()
val sandboxBasicInput = classLoader.createBasicInput()
val primitiveSerializerFactory: Function<Class<*>, AMQPSerializer<Any>> = Function { clazz ->
PrimitiveSerializer(clazz, sandboxBasicInput)
}
val factory = SerializationFactoryImpl(mutableMapOf()).apply {
registerScheme(AMQPSerializationScheme(
classLoader = classLoader,
sandboxBasicInput = sandboxBasicInput,
taskFactory = taskFactory,
serializerFactoryFactory = SandboxSerializerFactoryFactory(primitiveSerializerFactory)
))
}
return SerializationEnvironment.with(factory, p2pContext = p2pContext)
}
inline fun <reified T: Any> SerializedBytes<T>.deserializeFor(classLoader: SandboxClassLoader): Any {
return deserializeTo(T::class.java, classLoader)
}
inline fun <reified T: Any> ByteSequence.deserializeTypeFor(classLoader: SandboxClassLoader): Any {
return deserializeTo(T::class.java, classLoader)
}
fun <T: Any> ByteSequence.deserializeTo(clazz: Class<T>, classLoader: SandboxClassLoader): Any {
val sandboxClazz = classLoader.toSandboxClass(clazz)
return deserializeTo(sandboxClazz)
}
fun ByteSequence.deserializeTo(clazz: Class<*>): Any {
return deserializeTo(clazz, SerializationFactory.defaultFactory)
}
fun ByteSequence.deserializeTo(clazz: Class<*>, factory: SerializationFactory): Any {
return factory.deserialize(this, clazz, factory.defaultContext)
}

View File

@ -0,0 +1,39 @@
@file:JvmName("ExceptionUtils")
package net.corda.serialization.djvm.serializers
import net.corda.serialization.internal.amqp.AMQPNotSerializableException
/**
* Utility function which helps tracking the path in the object graph when exceptions are thrown.
* Since there might be a chain of nested calls it is useful to record which part of the graph caused an issue.
* Path information is added to the message of the exception being thrown.
*/
@Suppress("TooGenericExceptionCaught")
internal inline fun <T> ifThrowsAppend(strToAppendFn: () -> String, block: () -> T): T {
try {
return block()
} catch (th: Throwable) {
when (th) {
is AMQPNotSerializableException -> th.classHierarchy.add(strToAppendFn())
// Do not overwrite the message of these exceptions as it may be used.
is ClassNotFoundException -> {}
is NoClassDefFoundError -> {}
else -> th.resetMessage("${strToAppendFn()} -> ${th.message}")
}
throw th
}
}
/**
* Not a public property so will have to use reflection
*/
private fun Throwable.resetMessage(newMsg: String) {
val detailMessageField = Throwable::class.java.getDeclaredField("detailMessage")
detailMessageField.isAccessible = true
detailMessageField.set(this, newMsg)
}
/**
* We currently only support deserialisation, and so we're going to need this.
*/
fun abortReadOnly(): Nothing = throw UnsupportedOperationException("Read Only!")

View File

@ -0,0 +1,36 @@
package net.corda.serialization.djvm.serializers
import net.corda.core.serialization.SerializationContext
import net.corda.serialization.internal.amqp.AMQPSerializer
import net.corda.serialization.internal.amqp.DeserializationInput
import net.corda.serialization.internal.amqp.SerializationOutput
import net.corda.serialization.internal.amqp.SerializationSchemas
import net.corda.serialization.internal.amqp.typeDescriptorFor
import org.apache.qpid.proton.amqp.Binary
import org.apache.qpid.proton.amqp.Symbol
import org.apache.qpid.proton.codec.Data
import java.lang.reflect.Type
import java.util.function.Function
class PrimitiveSerializer(
override val type: Class<*>,
private val sandboxBasicInput: Function<in Any?, out Any?>
) : AMQPSerializer<Any> {
override val typeDescriptor: Symbol = typeDescriptorFor(type)
override fun readObject(
obj: Any, schemas: SerializationSchemas, input: DeserializationInput, context: SerializationContext
): Any {
return (obj as? Binary)?.array ?: sandboxBasicInput.apply(obj)!!
}
override fun writeClassInfo(output: SerializationOutput) {
abortReadOnly()
}
override fun writeObject(
obj: Any, data: Data, type: Type, output: SerializationOutput, context: SerializationContext, debugIndent: Int
) {
abortReadOnly()
}
}

View File

@ -0,0 +1,31 @@
package net.corda.serialization.djvm.serializers
import net.corda.djvm.rewiring.SandboxClassLoader
import net.corda.serialization.djvm.deserializers.BitSetDeserializer
import net.corda.serialization.djvm.toSandboxAnyClass
import net.corda.serialization.internal.amqp.CustomSerializer
import net.corda.serialization.internal.amqp.SerializerFactory
import net.corda.serialization.internal.amqp.custom.BitSetSerializer.BitSetProxy
import java.util.BitSet
import java.util.Collections.singleton
import java.util.function.Function
class SandboxBitSetSerializer(
classLoader: SandboxClassLoader,
taskFactory: Function<in Any, out Function<in Any?, out Any?>>,
factory: SerializerFactory
) : CustomSerializer.Proxy<Any, Any>(
clazz = classLoader.toSandboxAnyClass(BitSet::class.java),
proxyClass = classLoader.toSandboxAnyClass(BitSetProxy::class.java),
factory = factory
) {
private val task = classLoader.createTaskFor(taskFactory, BitSetDeserializer::class.java)
override val deserializationAliases: Set<Class<*>> = singleton(BitSet::class.java)
override fun toProxy(obj: Any): Any = abortReadOnly()
override fun fromProxy(proxy: Any): Any {
return task.apply(proxy)!!
}
}

View File

@ -0,0 +1,31 @@
package net.corda.serialization.djvm.serializers
import net.corda.djvm.rewiring.SandboxClassLoader
import net.corda.serialization.djvm.deserializers.CertPathDeserializer
import net.corda.serialization.djvm.toSandboxAnyClass
import net.corda.serialization.internal.amqp.CustomSerializer
import net.corda.serialization.internal.amqp.SerializerFactory
import net.corda.serialization.internal.amqp.custom.CertPathSerializer.CertPathProxy
import java.security.cert.CertPath
import java.util.Collections.singleton
import java.util.function.Function
class SandboxCertPathSerializer(
classLoader: SandboxClassLoader,
taskFactory: Function<in Any, out Function<in Any?, out Any?>>,
factory: SerializerFactory
) : CustomSerializer.Proxy<Any, Any>(
clazz = classLoader.toSandboxAnyClass(CertPath::class.java),
proxyClass = classLoader.toSandboxAnyClass(CertPathProxy::class.java),
factory = factory
) {
private val task = classLoader.createTaskFor(taskFactory, CertPathDeserializer::class.java)
override val deserializationAliases: Set<Class<*>> = singleton(CertPath::class.java)
override fun toProxy(obj: Any): Any = abortReadOnly()
override fun fromProxy(proxy: Any): Any {
return task.apply(proxy)!!
}
}

View File

@ -0,0 +1,37 @@
package net.corda.serialization.djvm.serializers
import net.corda.core.serialization.SerializationContext
import net.corda.djvm.rewiring.SandboxClassLoader
import net.corda.serialization.djvm.toSandboxAnyClass
import net.corda.serialization.internal.amqp.CustomSerializer
import net.corda.serialization.internal.amqp.DeserializationInput
import net.corda.serialization.internal.amqp.Schema
import net.corda.serialization.internal.amqp.SerializationOutput
import net.corda.serialization.internal.amqp.SerializationSchemas
import org.apache.qpid.proton.codec.Data
import java.lang.reflect.Type
import java.util.function.Function
class SandboxCharacterSerializer(
classLoader: SandboxClassLoader,
private val basicInput: Function<in Any?, out Any?>
) : CustomSerializer.Is<Any>(classLoader.toSandboxAnyClass(Char::class.javaObjectType)) {
override val schemaForDocumentation: Schema = Schema(emptyList())
override fun readObject(obj: Any, schemas: SerializationSchemas, input: DeserializationInput, context: SerializationContext): Any {
return basicInput.apply(convertToChar(obj))!!
}
private fun convertToChar(obj: Any): Any {
return when (obj) {
is Short -> obj.toChar()
is Int -> obj.toChar()
else -> obj
}
}
override fun writeDescribedObject(obj: Any, data: Data, type: Type, output: SerializationOutput, context: SerializationContext) {
abortReadOnly()
}
}

View File

@ -0,0 +1,47 @@
package net.corda.serialization.djvm.serializers
import net.corda.djvm.rewiring.SandboxClassLoader
import net.corda.serialization.djvm.deserializers.ClassDeserializer
import net.corda.serialization.djvm.toSandboxAnyClass
import net.corda.serialization.internal.amqp.AMQPNotSerializableException
import net.corda.serialization.internal.amqp.CustomSerializer
import net.corda.serialization.internal.amqp.SerializerFactory
import net.corda.serialization.internal.amqp.custom.ClassSerializer.ClassProxy
import java.util.function.Function
@Suppress("unchecked_cast")
class SandboxClassSerializer(
classLoader: SandboxClassLoader,
taskFactory: Function<in Any, out Function<in Any?, out Any?>>,
factory: SerializerFactory
) : CustomSerializer.Proxy<Any, Any>(
clazz = Class::class.java as Class<Any>,
proxyClass = classLoader.toSandboxAnyClass(ClassProxy::class.java),
factory = factory
) {
private val task = classLoader.createTaskFor(taskFactory, ClassDeserializer::class.java)
private val nameOf: Function<Any, String>
init {
val fetch = proxyClass.getMethod("getClassName")
nameOf = Function { proxy ->
fetch(proxy).toString()
}
}
override fun toProxy(obj: Any): Any = abortReadOnly()
override fun fromProxy(proxy: Any): Any {
return try {
task.apply(proxy)!!
} catch (e: ClassNotFoundException) {
val className = nameOf.apply(proxy)
throw AMQPNotSerializableException(type,
"Could not instantiate $className - not on the classpath",
"$className was not found by the node, check the Node containing the CorDapp that " +
"implements $className is loaded and on the Classpath",
mutableListOf(className)
)
}
}
}

View File

@ -0,0 +1,129 @@
package net.corda.serialization.djvm.serializers
import net.corda.core.serialization.SerializationContext
import net.corda.core.utilities.NonEmptySet
import net.corda.djvm.rewiring.SandboxClassLoader
import net.corda.serialization.djvm.deserializers.CreateCollection
import net.corda.serialization.djvm.toSandboxAnyClass
import net.corda.serialization.internal.amqp.AMQPSerializer
import net.corda.serialization.internal.amqp.CustomSerializer
import net.corda.serialization.internal.amqp.DeserializationInput
import net.corda.serialization.internal.amqp.LocalSerializerFactory
import net.corda.serialization.internal.amqp.Schema
import net.corda.serialization.internal.amqp.SerializationOutput
import net.corda.serialization.internal.amqp.SerializationSchemas
import net.corda.serialization.internal.amqp.redescribe
import net.corda.serialization.internal.model.LocalTypeInformation
import net.corda.serialization.internal.model.TypeIdentifier
import org.apache.qpid.proton.amqp.Symbol
import org.apache.qpid.proton.codec.Data
import java.lang.reflect.ParameterizedType
import java.lang.reflect.Type
import java.util.EnumSet
import java.util.NavigableSet
import java.util.SortedSet
import java.util.function.Function
class SandboxCollectionSerializer(
classLoader: SandboxClassLoader,
taskFactory: Function<in Any, out Function<in Any?, out Any?>>,
private val localFactory: LocalSerializerFactory
) : CustomSerializer.Implements<Any>(clazz = classLoader.toSandboxAnyClass(Collection::class.java)) {
@Suppress("unchecked_cast")
private val creator: Function<Array<Any>, out Any?>
= classLoader.createTaskFor(taskFactory, CreateCollection::class.java) as Function<Array<Any>, out Any?>
private val unsupportedTypes: Set<Class<Any>> = listOf(
EnumSet::class.java
).map {
classLoader.toSandboxAnyClass(it)
}.toSet()
// The order matters here - the first match should be the most specific one.
// Kotlin preserves the ordering for us by associating into a LinkedHashMap.
private val supportedTypes: Map<Class<Any>, Class<out Collection<*>>> = listOf(
List::class.java,
NonEmptySet::class.java,
NavigableSet::class.java,
SortedSet::class.java,
Set::class.java,
Collection::class.java
).associateBy {
classLoader.toSandboxAnyClass(it)
}
private fun getBestMatchFor(type: Class<Any>): Map.Entry<Class<Any>, Class<out Collection<*>>>
= supportedTypes.entries.first { it.key.isAssignableFrom(type) }
override val schemaForDocumentation: Schema = Schema(emptyList())
override fun isSerializerFor(clazz: Class<*>): Boolean {
return super.isSerializerFor(clazz) && unsupportedTypes.none { it.isAssignableFrom(clazz) }
}
override fun specialiseFor(declaredType: Type): AMQPSerializer<Any>? {
if (declaredType !is ParameterizedType) {
return null
}
@Suppress("unchecked_cast")
val rawType = declaredType.rawType as Class<Any>
return ConcreteCollectionSerializer(declaredType, getBestMatchFor(rawType), creator, localFactory)
}
override fun readObject(
obj: Any, schemas: SerializationSchemas, input: DeserializationInput, context: SerializationContext
): Any {
throw UnsupportedOperationException("Factory only")
}
override fun writeDescribedObject(
obj: Any, data: Data, type: Type, output: SerializationOutput, context: SerializationContext
) {
throw UnsupportedOperationException("Factory Only")
}
}
private class ConcreteCollectionSerializer(
declaredType: ParameterizedType,
private val matchingType: Map.Entry<Class<Any>, Class<out Collection<*>>>,
private val creator: Function<Array<Any>, out Any?>,
factory: LocalSerializerFactory
) : AMQPSerializer<Any> {
override val type: ParameterizedType = declaredType
override val typeDescriptor: Symbol by lazy {
factory.createDescriptor(
LocalTypeInformation.ACollection(
observedType = declaredType.rawType,
typeIdentifier = TypeIdentifier.forGenericType(declaredType),
elementType =factory.getTypeInformation(declaredType.actualTypeArguments[0])
)
)
}
override fun readObject(
obj: Any,
schemas: SerializationSchemas,
input: DeserializationInput,
context: SerializationContext
): Any {
val inboundType = type.actualTypeArguments[0]
return ifThrowsAppend({ type.typeName }) {
val args = (obj as List<*>).map {
input.readObjectOrNull(redescribe(it, inboundType), schemas, inboundType, context)
}.toTypedArray()
creator.apply(arrayOf(matchingType.key, args))!!
}
}
override fun writeClassInfo(output: SerializationOutput) {
abortReadOnly()
}
override fun writeObject(
obj: Any, data: Data, type: Type, output: SerializationOutput, context: SerializationContext, debugIndent: Int
) {
abortReadOnly()
}
}

View File

@ -0,0 +1,41 @@
package net.corda.serialization.djvm.serializers
import net.corda.core.serialization.SerializationContext
import net.corda.djvm.rewiring.SandboxClassLoader
import net.corda.serialization.djvm.deserializers.CreateCurrency
import net.corda.serialization.djvm.toSandboxAnyClass
import net.corda.serialization.internal.amqp.CustomSerializer
import net.corda.serialization.internal.amqp.DeserializationInput
import net.corda.serialization.internal.amqp.Schema
import net.corda.serialization.internal.amqp.SerializationOutput
import net.corda.serialization.internal.amqp.SerializationSchemas
import org.apache.qpid.proton.codec.Data
import java.lang.reflect.Type
import java.util.Collections.singleton
import java.util.Currency
import java.util.function.Function
class SandboxCurrencySerializer(
classLoader: SandboxClassLoader,
taskFactory: Function<in Any, out Function<in Any?, out Any?>>,
basicInput: Function<in Any?, out Any?>
) : CustomSerializer.Is<Any>(classLoader.toSandboxAnyClass(Currency::class.java)) {
private val creator: Function<Any?, Any?>
init {
val createTask = classLoader.createTaskFor(taskFactory, CreateCurrency::class.java)
creator = basicInput.andThen(createTask)
}
override val deserializationAliases: Set<Class<*>> = singleton(Currency::class.java)
override val schemaForDocumentation: Schema = Schema(emptyList())
override fun readObject(obj: Any, schemas: SerializationSchemas, input: DeserializationInput, context: SerializationContext): Any {
return creator.apply(obj)!!
}
override fun writeDescribedObject(obj: Any, data: Data, type: Type, output: SerializationOutput, context: SerializationContext) {
abortReadOnly()
}
}

View File

@ -0,0 +1,35 @@
package net.corda.serialization.djvm.serializers
import net.corda.core.serialization.SerializationContext
import net.corda.djvm.rewiring.SandboxClassLoader
import net.corda.serialization.djvm.deserializers.Decimal128Deserializer
import net.corda.serialization.djvm.toSandboxAnyClass
import net.corda.serialization.internal.amqp.CustomSerializer
import net.corda.serialization.internal.amqp.DeserializationInput
import net.corda.serialization.internal.amqp.Schema
import net.corda.serialization.internal.amqp.SerializationOutput
import net.corda.serialization.internal.amqp.SerializationSchemas
import org.apache.qpid.proton.amqp.Decimal128
import org.apache.qpid.proton.codec.Data
import java.lang.reflect.Type
import java.util.function.Function
class SandboxDecimal128Serializer(
classLoader: SandboxClassLoader,
taskFactory: Function<in Any, out Function<in Any?, out Any?>>
) : CustomSerializer.Is<Any>(classLoader.toSandboxAnyClass(Decimal128::class.java)) {
@Suppress("unchecked_cast")
private val transformer: Function<LongArray, out Any?>
= classLoader.createTaskFor(taskFactory, Decimal128Deserializer::class.java) as Function<LongArray, out Any?>
override val schemaForDocumentation: Schema = Schema(emptyList())
override fun readObject(obj: Any, schemas: SerializationSchemas, input: DeserializationInput, context: SerializationContext): Any {
val decimal128 = obj as Decimal128
return transformer.apply(longArrayOf(decimal128.mostSignificantBits, decimal128.leastSignificantBits))!!
}
override fun writeDescribedObject(obj: Any, data: Data, type: Type, output: SerializationOutput, context: SerializationContext) {
abortReadOnly()
}
}

View File

@ -0,0 +1,34 @@
package net.corda.serialization.djvm.serializers
import net.corda.core.serialization.SerializationContext
import net.corda.djvm.rewiring.SandboxClassLoader
import net.corda.serialization.djvm.deserializers.Decimal32Deserializer
import net.corda.serialization.djvm.toSandboxAnyClass
import net.corda.serialization.internal.amqp.CustomSerializer
import net.corda.serialization.internal.amqp.DeserializationInput
import net.corda.serialization.internal.amqp.Schema
import net.corda.serialization.internal.amqp.SerializationOutput
import net.corda.serialization.internal.amqp.SerializationSchemas
import org.apache.qpid.proton.amqp.Decimal32
import org.apache.qpid.proton.codec.Data
import java.lang.reflect.Type
import java.util.function.Function
class SandboxDecimal32Serializer(
classLoader: SandboxClassLoader,
taskFactory: Function<in Any, out Function<in Any?, out Any?>>
) : CustomSerializer.Is<Any>(classLoader.toSandboxAnyClass(Decimal32::class.java)) {
@Suppress("unchecked_cast")
private val transformer: Function<IntArray, out Any?>
= classLoader.createTaskFor(taskFactory, Decimal32Deserializer::class.java) as Function<IntArray, out Any?>
override val schemaForDocumentation: Schema = Schema(emptyList())
override fun readObject(obj: Any, schemas: SerializationSchemas, input: DeserializationInput, context: SerializationContext): Any {
return transformer.apply(intArrayOf((obj as Decimal32).bits))!!
}
override fun writeDescribedObject(obj: Any, data: Data, type: Type, output: SerializationOutput, context: SerializationContext) {
abortReadOnly()
}
}

View File

@ -0,0 +1,34 @@
package net.corda.serialization.djvm.serializers
import net.corda.core.serialization.SerializationContext
import net.corda.djvm.rewiring.SandboxClassLoader
import net.corda.serialization.djvm.deserializers.Decimal64Deserializer
import net.corda.serialization.djvm.toSandboxAnyClass
import net.corda.serialization.internal.amqp.CustomSerializer
import net.corda.serialization.internal.amqp.DeserializationInput
import net.corda.serialization.internal.amqp.Schema
import net.corda.serialization.internal.amqp.SerializationOutput
import net.corda.serialization.internal.amqp.SerializationSchemas
import org.apache.qpid.proton.amqp.Decimal64
import org.apache.qpid.proton.codec.Data
import java.lang.reflect.Type
import java.util.function.Function
class SandboxDecimal64Serializer(
classLoader: SandboxClassLoader,
taskFactory: Function<in Any, out Function<in Any?, out Any?>>
) : CustomSerializer.Is<Any>(classLoader.toSandboxAnyClass(Decimal64::class.java)) {
@Suppress("unchecked_cast")
private val transformer: Function<LongArray, out Any?>
= classLoader.createTaskFor(taskFactory, Decimal64Deserializer::class.java) as Function<LongArray, out Any?>
override val schemaForDocumentation: Schema = Schema(emptyList())
override fun readObject(obj: Any, schemas: SerializationSchemas, input: DeserializationInput, context: SerializationContext): Any {
return transformer.apply(longArrayOf((obj as Decimal64).bits))!!
}
override fun writeDescribedObject(obj: Any, data: Data, type: Type, output: SerializationOutput, context: SerializationContext) {
abortReadOnly()
}
}

View File

@ -0,0 +1,31 @@
package net.corda.serialization.djvm.serializers
import net.corda.djvm.rewiring.SandboxClassLoader
import net.corda.serialization.djvm.deserializers.DurationDeserializer
import net.corda.serialization.djvm.toSandboxAnyClass
import net.corda.serialization.internal.amqp.CustomSerializer
import net.corda.serialization.internal.amqp.SerializerFactory
import net.corda.serialization.internal.amqp.custom.DurationSerializer.DurationProxy
import java.time.Duration
import java.util.Collections.singleton
import java.util.function.Function
class SandboxDurationSerializer(
classLoader: SandboxClassLoader,
taskFactory: Function<in Any, out Function<in Any?, out Any?>>,
factory: SerializerFactory
) : CustomSerializer.Proxy<Any, Any>(
clazz = classLoader.toSandboxAnyClass(Duration::class.java),
proxyClass = classLoader.toSandboxAnyClass(DurationProxy::class.java),
factory = factory
) {
private val task = classLoader.createTaskFor(taskFactory, DurationDeserializer::class.java)
override val deserializationAliases: Set<Class<*>> = singleton(Duration::class.java)
override fun toProxy(obj: Any): Any = abortReadOnly()
override fun fromProxy(proxy: Any): Any {
return task.apply(proxy)!!
}
}

View File

@ -0,0 +1,103 @@
package net.corda.serialization.djvm.serializers
import net.corda.core.serialization.SerializationContext
import net.corda.djvm.rewiring.SandboxClassLoader
import net.corda.serialization.djvm.deserializers.DescribeEnum
import net.corda.serialization.djvm.toSandboxAnyClass
import net.corda.serialization.internal.amqp.AMQPNotSerializableException
import net.corda.serialization.internal.amqp.AMQPSerializer
import net.corda.serialization.internal.amqp.CustomSerializer
import net.corda.serialization.internal.amqp.DeserializationInput
import net.corda.serialization.internal.amqp.LocalSerializerFactory
import net.corda.serialization.internal.amqp.Schema
import net.corda.serialization.internal.amqp.SerializationOutput
import net.corda.serialization.internal.amqp.SerializationSchemas
import net.corda.serialization.internal.model.EnumTransforms
import net.corda.serialization.internal.model.LocalTypeInformation
import net.corda.serialization.internal.model.TypeIdentifier
import org.apache.qpid.proton.amqp.Symbol
import org.apache.qpid.proton.codec.Data
import java.lang.reflect.Type
import java.util.function.Function
class SandboxEnumSerializer(
classLoader: SandboxClassLoader,
taskFactory: Function<in Any, out Function<in Any?, out Any?>>,
private val localFactory: LocalSerializerFactory
) : CustomSerializer.Implements<Any>(clazz = classLoader.toSandboxAnyClass(Enum::class.java)) {
@Suppress("unchecked_cast")
private val describer: Function<Class<*>, Array<Any>>
= classLoader.createTaskFor(taskFactory, DescribeEnum::class.java) as Function<Class<*>, Array<Any>>
override val schemaForDocumentation: Schema = Schema(emptyList())
override fun specialiseFor(declaredType: Type): AMQPSerializer<Any>? {
if (declaredType !is Class<*>) {
return null
}
val members = describer.apply(declaredType)
return ConcreteEnumSerializer(declaredType, members, localFactory)
}
override fun readObject(
obj: Any, schemas: SerializationSchemas, input: DeserializationInput, context: SerializationContext
): Any {
throw UnsupportedOperationException("Factory only")
}
override fun writeDescribedObject(
obj: Any, data: Data, type: Type, output: SerializationOutput, context: SerializationContext
) {
throw UnsupportedOperationException("Factory Only")
}
}
private class ConcreteEnumSerializer(
declaredType: Class<*>,
private val members: Array<Any>,
factory: LocalSerializerFactory
) : AMQPSerializer<Any> {
override val type: Class<*> = declaredType
override val typeDescriptor: Symbol by lazy {
factory.createDescriptor(
/*
* Partially populated, providing just the information
* required by the fingerprinter.
*/
LocalTypeInformation.AnEnum(
declaredType,
TypeIdentifier.forGenericType(declaredType),
members.map { it.toString() },
emptyList(),
EnumTransforms.empty
)
)
}
override fun readObject(
obj: Any, schemas: SerializationSchemas, input: DeserializationInput, context: SerializationContext
): Any {
val enumName = (obj as List<*>)[0] as String
val enumOrd = obj[1] as Int
val fromOrd = members[enumOrd]
if (enumName != fromOrd.toString()) {
throw AMQPNotSerializableException(
type,
"Deserializing obj as enum $type with value $enumName.$enumOrd but ordinality has changed"
)
}
return fromOrd
}
override fun writeClassInfo(output: SerializationOutput) {
abortReadOnly()
}
override fun writeObject(
obj: Any, data: Data, type: Type, output: SerializationOutput, context: SerializationContext, debugIndent: Int
) {
abortReadOnly()
}
}

View File

@ -0,0 +1,35 @@
package net.corda.serialization.djvm.serializers
import net.corda.djvm.rewiring.SandboxClassLoader
import net.corda.serialization.djvm.deserializers.EnumSetDeserializer
import net.corda.serialization.djvm.toSandboxAnyClass
import net.corda.serialization.internal.amqp.CustomSerializer
import net.corda.serialization.internal.amqp.SerializerFactory
import net.corda.serialization.internal.amqp.custom.EnumSetSerializer.EnumSetProxy
import java.util.Collections.singleton
import java.util.EnumSet
import java.util.function.Function
class SandboxEnumSetSerializer(
classLoader: SandboxClassLoader,
taskFactory: Function<in Any, out Function<in Any?, out Any?>>,
factory: SerializerFactory
) : CustomSerializer.Proxy<Any, Any>(
clazz = classLoader.toSandboxAnyClass(EnumSet::class.java),
proxyClass = classLoader.toSandboxAnyClass(EnumSetProxy::class.java),
factory = factory
) {
private val task = classLoader.createTaskFor(taskFactory, EnumSetDeserializer::class.java)
override val additionalSerializers: Set<CustomSerializer<out Any>> = singleton(
SandboxClassSerializer(classLoader, taskFactory, factory)
)
override val deserializationAliases: Set<Class<*>> = singleton(EnumSet::class.java)
override fun toProxy(obj: Any): Any = abortReadOnly()
override fun fromProxy(proxy: Any): Any {
return task.apply(proxy)!!
}
}

View File

@ -0,0 +1,38 @@
package net.corda.serialization.djvm.serializers
import net.corda.core.serialization.SerializationContext
import net.corda.djvm.rewiring.SandboxClassLoader
import net.corda.serialization.djvm.deserializers.InputStreamDeserializer
import net.corda.serialization.djvm.toSandboxAnyClass
import net.corda.serialization.internal.amqp.CustomSerializer
import net.corda.serialization.internal.amqp.DeserializationInput
import net.corda.serialization.internal.amqp.Schema
import net.corda.serialization.internal.amqp.SerializationOutput
import net.corda.serialization.internal.amqp.SerializationSchemas
import org.apache.qpid.proton.codec.Data
import java.io.InputStream
import java.lang.reflect.Type
import java.util.Collections.singleton
import java.util.function.Function
class SandboxInputStreamSerializer(
classLoader: SandboxClassLoader,
taskFactory: Function<in Any, out Function<in Any?, out Any?>>
) : CustomSerializer.Implements<Any>(classLoader.toSandboxAnyClass(InputStream::class.java)) {
@Suppress("unchecked_cast")
private val decoder: Function<ByteArray, out Any?>
= classLoader.createTaskFor(taskFactory, InputStreamDeserializer::class.java) as Function<ByteArray, out Any?>
override val schemaForDocumentation: Schema = Schema(emptyList())
override val deserializationAliases: Set<Class<*>> = singleton(InputStream::class.java)
override fun readObject(obj: Any, schemas: SerializationSchemas, input: DeserializationInput, context: SerializationContext): Any {
val bits = input.readObject(obj, schemas, ByteArray::class.java, context) as ByteArray
return decoder.apply(bits)!!
}
override fun writeDescribedObject(obj: Any, data: Data, type: Type, output: SerializationOutput, context: SerializationContext) {
abortReadOnly()
}
}

View File

@ -0,0 +1,31 @@
package net.corda.serialization.djvm.serializers
import net.corda.djvm.rewiring.SandboxClassLoader
import net.corda.serialization.djvm.deserializers.InstantDeserializer
import net.corda.serialization.djvm.toSandboxAnyClass
import net.corda.serialization.internal.amqp.CustomSerializer
import net.corda.serialization.internal.amqp.SerializerFactory
import net.corda.serialization.internal.amqp.custom.InstantSerializer.InstantProxy
import java.time.Instant
import java.util.Collections.singleton
import java.util.function.Function
class SandboxInstantSerializer(
classLoader: SandboxClassLoader,
taskFactory: Function<in Any, out Function<in Any?, out Any?>>,
factory: SerializerFactory
) : CustomSerializer.Proxy<Any, Any>(
clazz = classLoader.toSandboxAnyClass(Instant::class.java),
proxyClass = classLoader.toSandboxAnyClass(InstantProxy::class.java),
factory = factory
) {
private val task = classLoader.createTaskFor(taskFactory, InstantDeserializer::class.java)
override val deserializationAliases: Set<Class<*>> = singleton(Instant::class.java)
override fun toProxy(obj: Any): Any = abortReadOnly()
override fun fromProxy(proxy: Any): Any {
return task.apply(proxy)!!
}
}

View File

@ -0,0 +1,31 @@
package net.corda.serialization.djvm.serializers
import net.corda.djvm.rewiring.SandboxClassLoader
import net.corda.serialization.djvm.deserializers.LocalDateDeserializer
import net.corda.serialization.djvm.toSandboxAnyClass
import net.corda.serialization.internal.amqp.CustomSerializer
import net.corda.serialization.internal.amqp.SerializerFactory
import net.corda.serialization.internal.amqp.custom.LocalDateSerializer.LocalDateProxy
import java.time.LocalDate
import java.util.Collections.singleton
import java.util.function.Function
class SandboxLocalDateSerializer(
classLoader: SandboxClassLoader,
taskFactory: Function<in Any, out Function<in Any?, out Any?>>,
factory: SerializerFactory
) : CustomSerializer.Proxy<Any, Any>(
clazz = classLoader.toSandboxAnyClass(LocalDate::class.java),
proxyClass = classLoader.toSandboxAnyClass(LocalDateProxy::class.java),
factory = factory
) {
private val task = classLoader.createTaskFor(taskFactory, LocalDateDeserializer::class.java)
override val deserializationAliases: Set<Class<*>> = singleton(LocalDate::class.java)
override fun toProxy(obj: Any): Any = abortReadOnly()
override fun fromProxy(proxy: Any): Any {
return task.apply(proxy)!!
}
}

View File

@ -0,0 +1,31 @@
package net.corda.serialization.djvm.serializers
import net.corda.djvm.rewiring.SandboxClassLoader
import net.corda.serialization.djvm.deserializers.LocalDateTimeDeserializer
import net.corda.serialization.djvm.toSandboxAnyClass
import net.corda.serialization.internal.amqp.CustomSerializer
import net.corda.serialization.internal.amqp.SerializerFactory
import net.corda.serialization.internal.amqp.custom.LocalDateTimeSerializer.LocalDateTimeProxy
import java.time.LocalDateTime
import java.util.Collections.singleton
import java.util.function.Function
class SandboxLocalDateTimeSerializer(
classLoader: SandboxClassLoader,
taskFactory: Function<in Any, out Function<in Any?, out Any?>>,
factory: SerializerFactory
) : CustomSerializer.Proxy<Any, Any>(
clazz = classLoader.toSandboxAnyClass(LocalDateTime::class.java),
proxyClass = classLoader.toSandboxAnyClass(LocalDateTimeProxy::class.java),
factory = factory
) {
private val task = classLoader.createTaskFor(taskFactory, LocalDateTimeDeserializer::class.java)
override val deserializationAliases: Set<Class<*>> = singleton(LocalDateTime::class.java)
override fun toProxy(obj: Any): Any = abortReadOnly()
override fun fromProxy(proxy: Any): Any {
return task.apply(proxy)!!
}
}

View File

@ -0,0 +1,31 @@
package net.corda.serialization.djvm.serializers
import net.corda.djvm.rewiring.SandboxClassLoader
import net.corda.serialization.djvm.deserializers.LocalTimeDeserializer
import net.corda.serialization.djvm.toSandboxAnyClass
import net.corda.serialization.internal.amqp.CustomSerializer
import net.corda.serialization.internal.amqp.SerializerFactory
import net.corda.serialization.internal.amqp.custom.LocalTimeSerializer.LocalTimeProxy
import java.time.LocalTime
import java.util.Collections.singleton
import java.util.function.Function
class SandboxLocalTimeSerializer(
classLoader: SandboxClassLoader,
taskFactory: Function<in Any, out Function<in Any?, out Any?>>,
factory: SerializerFactory
) : CustomSerializer.Proxy<Any, Any>(
clazz = classLoader.toSandboxAnyClass(LocalTime::class.java),
proxyClass = classLoader.toSandboxAnyClass(LocalTimeProxy::class.java),
factory = factory
) {
private val task = classLoader.createTaskFor(taskFactory, LocalTimeDeserializer::class.java)
override val deserializationAliases: Set<Class<*>> = singleton(LocalTime::class.java)
override fun toProxy(obj: Any): Any = abortReadOnly()
override fun fromProxy(proxy: Any): Any {
return task.apply(proxy)!!
}
}

View File

@ -0,0 +1,124 @@
package net.corda.serialization.djvm.serializers
import net.corda.core.serialization.SerializationContext
import net.corda.djvm.rewiring.SandboxClassLoader
import net.corda.serialization.djvm.deserializers.CreateMap
import net.corda.serialization.djvm.toSandboxAnyClass
import net.corda.serialization.internal.amqp.AMQPSerializer
import net.corda.serialization.internal.amqp.CustomSerializer
import net.corda.serialization.internal.amqp.DeserializationInput
import net.corda.serialization.internal.amqp.LocalSerializerFactory
import net.corda.serialization.internal.amqp.Schema
import net.corda.serialization.internal.amqp.SerializationOutput
import net.corda.serialization.internal.amqp.SerializationSchemas
import net.corda.serialization.internal.amqp.redescribe
import net.corda.serialization.internal.model.LocalTypeInformation
import net.corda.serialization.internal.model.TypeIdentifier
import org.apache.qpid.proton.amqp.Symbol
import org.apache.qpid.proton.codec.Data
import java.lang.reflect.ParameterizedType
import java.lang.reflect.Type
import java.util.EnumMap
import java.util.NavigableMap
import java.util.SortedMap
import java.util.TreeMap
import java.util.function.Function
class SandboxMapSerializer(
classLoader: SandboxClassLoader,
taskFactory: Function<in Any, out Function<in Any?, out Any?>>,
private val localFactory: LocalSerializerFactory
) : CustomSerializer.Implements<Any>(clazz = classLoader.toSandboxAnyClass(Map::class.java)) {
@Suppress("unchecked_cast")
private val creator: Function<Array<Any>, out Any?>
= classLoader.createTaskFor(taskFactory, CreateMap::class.java) as Function<Array<Any>, out Any?>
// The order matters here - the first match should be the most specific one.
// Kotlin preserves the ordering for us by associating into a LinkedHashMap.
private val supportedTypes: Map<Class<Any>, Class<out Map<*, *>>> = listOf(
TreeMap::class.java,
LinkedHashMap::class.java,
NavigableMap::class.java,
SortedMap::class.java,
EnumMap::class.java,
Map::class.java
).associateBy {
classLoader.toSandboxAnyClass(it)
}
private fun getBestMatchFor(type: Class<Any>): Map.Entry<Class<Any>, Class<out Map<*, *>>>
= supportedTypes.entries.first { it.key.isAssignableFrom(type) }
override val schemaForDocumentation: Schema = Schema(emptyList())
override fun specialiseFor(declaredType: Type): AMQPSerializer<Any>? {
if (declaredType !is ParameterizedType) {
return null
}
@Suppress("unchecked_cast")
val rawType = declaredType.rawType as Class<Any>
return ConcreteMapSerializer(declaredType, getBestMatchFor(rawType), creator, localFactory)
}
override fun readObject(
obj: Any, schemas: SerializationSchemas, input: DeserializationInput, context: SerializationContext
): Any {
throw UnsupportedOperationException("Factory only")
}
override fun writeDescribedObject(
obj: Any, data: Data, type: Type, output: SerializationOutput, context: SerializationContext
) {
throw UnsupportedOperationException("Factory Only")
}
}
private class ConcreteMapSerializer(
declaredType: ParameterizedType,
private val matchingType: Map.Entry<Class<Any>, Class<out Map<*, *>>>,
private val creator: Function<Array<Any>, out Any?>,
factory: LocalSerializerFactory
) : AMQPSerializer<Any> {
override val type: ParameterizedType = declaredType
override val typeDescriptor: Symbol by lazy {
factory.createDescriptor(
LocalTypeInformation.AMap(
observedType = declaredType.rawType,
typeIdentifier = TypeIdentifier.forGenericType(declaredType),
keyType =factory.getTypeInformation(declaredType.actualTypeArguments[0]),
valueType = factory.getTypeInformation(declaredType.actualTypeArguments[1])
)
)
}
override fun readObject(
obj: Any,
schemas: SerializationSchemas,
input: DeserializationInput,
context: SerializationContext
): Any {
val inboundKeyType = type.actualTypeArguments[0]
val inboundValueType = type.actualTypeArguments[1]
return ifThrowsAppend({ type.typeName }) {
val entries = (obj as Map<*, *>).map {
arrayOf(
input.readObjectOrNull(redescribe(it.key, inboundKeyType), schemas, inboundKeyType, context),
input.readObjectOrNull(redescribe(it.value, inboundValueType), schemas, inboundValueType, context)
)
}.toTypedArray()
creator.apply(arrayOf(matchingType.key, entries))!!
}
}
override fun writeClassInfo(output: SerializationOutput) {
abortReadOnly()
}
override fun writeObject(
obj: Any, data: Data, type: Type, output: SerializationOutput, context: SerializationContext, debugIndent: Int
) {
abortReadOnly()
}
}

View File

@ -0,0 +1,31 @@
package net.corda.serialization.djvm.serializers
import net.corda.djvm.rewiring.SandboxClassLoader
import net.corda.serialization.djvm.deserializers.MonthDayDeserializer
import net.corda.serialization.djvm.toSandboxAnyClass
import net.corda.serialization.internal.amqp.CustomSerializer
import net.corda.serialization.internal.amqp.SerializerFactory
import net.corda.serialization.internal.amqp.custom.MonthDaySerializer.MonthDayProxy
import java.time.MonthDay
import java.util.Collections.singleton
import java.util.function.Function
class SandboxMonthDaySerializer(
classLoader: SandboxClassLoader,
taskFactory: Function<in Any, out Function<in Any?, out Any?>>,
factory: SerializerFactory
) : CustomSerializer.Proxy<Any, Any>(
clazz = classLoader.toSandboxAnyClass(MonthDay::class.java),
proxyClass = classLoader.toSandboxAnyClass(MonthDayProxy::class.java),
factory = factory
) {
private val task = classLoader.createTaskFor(taskFactory, MonthDayDeserializer::class.java)
override val deserializationAliases: Set<Class<*>> = singleton(MonthDay::class.java)
override fun toProxy(obj: Any): Any = abortReadOnly()
override fun fromProxy(proxy: Any): Any {
return task.apply(proxy)!!
}
}

View File

@ -0,0 +1,31 @@
package net.corda.serialization.djvm.serializers
import net.corda.djvm.rewiring.SandboxClassLoader
import net.corda.serialization.djvm.deserializers.OffsetDateTimeDeserializer
import net.corda.serialization.djvm.toSandboxAnyClass
import net.corda.serialization.internal.amqp.CustomSerializer
import net.corda.serialization.internal.amqp.SerializerFactory
import net.corda.serialization.internal.amqp.custom.OffsetDateTimeSerializer.OffsetDateTimeProxy
import java.time.OffsetDateTime
import java.util.Collections.singleton
import java.util.function.Function
class SandboxOffsetDateTimeSerializer(
classLoader: SandboxClassLoader,
taskFactory: Function<in Any, out Function<in Any?, out Any?>>,
factory: SerializerFactory
) : CustomSerializer.Proxy<Any, Any>(
clazz = classLoader.toSandboxAnyClass(OffsetDateTime::class.java),
proxyClass = classLoader.toSandboxAnyClass(OffsetDateTimeProxy::class.java),
factory = factory
) {
private val task = classLoader.createTaskFor(taskFactory, OffsetDateTimeDeserializer::class.java)
override val deserializationAliases: Set<Class<*>> = singleton(OffsetDateTime::class.java)
override fun toProxy(obj: Any): Any = abortReadOnly()
override fun fromProxy(proxy: Any): Any {
return task.apply(proxy)!!
}
}

View File

@ -0,0 +1,31 @@
package net.corda.serialization.djvm.serializers
import net.corda.djvm.rewiring.SandboxClassLoader
import net.corda.serialization.djvm.deserializers.OffsetTimeDeserializer
import net.corda.serialization.djvm.toSandboxAnyClass
import net.corda.serialization.internal.amqp.CustomSerializer
import net.corda.serialization.internal.amqp.SerializerFactory
import net.corda.serialization.internal.amqp.custom.OffsetTimeSerializer.OffsetTimeProxy
import java.time.OffsetTime
import java.util.Collections.singleton
import java.util.function.Function
class SandboxOffsetTimeSerializer(
classLoader: SandboxClassLoader,
taskFactory: Function<in Any, out Function<in Any?, out Any?>>,
factory: SerializerFactory
) : CustomSerializer.Proxy<Any, Any>(
clazz = classLoader.toSandboxAnyClass(OffsetTime::class.java),
proxyClass = classLoader.toSandboxAnyClass(OffsetTimeProxy::class.java),
factory = factory
) {
private val task = classLoader.createTaskFor(taskFactory, OffsetTimeDeserializer::class.java)
override val deserializationAliases: Set<Class<*>> = singleton(OffsetTime::class.java)
override fun toProxy(obj: Any): Any = abortReadOnly()
override fun fromProxy(proxy: Any): Any {
return task.apply(proxy)!!
}
}

View File

@ -0,0 +1,31 @@
package net.corda.serialization.djvm.serializers
import net.corda.core.utilities.OpaqueBytes
import net.corda.core.utilities.OpaqueBytesSubSequence
import net.corda.djvm.rewiring.SandboxClassLoader
import net.corda.serialization.djvm.deserializers.OpaqueBytesSubSequenceDeserializer
import net.corda.serialization.djvm.toSandboxAnyClass
import net.corda.serialization.internal.amqp.CustomSerializer
import net.corda.serialization.internal.amqp.SerializerFactory
import java.util.Collections.singleton
import java.util.function.Function
class SandboxOpaqueBytesSubSequenceSerializer(
classLoader: SandboxClassLoader,
taskFactory: Function<in Any, out Function<in Any?, out Any?>>,
factory: SerializerFactory
) : CustomSerializer.Proxy<Any, Any>(
clazz = classLoader.toSandboxAnyClass(OpaqueBytesSubSequence::class.java),
proxyClass = classLoader.toSandboxAnyClass(OpaqueBytes::class.java),
factory = factory
) {
private val task = classLoader.createTaskFor(taskFactory, OpaqueBytesSubSequenceDeserializer::class.java)
override val deserializationAliases: Set<Class<*>> = singleton(OpaqueBytesSubSequence::class.java)
override fun toProxy(obj: Any): Any = abortReadOnly()
override fun fromProxy(proxy: Any): Any {
return task.apply(proxy)!!
}
}

View File

@ -0,0 +1,31 @@
package net.corda.serialization.djvm.serializers
import net.corda.djvm.rewiring.SandboxClassLoader
import net.corda.serialization.djvm.deserializers.OptionalDeserializer
import net.corda.serialization.djvm.toSandboxAnyClass
import net.corda.serialization.internal.amqp.CustomSerializer
import net.corda.serialization.internal.amqp.SerializerFactory
import net.corda.serialization.internal.amqp.custom.OptionalSerializer.OptionalProxy
import java.util.Collections.singleton
import java.util.Optional
import java.util.function.Function
class SandboxOptionalSerializer(
classLoader: SandboxClassLoader,
taskFactory: Function<in Any, out Function<in Any?, out Any?>>,
factory: SerializerFactory
) : CustomSerializer.Proxy<Any, Any>(
clazz = classLoader.toSandboxAnyClass(Optional::class.java),
proxyClass = classLoader.toSandboxAnyClass(OptionalProxy::class.java),
factory = factory
) {
private val task = classLoader.createTaskFor(taskFactory, OptionalDeserializer::class.java)
override val deserializationAliases: Set<Class<*>> = singleton(Optional::class.java)
override fun toProxy(obj: Any): Any = abortReadOnly()
override fun fromProxy(proxy: Any): Any {
return task.apply(proxy)!!
}
}

View File

@ -0,0 +1,31 @@
package net.corda.serialization.djvm.serializers
import net.corda.djvm.rewiring.SandboxClassLoader
import net.corda.serialization.djvm.deserializers.PeriodDeserializer
import net.corda.serialization.djvm.toSandboxAnyClass
import net.corda.serialization.internal.amqp.CustomSerializer
import net.corda.serialization.internal.amqp.SerializerFactory
import net.corda.serialization.internal.amqp.custom.PeriodSerializer.PeriodProxy
import java.time.Period
import java.util.Collections.singleton
import java.util.function.Function
class SandboxPeriodSerializer(
classLoader: SandboxClassLoader,
taskFactory: Function<in Any, out Function<in Any?, out Any?>>,
factory: SerializerFactory
) : CustomSerializer.Proxy<Any, Any>(
clazz = classLoader.toSandboxAnyClass(Period::class.java),
proxyClass = classLoader.toSandboxAnyClass(PeriodProxy::class.java),
factory = factory
) {
private val task = classLoader.createTaskFor(taskFactory, PeriodDeserializer::class.java)
override val deserializationAliases: Set<Class<*>> = singleton(Period::class.java)
override fun toProxy(obj: Any): Any = abortReadOnly()
override fun fromProxy(proxy: Any): Any {
return task.apply(proxy)!!
}
}

View File

@ -0,0 +1,30 @@
package net.corda.serialization.djvm.serializers
import net.corda.core.serialization.SerializationContext
import net.corda.djvm.rewiring.SandboxClassLoader
import net.corda.serialization.djvm.toSandboxAnyClass
import net.corda.serialization.internal.amqp.CustomSerializer
import net.corda.serialization.internal.amqp.DeserializationInput
import net.corda.serialization.internal.amqp.Schema
import net.corda.serialization.internal.amqp.SerializationOutput
import net.corda.serialization.internal.amqp.SerializationSchemas
import org.apache.qpid.proton.codec.Data
import java.lang.reflect.Type
import java.util.function.Function
class SandboxPrimitiveSerializer(
clazz: Class<*>,
classLoader: SandboxClassLoader,
private val basicInput: Function<in Any?, out Any?>
) : CustomSerializer.Is<Any>(classLoader.toSandboxAnyClass(clazz)) {
override val schemaForDocumentation: Schema = Schema(emptyList())
override fun readObject(obj: Any, schemas: SerializationSchemas, input: DeserializationInput, context: SerializationContext): Any {
return basicInput.apply(obj)!!
}
override fun writeDescribedObject(obj: Any, data: Data, type: Type, output: SerializationOutput, context: SerializationContext) {
abortReadOnly()
}
}

View File

@ -0,0 +1,38 @@
package net.corda.serialization.djvm.serializers
import net.corda.core.serialization.SerializationContext
import net.corda.djvm.rewiring.SandboxClassLoader
import net.corda.serialization.djvm.deserializers.PublicKeyDecoder
import net.corda.serialization.djvm.toSandboxAnyClass
import net.corda.serialization.internal.amqp.CustomSerializer
import net.corda.serialization.internal.amqp.DeserializationInput
import net.corda.serialization.internal.amqp.Schema
import net.corda.serialization.internal.amqp.SerializationOutput
import net.corda.serialization.internal.amqp.SerializationSchemas
import org.apache.qpid.proton.codec.Data
import java.lang.reflect.Type
import java.security.PublicKey
import java.util.Collections.singleton
import java.util.function.Function
class SandboxPublicKeySerializer(
classLoader: SandboxClassLoader,
taskFactory: Function<in Any, out Function<in Any?, out Any?>>
) : CustomSerializer.Implements<Any>(classLoader.toSandboxAnyClass(PublicKey::class.java)) {
@Suppress("unchecked_cast")
private val decoder: Function<ByteArray, out Any?>
= classLoader.createTaskFor(taskFactory, PublicKeyDecoder::class.java) as Function<ByteArray, out Any?>
override val schemaForDocumentation: Schema = Schema(emptyList())
override val deserializationAliases: Set<Class<*>> = singleton(PublicKey::class.java)
override fun readObject(obj: Any, schemas: SerializationSchemas, input: DeserializationInput, context: SerializationContext): Any {
val bits = input.readObject(obj, schemas, ByteArray::class.java, context) as ByteArray
return decoder.apply(bits)!!
}
override fun writeDescribedObject(obj: Any, data: Data, type: Type, output: SerializationOutput, context: SerializationContext) {
abortReadOnly()
}
}

View File

@ -0,0 +1,39 @@
package net.corda.serialization.djvm.serializers
import net.corda.core.serialization.SerializationContext
import net.corda.djvm.rewiring.SandboxClassLoader
import net.corda.serialization.djvm.deserializers.SymbolDeserializer
import net.corda.serialization.djvm.toSandboxAnyClass
import net.corda.serialization.internal.amqp.CustomSerializer
import net.corda.serialization.internal.amqp.DeserializationInput
import net.corda.serialization.internal.amqp.Schema
import net.corda.serialization.internal.amqp.SerializationOutput
import net.corda.serialization.internal.amqp.SerializationSchemas
import org.apache.qpid.proton.amqp.Symbol
import org.apache.qpid.proton.codec.Data
import java.lang.reflect.Type
import java.util.function.Function
class SandboxSymbolSerializer(
classLoader: SandboxClassLoader,
taskFactory: Function<in Any, out Function<in Any?, out Any?>>,
basicInput: Function<in Any?, out Any?>
) : CustomSerializer.Is<Any>(classLoader.toSandboxAnyClass(Symbol::class.java)) {
private val transformer: Function<String, out Any?>
init {
val transformTask = classLoader.createTaskFor(taskFactory, SymbolDeserializer::class.java)
@Suppress("unchecked_cast")
transformer = basicInput.andThen(transformTask) as Function<String, Any?>
}
override val schemaForDocumentation: Schema = Schema(emptyList())
override fun readObject(obj: Any, schemas: SerializationSchemas, input: DeserializationInput, context: SerializationContext): Any {
return transformer.apply((obj as Symbol).toString())!!
}
override fun writeDescribedObject(obj: Any, data: Data, type: Type, output: SerializationOutput, context: SerializationContext) {
abortReadOnly()
}
}

View File

@ -0,0 +1,45 @@
package net.corda.serialization.djvm.serializers
import net.corda.core.serialization.SerializationContext
import net.corda.djvm.rewiring.SandboxClassLoader
import net.corda.serialization.djvm.deserializers.CreateFromString
import net.corda.serialization.djvm.toSandboxAnyClass
import net.corda.serialization.internal.amqp.CustomSerializer
import net.corda.serialization.internal.amqp.DeserializationInput
import net.corda.serialization.internal.amqp.Schema
import net.corda.serialization.internal.amqp.SerializationOutput
import net.corda.serialization.internal.amqp.SerializationSchemas
import org.apache.qpid.proton.codec.Data
import java.lang.reflect.Constructor
import java.lang.reflect.Type
import java.util.Collections.singleton
import java.util.function.Function
class SandboxToStringSerializer(
unsafeClass: Class<*>,
classLoader: SandboxClassLoader,
taskFactory: Function<in Any, out Function<in Any?, out Any?>>,
basicInput: Function<in Any?, out Any?>
) : CustomSerializer.Is<Any>(classLoader.toSandboxAnyClass(unsafeClass)) {
private val creator: Function<Any?, Any?>
init {
val stringClass = classLoader.loadClass("sandbox.java.lang.String")
val createTask = classLoader.toSandboxClass(CreateFromString::class.java)
.getConstructor(Constructor::class.java)
.newInstance(clazz.getConstructor(stringClass))
creator = basicInput.andThen(taskFactory.apply(createTask))
}
override val deserializationAliases: Set<Class<*>> = singleton(unsafeClass)
override val schemaForDocumentation: Schema = Schema(emptyList())
override fun readObject(obj: Any, schemas: SerializationSchemas, input: DeserializationInput, context: SerializationContext): Any {
return creator.apply(obj)!!
}
override fun writeDescribedObject(obj: Any, data: Data, type: Type, output: SerializationOutput, context: SerializationContext) {
abortReadOnly()
}
}

View File

@ -0,0 +1,34 @@
package net.corda.serialization.djvm.serializers
import net.corda.core.serialization.SerializationContext
import net.corda.djvm.rewiring.SandboxClassLoader
import net.corda.serialization.djvm.deserializers.UnsignedByteDeserializer
import net.corda.serialization.djvm.toSandboxAnyClass
import net.corda.serialization.internal.amqp.CustomSerializer
import net.corda.serialization.internal.amqp.DeserializationInput
import net.corda.serialization.internal.amqp.Schema
import net.corda.serialization.internal.amqp.SerializationOutput
import net.corda.serialization.internal.amqp.SerializationSchemas
import org.apache.qpid.proton.amqp.UnsignedByte
import org.apache.qpid.proton.codec.Data
import java.lang.reflect.Type
import java.util.function.Function
class SandboxUnsignedByteSerializer(
classLoader: SandboxClassLoader,
taskFactory: Function<in Any, out Function<in Any?, out Any?>>
) : CustomSerializer.Is<Any>(classLoader.toSandboxAnyClass(UnsignedByte::class.java)) {
@Suppress("unchecked_cast")
private val transformer: Function<ByteArray, out Any?>
= classLoader.createTaskFor(taskFactory, UnsignedByteDeserializer::class.java) as Function<ByteArray, out Any?>
override val schemaForDocumentation: Schema = Schema(emptyList())
override fun readObject(obj: Any, schemas: SerializationSchemas, input: DeserializationInput, context: SerializationContext): Any {
return transformer.apply(byteArrayOf((obj as UnsignedByte).toByte()))!!
}
override fun writeDescribedObject(obj: Any, data: Data, type: Type, output: SerializationOutput, context: SerializationContext) {
abortReadOnly()
}
}

View File

@ -0,0 +1,34 @@
package net.corda.serialization.djvm.serializers
import net.corda.core.serialization.SerializationContext
import net.corda.djvm.rewiring.SandboxClassLoader
import net.corda.serialization.djvm.deserializers.UnsignedIntegerDeserializer
import net.corda.serialization.djvm.toSandboxAnyClass
import net.corda.serialization.internal.amqp.CustomSerializer
import net.corda.serialization.internal.amqp.DeserializationInput
import net.corda.serialization.internal.amqp.Schema
import net.corda.serialization.internal.amqp.SerializationOutput
import net.corda.serialization.internal.amqp.SerializationSchemas
import org.apache.qpid.proton.amqp.UnsignedInteger
import org.apache.qpid.proton.codec.Data
import java.lang.reflect.Type
import java.util.function.Function
class SandboxUnsignedIntegerSerializer(
classLoader: SandboxClassLoader,
taskFactory: Function<in Any, out Function<in Any?, out Any?>>
) : CustomSerializer.Is<Any>(classLoader.toSandboxAnyClass(UnsignedInteger::class.java)) {
@Suppress("unchecked_cast")
private val transformer: Function<IntArray, out Any?>
= classLoader.createTaskFor(taskFactory, UnsignedIntegerDeserializer::class.java) as Function<IntArray, out Any?>
override val schemaForDocumentation: Schema = Schema(emptyList())
override fun readObject(obj: Any, schemas: SerializationSchemas, input: DeserializationInput, context: SerializationContext): Any {
return transformer.apply(intArrayOf((obj as UnsignedInteger).toInt()))!!
}
override fun writeDescribedObject(obj: Any, data: Data, type: Type, output: SerializationOutput, context: SerializationContext) {
abortReadOnly()
}
}

View File

@ -0,0 +1,34 @@
package net.corda.serialization.djvm.serializers
import net.corda.core.serialization.SerializationContext
import net.corda.djvm.rewiring.SandboxClassLoader
import net.corda.serialization.djvm.deserializers.UnsignedLongDeserializer
import net.corda.serialization.djvm.toSandboxAnyClass
import net.corda.serialization.internal.amqp.CustomSerializer
import net.corda.serialization.internal.amqp.DeserializationInput
import net.corda.serialization.internal.amqp.Schema
import net.corda.serialization.internal.amqp.SerializationOutput
import net.corda.serialization.internal.amqp.SerializationSchemas
import org.apache.qpid.proton.amqp.UnsignedLong
import org.apache.qpid.proton.codec.Data
import java.lang.reflect.Type
import java.util.function.Function
class SandboxUnsignedLongSerializer(
classLoader: SandboxClassLoader,
taskFactory: Function<in Any, out Function<in Any?, out Any?>>
) : CustomSerializer.Is<Any>(classLoader.toSandboxAnyClass(UnsignedLong::class.java)) {
@Suppress("unchecked_cast")
private val transformer: Function<LongArray, out Any?>
= classLoader.createTaskFor(taskFactory, UnsignedLongDeserializer::class.java) as Function<LongArray, out Any?>
override val schemaForDocumentation: Schema = Schema(emptyList())
override fun readObject(obj: Any, schemas: SerializationSchemas, input: DeserializationInput, context: SerializationContext): Any {
return transformer.apply(longArrayOf((obj as UnsignedLong).toLong()))!!
}
override fun writeDescribedObject(obj: Any, data: Data, type: Type, output: SerializationOutput, context: SerializationContext) {
abortReadOnly()
}
}

View File

@ -0,0 +1,34 @@
package net.corda.serialization.djvm.serializers
import net.corda.core.serialization.SerializationContext
import net.corda.djvm.rewiring.SandboxClassLoader
import net.corda.serialization.djvm.deserializers.UnsignedShortDeserializer
import net.corda.serialization.djvm.toSandboxAnyClass
import net.corda.serialization.internal.amqp.CustomSerializer
import net.corda.serialization.internal.amqp.DeserializationInput
import net.corda.serialization.internal.amqp.Schema
import net.corda.serialization.internal.amqp.SerializationOutput
import net.corda.serialization.internal.amqp.SerializationSchemas
import org.apache.qpid.proton.amqp.UnsignedShort
import org.apache.qpid.proton.codec.Data
import java.lang.reflect.Type
import java.util.function.Function
class SandboxUnsignedShortSerializer(
classLoader: SandboxClassLoader,
taskFactory: Function<in Any, out Function<in Any?, out Any?>>
) : CustomSerializer.Is<Any>(classLoader.toSandboxAnyClass(UnsignedShort::class.java)) {
@Suppress("unchecked_cast")
private val transformer: Function<ShortArray, out Any?>
= classLoader.createTaskFor(taskFactory, UnsignedShortDeserializer::class.java) as Function<ShortArray, out Any?>
override val schemaForDocumentation: Schema = Schema(emptyList())
override fun readObject(obj: Any, schemas: SerializationSchemas, input: DeserializationInput, context: SerializationContext): Any {
return transformer.apply(shortArrayOf((obj as UnsignedShort).toShort()))!!
}
override fun writeDescribedObject(obj: Any, data: Data, type: Type, output: SerializationOutput, context: SerializationContext) {
abortReadOnly()
}
}

View File

@ -0,0 +1,38 @@
package net.corda.serialization.djvm.serializers
import net.corda.core.serialization.SerializationContext
import net.corda.djvm.rewiring.SandboxClassLoader
import net.corda.serialization.djvm.deserializers.X509CRLDeserializer
import net.corda.serialization.djvm.toSandboxAnyClass
import net.corda.serialization.internal.amqp.CustomSerializer
import net.corda.serialization.internal.amqp.DeserializationInput
import net.corda.serialization.internal.amqp.Schema
import net.corda.serialization.internal.amqp.SerializationOutput
import net.corda.serialization.internal.amqp.SerializationSchemas
import org.apache.qpid.proton.codec.Data
import java.lang.reflect.Type
import java.security.cert.X509CRL
import java.util.Collections.singleton
import java.util.function.Function
class SandboxX509CRLSerializer(
classLoader: SandboxClassLoader,
taskFactory: Function<in Any, out Function<in Any?, out Any?>>
) : CustomSerializer.Implements<Any>(classLoader.toSandboxAnyClass(X509CRL::class.java)) {
@Suppress("unchecked_cast")
private val generator: Function<ByteArray, out Any?>
= classLoader.createTaskFor(taskFactory, X509CRLDeserializer::class.java) as Function<ByteArray, out Any?>
override val schemaForDocumentation: Schema = Schema(emptyList())
override val deserializationAliases: Set<Class<*>> = singleton(X509CRL::class.java)
override fun readObject(obj: Any, schemas: SerializationSchemas, input: DeserializationInput, context: SerializationContext): Any {
val bits = input.readObject(obj, schemas, ByteArray::class.java, context) as ByteArray
return generator.apply(bits)!!
}
override fun writeDescribedObject(obj: Any, data: Data, type: Type, output: SerializationOutput, context: SerializationContext) {
abortReadOnly()
}
}

View File

@ -0,0 +1,38 @@
package net.corda.serialization.djvm.serializers
import net.corda.core.serialization.SerializationContext
import net.corda.djvm.rewiring.SandboxClassLoader
import net.corda.serialization.djvm.deserializers.X509CertificateDeserializer
import net.corda.serialization.djvm.toSandboxAnyClass
import net.corda.serialization.internal.amqp.CustomSerializer
import net.corda.serialization.internal.amqp.DeserializationInput
import net.corda.serialization.internal.amqp.Schema
import net.corda.serialization.internal.amqp.SerializationOutput
import net.corda.serialization.internal.amqp.SerializationSchemas
import org.apache.qpid.proton.codec.Data
import java.lang.reflect.Type
import java.security.cert.X509Certificate
import java.util.Collections.singleton
import java.util.function.Function
class SandboxX509CertificateSerializer(
classLoader: SandboxClassLoader,
taskFactory: Function<in Any, out Function<in Any?, out Any?>>
) : CustomSerializer.Implements<Any>(classLoader.toSandboxAnyClass(X509Certificate::class.java)) {
@Suppress("unchecked_cast")
private val generator: Function<ByteArray, out Any?>
= classLoader.createTaskFor(taskFactory, X509CertificateDeserializer::class.java) as Function<ByteArray, out Any?>
override val schemaForDocumentation: Schema = Schema(emptyList())
override val deserializationAliases: Set<Class<*>> = singleton(X509Certificate::class.java)
override fun readObject(obj: Any, schemas: SerializationSchemas, input: DeserializationInput, context: SerializationContext): Any {
val bits = input.readObject(obj, schemas, ByteArray::class.java, context) as ByteArray
return generator.apply(bits)!!
}
override fun writeDescribedObject(obj: Any, data: Data, type: Type, output: SerializationOutput, context: SerializationContext) {
abortReadOnly()
}
}

View File

@ -0,0 +1,31 @@
package net.corda.serialization.djvm.serializers
import net.corda.djvm.rewiring.SandboxClassLoader
import net.corda.serialization.djvm.deserializers.YearMonthDeserializer
import net.corda.serialization.djvm.toSandboxAnyClass
import net.corda.serialization.internal.amqp.CustomSerializer
import net.corda.serialization.internal.amqp.SerializerFactory
import net.corda.serialization.internal.amqp.custom.YearMonthSerializer.YearMonthProxy
import java.time.YearMonth
import java.util.Collections.singleton
import java.util.function.Function
class SandboxYearMonthSerializer(
classLoader: SandboxClassLoader,
taskFactory: Function<in Any, out Function<in Any?, out Any?>>,
factory: SerializerFactory
) : CustomSerializer.Proxy<Any, Any>(
clazz = classLoader.toSandboxAnyClass(YearMonth::class.java),
proxyClass = classLoader.toSandboxAnyClass(YearMonthProxy::class.java),
factory = factory
) {
private val task = classLoader.createTaskFor(taskFactory, YearMonthDeserializer::class.java)
override val deserializationAliases: Set<Class<*>> = singleton(YearMonth::class.java)
override fun toProxy(obj: Any): Any = abortReadOnly()
override fun fromProxy(proxy: Any): Any {
return task.apply(proxy)!!
}
}

View File

@ -0,0 +1,31 @@
package net.corda.serialization.djvm.serializers
import net.corda.djvm.rewiring.SandboxClassLoader
import net.corda.serialization.djvm.deserializers.YearDeserializer
import net.corda.serialization.djvm.toSandboxAnyClass
import net.corda.serialization.internal.amqp.CustomSerializer
import net.corda.serialization.internal.amqp.SerializerFactory
import net.corda.serialization.internal.amqp.custom.YearSerializer.YearProxy
import java.time.Year
import java.util.Collections.singleton
import java.util.function.Function
class SandboxYearSerializer(
classLoader: SandboxClassLoader,
taskFactory: Function<in Any, out Function<in Any?, out Any?>>,
factory: SerializerFactory
) : CustomSerializer.Proxy<Any, Any>(
clazz = classLoader.toSandboxAnyClass(Year::class.java),
proxyClass = classLoader.toSandboxAnyClass(YearProxy::class.java),
factory = factory
) {
private val task = classLoader.createTaskFor(taskFactory, YearDeserializer::class.java)
override val deserializationAliases: Set<Class<*>> = singleton(Year::class.java)
override fun toProxy(obj: Any): Any = abortReadOnly()
override fun fromProxy(proxy: Any): Any {
return task.apply(proxy)!!
}
}

View File

@ -0,0 +1,33 @@
package net.corda.serialization.djvm.serializers
import net.corda.djvm.rewiring.SandboxClassLoader
import net.corda.serialization.djvm.deserializers.ZoneIdDeserializer
import net.corda.serialization.djvm.toSandboxAnyClass
import net.corda.serialization.internal.amqp.CustomSerializer
import net.corda.serialization.internal.amqp.SerializerFactory
import net.corda.serialization.internal.amqp.custom.ZoneIdSerializer.ZoneIdProxy
import java.time.ZoneId
import java.util.Collections.singleton
import java.util.function.Function
class SandboxZoneIdSerializer(
classLoader: SandboxClassLoader,
taskFactory: Function<in Any, out Function<in Any?, out Any?>>,
factory: SerializerFactory
) : CustomSerializer.Proxy<Any, Any>(
clazz = classLoader.toSandboxAnyClass(ZoneId::class.java),
proxyClass = classLoader.toSandboxAnyClass(ZoneIdProxy::class.java),
factory = factory
) {
private val task = classLoader.createTaskFor(taskFactory, ZoneIdDeserializer::class.java)
override val revealSubclassesInSchema: Boolean = true
override val deserializationAliases: Set<Class<*>> = singleton(ZoneId::class.java)
override fun toProxy(obj: Any): Any = abortReadOnly()
override fun fromProxy(proxy: Any): Any {
return task.apply(proxy)!!
}
}

View File

@ -0,0 +1,48 @@
package net.corda.serialization.djvm.serializers
import net.corda.djvm.rewiring.SandboxClassLoader
import net.corda.serialization.djvm.deserializers.ZonedDateTimeDeserializer
import net.corda.serialization.djvm.toSandboxAnyClass
import net.corda.serialization.internal.amqp.CustomSerializer
import net.corda.serialization.internal.amqp.SerializerFactory
import net.corda.serialization.internal.amqp.custom.ZonedDateTimeSerializer.ZonedDateTimeProxy
import java.time.LocalDateTime
import java.time.ZoneId
import java.time.ZoneOffset
import java.time.ZonedDateTime
import java.util.Collections.singleton
import java.util.function.Function
class SandboxZonedDateTimeSerializer(
classLoader: SandboxClassLoader,
taskFactory: Function<in Any, out Function<in Any?, out Any?>>,
factory: SerializerFactory
) : CustomSerializer.Proxy<Any, Any>(
clazz = classLoader.toSandboxAnyClass(ZonedDateTime::class.java),
proxyClass = classLoader.toSandboxAnyClass(ZonedDateTimeProxy::class.java),
factory = factory
) {
private val task = classLoader.createTaskFor(taskFactory, ZonedDateTimeDeserializer::class.java)
private val creator: Function<in Any?, out Any?>
init {
val createTask = clazz.getMethod(
"createDJVM",
classLoader.toSandboxClass(LocalDateTime::class.java),
classLoader.toSandboxClass(ZoneOffset::class.java),
classLoader.toSandboxClass(ZoneId::class.java)
)
creator = task.andThen { input ->
@Suppress("unchecked_cast", "SpreadOperator")
createTask(null, *(input as Array<Any?>))!!
}
}
override val deserializationAliases: Set<Class<*>> = singleton(ZonedDateTime::class.java)
override fun toProxy(obj: Any): Any = abortReadOnly()
override fun fromProxy(proxy: Any): Any {
return creator.apply(proxy)!!
}
}

View File

@ -0,0 +1,13 @@
package greymalkin;
public class ExternalData {
private final String data;
public ExternalData(String data) {
this.data = data;
}
public String getData() {
return data;
}
}

View File

@ -0,0 +1,11 @@
package greymalkin;
import net.corda.core.serialization.CordaSerializable;
@SuppressWarnings("unused")
@CordaSerializable
public enum ExternalEnum {
DOH,
RAY,
ME
}

View File

@ -0,0 +1,22 @@
package net.corda.serialization.djvm;
import net.corda.core.serialization.CordaSerializable;
@CordaSerializable
public class InnocentData {
private final String message;
private final Short number;
public InnocentData(String message, Short number) {
this.message = message;
this.number = number;
}
public String getMessage() {
return message;
}
public Short getNumber() {
return number;
}
}

View File

@ -0,0 +1,53 @@
package net.corda.serialization.djvm;
import net.corda.core.serialization.ConstructorForDeserialization;
import net.corda.core.serialization.CordaSerializable;
@SuppressWarnings({"unused", "WeakerAccess"})
@CordaSerializable
public class MultiConstructorData {
private final String message;
private final long bigNumber;
private final Character tag;
@ConstructorForDeserialization
public MultiConstructorData(String message, long bigNumber, Character tag) {
this.message = message;
this.bigNumber = bigNumber;
this.tag = tag;
}
public MultiConstructorData(String message, long bigNumber) {
this(message, bigNumber, null);
}
public MultiConstructorData(String message, char tag) {
this(message, 0, tag);
}
public MultiConstructorData(String message) {
this(message, 0);
}
public String getMessage() {
return message;
}
public long getBigNumber() {
return bigNumber;
}
public Character getTag() {
return tag;
}
@SuppressWarnings("StringBufferReplaceableByString")
@Override
public String toString() {
return new StringBuilder("MultiConstructor[message='").append(message)
.append("', bigNumber=").append(bigNumber)
.append(", tag=").append(tag)
.append(']')
.toString();
}
}

View File

@ -0,0 +1,15 @@
package net.corda.serialization.djvm;
@SuppressWarnings("unused")
public class VeryEvilData extends InnocentData {
static {
if (!VeryEvilData.class.getName().startsWith("sandbox.")) {
// Execute our evil payload OUTSIDE the sandbox!
throw new IllegalStateException("Victory is mine!");
}
}
public VeryEvilData(String message, Short number) {
super(message, number);
}
}

View File

@ -0,0 +1,49 @@
package net.corda.serialization.djvm
import net.corda.core.serialization.CordaSerializable
import net.corda.core.serialization.internal._contextSerializationEnv
import net.corda.core.serialization.serialize
import net.corda.serialization.djvm.SandboxType.KOTLIN
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
import org.junit.jupiter.api.fail
import java.math.BigDecimal
import java.util.function.Function
@ExtendWith(LocalSerialization::class)
class DeserializeBigDecimalTest : TestBase(KOTLIN) {
companion object {
const val VERY_BIG_DECIMAL = 994349993939.32737232
}
@Test
fun `test deserializing big decimal`() {
val bigDecimal = BigDecimalData(BigDecimal.valueOf(VERY_BIG_DECIMAL))
val data = bigDecimal.serialize()
sandbox {
_contextSerializationEnv.set(createSandboxSerializationEnv(classLoader))
val sandboxBigInteger = data.deserializeFor(classLoader)
val taskFactory = classLoader.createRawTaskFactory()
val showBigDecimal = classLoader.createTaskFor(taskFactory, ShowBigDecimal::class.java)
val result = showBigDecimal.apply(sandboxBigInteger) ?: fail("Result cannot be null")
assertEquals(ShowBigDecimal().apply(bigDecimal), result.toString())
assertEquals(SANDBOX_STRING, result::class.java.name)
}
}
class ShowBigDecimal : Function<BigDecimalData, String> {
override fun apply(data: BigDecimalData): String {
return with(data) {
"BigDecimal: $number"
}
}
}
}
@CordaSerializable
data class BigDecimalData(val number: BigDecimal)

View File

@ -0,0 +1,49 @@
package net.corda.serialization.djvm
import net.corda.core.serialization.CordaSerializable
import net.corda.core.serialization.internal._contextSerializationEnv
import net.corda.core.serialization.serialize
import net.corda.serialization.djvm.SandboxType.KOTLIN
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
import org.junit.jupiter.api.fail
import java.math.BigInteger
import java.util.function.Function
@ExtendWith(LocalSerialization::class)
class DeserializeBigIntegerTest : TestBase(KOTLIN) {
companion object {
const val VERY_BIG_NUMBER = 1234567890123456789
}
@Test
fun `test deserializing big integer`() {
val bigInteger = BigIntegerData(BigInteger.valueOf(VERY_BIG_NUMBER))
val data = bigInteger.serialize()
sandbox {
_contextSerializationEnv.set(createSandboxSerializationEnv(classLoader))
val sandboxBigInteger = data.deserializeFor(classLoader)
val taskFactory = classLoader.createRawTaskFactory()
val showBigInteger = classLoader.createTaskFor(taskFactory, ShowBigInteger::class.java)
val result = showBigInteger.apply(sandboxBigInteger) ?: fail("Result cannot be null")
assertEquals(ShowBigInteger().apply(bigInteger), result.toString())
assertEquals(SANDBOX_STRING, result::class.java.name)
}
}
class ShowBigInteger : Function<BigIntegerData, String> {
override fun apply(data: BigIntegerData): String {
return with(data) {
"BigInteger: $number"
}
}
}
}
@CordaSerializable
data class BigIntegerData(val number: BigInteger)

View File

@ -0,0 +1,39 @@
package net.corda.serialization.djvm
import net.corda.core.serialization.internal._contextSerializationEnv
import net.corda.core.serialization.serialize
import net.corda.serialization.djvm.SandboxType.KOTLIN
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
import org.junit.jupiter.api.fail
import java.util.BitSet
import java.util.function.Function
@ExtendWith(LocalSerialization::class)
class DeserializeBitSetTest : TestBase(KOTLIN) {
@Test
fun `test deserializing bitset`() {
val bitSet = BitSet.valueOf(byteArrayOf(0x00, 0x70, 0x55, 0x3A, 0x48, 0x12))
val data = bitSet.serialize()
sandbox {
_contextSerializationEnv.set(createSandboxSerializationEnv(classLoader))
val sandboxBitSet = data.deserializeFor(classLoader)
val taskFactory = classLoader.createRawTaskFactory()
val showBitSet = classLoader.createTaskFor(taskFactory, ShowBitSet::class.java)
val result = showBitSet.apply(sandboxBitSet) ?: fail("Result cannot be null")
assertEquals(bitSet.toString(), result.toString())
assertEquals(SANDBOX_STRING, result::class.java.name)
}
}
class ShowBitSet : Function<BitSet, String> {
override fun apply(bitSet: BitSet): String {
return bitSet.toString()
}
}
}

View File

@ -0,0 +1,129 @@
package net.corda.serialization.djvm
import net.corda.core.serialization.internal._contextSerializationEnv
import net.corda.core.serialization.serialize
import net.corda.serialization.djvm.SandboxType.KOTLIN
import org.assertj.core.api.Assertions.assertThat
import org.bouncycastle.asn1.x509.CRLReason
import org.bouncycastle.asn1.x500.X500Name
import org.bouncycastle.cert.X509v2CRLBuilder
import org.bouncycastle.cert.jcajce.JcaX509CRLConverter
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.BeforeAll
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
import org.junit.jupiter.api.fail
import java.security.KeyPairGenerator
import java.security.cert.CertPath
import java.security.cert.CertificateFactory
import java.security.cert.X509CRL
import java.security.cert.X509Certificate
import java.util.Date
import java.util.function.Function
@ExtendWith(LocalSerialization::class)
class DeserializeCertificatesTest : TestBase(KOTLIN) {
companion object {
// The sandbox's localisation may not match that of the host.
// E.g. line separator characters.
fun String.toUNIX(): String {
return replace(System.lineSeparator(), "\n")
}
val factory: CertificateFactory = CertificateFactory.getInstance("X.509")
lateinit var certificate: X509Certificate
@Suppress("unused")
@BeforeAll
@JvmStatic
fun loadCertificate() {
certificate = this::class.java.classLoader.getResourceAsStream("testing.cert")?.use { input ->
factory.generateCertificate(input) as X509Certificate
} ?: fail("Certificate not found")
}
}
@Test
fun `test deserialize certificate path`() {
val certPath = factory.generateCertPath(listOf(certificate))
val data = certPath.serialize()
sandbox {
_contextSerializationEnv.set(createSandboxSerializationEnv(classLoader))
val sandboxCertPath = data.deserializeFor(classLoader)
val taskFactory = classLoader.createRawTaskFactory()
val showCertPath = classLoader.createTaskFor(taskFactory, ShowCertPath::class.java)
val result = showCertPath.apply(sandboxCertPath) ?: fail("Result cannot be null")
assertEquals(ShowCertPath().apply(certPath).toUNIX(), result.toString())
assertThat(result::class.java.name).startsWith("sandbox.")
}
}
class ShowCertPath : Function<CertPath, String> {
override fun apply(certPath: CertPath): String {
return "CertPath -> $certPath"
}
}
@Test
fun `test deserialize X509 certificate`() {
val data = certificate.serialize()
sandbox {
_contextSerializationEnv.set(createSandboxSerializationEnv(classLoader))
val sandboxCertificate = data.deserializeFor(classLoader)
val taskFactory = classLoader.createRawTaskFactory()
val showCertificate = classLoader.createTaskFor(taskFactory, ShowCertificate::class.java)
val result = showCertificate.apply(sandboxCertificate) ?: fail("Result cannot be null")
assertEquals(ShowCertificate().apply(certificate).toUNIX(), result.toString())
assertThat(result::class.java.name).startsWith("sandbox.")
}
}
class ShowCertificate : Function<X509Certificate, String> {
override fun apply(certificate: X509Certificate): String {
return "X.509 Certificate -> $certificate"
}
}
@Test
fun `test X509 CRL`() {
val caKeyPair = KeyPairGenerator.getInstance("RSA")
.generateKeyPair()
val signer = JcaContentSignerBuilder("SHA256WithRSAEncryption")
.build(caKeyPair.private)
val now = Date()
val crl = with(X509v2CRLBuilder(X500Name("CN=Test CA"), now)) {
addCRLEntry(certificate.serialNumber, now, CRLReason.privilegeWithdrawn)
JcaX509CRLConverter().getCRL(build(signer))
}
val data = crl.serialize()
sandbox {
_contextSerializationEnv.set(createSandboxSerializationEnv(classLoader))
val sandboxCRL = data.deserializeFor(classLoader)
val taskFactory = classLoader.createRawTaskFactory()
val showCRL = classLoader.createTaskFor(taskFactory, ShowCRL::class.java)
val result = showCRL.apply(sandboxCRL) ?: fail("Result cannot be null")
assertEquals(ShowCRL().apply(crl).toUNIX(), result.toString())
assertThat(result::class.java.name).startsWith("sandbox.")
}
}
class ShowCRL : Function<X509CRL, String> {
override fun apply(crl: X509CRL): String {
return "X.509 CRL -> $crl"
}
}
}

View File

@ -0,0 +1,59 @@
package net.corda.serialization.djvm
import greymalkin.ExternalData
import net.corda.core.serialization.internal._contextSerializationEnv
import net.corda.core.serialization.serialize
import net.corda.djvm.messages.Severity
import net.corda.serialization.djvm.SandboxType.KOTLIN
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertThrows
import org.junit.jupiter.api.extension.ExtendWith
import org.junit.jupiter.api.fail
import java.io.NotSerializableException
import java.util.function.Function
@ExtendWith(LocalSerialization::class)
class DeserializeClassTest : TestBase(KOTLIN) {
@Test
fun `test deserializing existing class`() {
val myClass = ExternalData::class.java
val data = myClass.serialize()
sandbox {
_contextSerializationEnv.set(createSandboxSerializationEnv(classLoader))
val sandboxInstant = data.deserializeFor(classLoader)
val taskFactory = classLoader.createRawTaskFactory()
val showClass = classLoader.createTaskFor(taskFactory, ShowClass::class.java)
val result = showClass.apply(sandboxInstant) ?: fail("Result cannot be null")
assertEquals("sandbox.${myClass.name}", result.toString())
assertEquals(SANDBOX_STRING, result::class.java.name)
}
}
@Test
fun `test deserializing missing class`() {
// The DJVM will refuse to find this class because it belongs to net.corda.djvm.**.
val myClass = Severity::class.java
val data = myClass.serialize()
sandbox {
_contextSerializationEnv.set(createSandboxSerializationEnv(classLoader))
val ex = assertThrows<NotSerializableException>{ data.deserializeFor(classLoader) }
assertThat(ex)
.isExactlyInstanceOf(NotSerializableException::class.java)
.hasMessageContaining("Severity was not found by the node,")
}
}
}
class ShowClass : Function<Class<*>, String> {
override fun apply(type: Class<*>): String {
return type.name
}
}

View File

@ -0,0 +1,215 @@
package net.corda.serialization.djvm
import greymalkin.ExternalEnum
import net.corda.core.serialization.CordaSerializable
import net.corda.core.serialization.internal._contextSerializationEnv
import net.corda.core.serialization.serialize
import net.corda.core.utilities.NonEmptySet
import net.corda.serialization.djvm.SandboxType.KOTLIN
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
import org.junit.jupiter.api.fail
import java.util.EnumSet
import java.util.NavigableSet
import java.util.SortedSet
import java.util.function.Function
@ExtendWith(LocalSerialization::class)
class DeserializeCollectionsTest : TestBase(KOTLIN) {
@Test
fun `test deserializing string list`() {
val stringList = StringList(listOf("Hello", "World", "!"))
val data = stringList.serialize()
sandbox {
_contextSerializationEnv.set(createSandboxSerializationEnv(classLoader))
val sandboxList = data.deserializeFor(classLoader)
val taskFactory = classLoader.createRawTaskFactory()
val showStringList = classLoader.createTaskFor(taskFactory, ShowStringList::class.java)
val result = showStringList.apply(sandboxList) ?: fail("Result cannot be null")
assertEquals(stringList.lines.joinToString(), result.toString())
assertEquals("Hello, World, !", result.toString())
}
}
class ShowStringList : Function<StringList, String> {
override fun apply(data: StringList): String {
return data.lines.joinToString()
}
}
@Test
fun `test deserializing integer set`() {
val integerSet = IntegerSet(linkedSetOf(10, 3, 15, 2, 10))
val data = integerSet.serialize()
sandbox {
_contextSerializationEnv.set(createSandboxSerializationEnv(classLoader))
val sandboxSet = data.deserializeFor(classLoader)
val taskFactory = classLoader.createRawTaskFactory()
val showIntegerSet = classLoader.createTaskFor(taskFactory, ShowIntegerSet::class.java)
val result = showIntegerSet.apply(sandboxSet) ?: fail("Result cannot be null")
assertEquals(integerSet.numbers.joinToString(), result.toString())
assertEquals("10, 3, 15, 2", result.toString())
}
}
class ShowIntegerSet : Function<IntegerSet, String> {
override fun apply(data: IntegerSet): String {
return data.numbers.joinToString()
}
}
@Test
fun `test deserializing integer sorted set`() {
val integerSortedSet = IntegerSortedSet(sortedSetOf(10, 15, 1000, 3, 2, 10))
val data = integerSortedSet.serialize()
sandbox {
_contextSerializationEnv.set(createSandboxSerializationEnv(classLoader))
val sandboxSet = data.deserializeFor(classLoader)
val taskFactory = classLoader.createRawTaskFactory()
val showIntegerSortedSet = classLoader.createTaskFor(taskFactory, ShowIntegerSortedSet::class.java)
val result = showIntegerSortedSet.apply(sandboxSet) ?: fail("Result cannot be null")
assertEquals(integerSortedSet.numbers.joinToString(), result.toString())
assertEquals("2, 3, 10, 15, 1000", result.toString())
}
}
class ShowIntegerSortedSet : Function<IntegerSortedSet, String> {
override fun apply(data: IntegerSortedSet): String {
return data.numbers.joinToString()
}
}
@Test
fun `test deserializing long navigable set`() {
val longNavigableSet = LongNavigableSet(sortedSetOf(99955L, 10, 15, 1000, 3, 2, 10))
val data = longNavigableSet.serialize()
sandbox {
_contextSerializationEnv.set(createSandboxSerializationEnv(classLoader))
val sandboxSet = data.deserializeFor(classLoader)
val taskFactory = classLoader.createRawTaskFactory()
val showLongNavigableSet = classLoader.createTaskFor(taskFactory, ShowLongNavigableSet::class.java)
val result = showLongNavigableSet.apply(sandboxSet) ?: fail("Result cannot be null")
assertEquals(longNavigableSet.numbers.joinToString(), result.toString())
assertEquals("2, 3, 10, 15, 1000, 99955", result.toString())
}
}
class ShowLongNavigableSet : Function<LongNavigableSet, String> {
override fun apply(data: LongNavigableSet): String {
return data.numbers.joinToString()
}
}
@Test
fun `test deserializing short collection`() {
val shortCollection = ShortCollection(listOf(10, 200, 3000))
val data = shortCollection.serialize()
sandbox {
_contextSerializationEnv.set(createSandboxSerializationEnv(classLoader))
val sandboxCollection = data.deserializeFor(classLoader)
val taskFactory = classLoader.createRawTaskFactory()
val showShortCollection = classLoader.createTaskFor(taskFactory, ShowShortCollection::class.java)
val result = showShortCollection.apply(sandboxCollection) ?: fail("Result cannot be null")
assertEquals(shortCollection.numbers.joinToString(), result.toString())
assertEquals("10, 200, 3000", result.toString())
}
}
class ShowShortCollection : Function<ShortCollection, String> {
override fun apply(data: ShortCollection): String {
return data.numbers.joinToString()
}
}
@Test
fun `test deserializing non-empty string set`() {
val nonEmptyStrings = NonEmptyStringSet(NonEmptySet.of("Hello", "World", "!"))
val data = nonEmptyStrings.serialize()
sandbox {
_contextSerializationEnv.set(createSandboxSerializationEnv(classLoader))
val sandboxSet = data.deserializeFor(classLoader)
val taskFactory = classLoader.createRawTaskFactory()
val showNonEmptyStringSet = classLoader.createTaskFor(taskFactory, ShowNonEmptyStringSet::class.java)
val result = showNonEmptyStringSet.apply(sandboxSet) ?: fail("Result cannot be null")
assertEquals(nonEmptyStrings.lines.joinToString(), result.toString())
assertEquals("Hello, World, !", result.toString())
}
}
class ShowNonEmptyStringSet : Function<NonEmptyStringSet, String> {
override fun apply(data: NonEmptyStringSet): String {
return data.lines.joinToString()
}
}
@Test
fun `test deserializing enum set`() {
val enumSet = HasEnumSet(EnumSet.of(ExternalEnum.DOH))
val data = enumSet.serialize()
sandbox {
_contextSerializationEnv.set(createSandboxSerializationEnv(classLoader))
val sandboxSet = data.deserializeFor(classLoader)
val taskFactory = classLoader.createRawTaskFactory()
val showHasEnumSet = classLoader.createTaskFor(taskFactory, ShowHasEnumSet::class.java)
val result = showHasEnumSet.apply(sandboxSet) ?: fail("Result cannot be null")
assertEquals(enumSet.values.toString(), result.toString())
assertEquals("[DOH]", result.toString())
}
}
class ShowHasEnumSet : Function<HasEnumSet, String> {
override fun apply(data: HasEnumSet): String {
return data.values.toString()
}
}
}
@CordaSerializable
class StringList(val lines: List<String>)
@CordaSerializable
class IntegerSet(val numbers: Set<Int>)
@CordaSerializable
class IntegerSortedSet(val numbers: SortedSet<Int>)
@CordaSerializable
class LongNavigableSet(val numbers: NavigableSet<Long>)
@CordaSerializable
class ShortCollection(val numbers: Collection<Short>)
@CordaSerializable
class NonEmptyStringSet(val lines: NonEmptySet<String>)
@CordaSerializable
class HasEnumSet(val values: EnumSet<ExternalEnum>)

View File

@ -0,0 +1,45 @@
package net.corda.serialization.djvm
import net.corda.core.serialization.CordaSerializable
import net.corda.core.serialization.internal._contextSerializationEnv
import net.corda.core.serialization.serialize
import net.corda.serialization.djvm.SandboxType.KOTLIN
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
import org.junit.jupiter.api.fail
import java.util.Currency
import java.util.function.Function
@ExtendWith(LocalSerialization::class)
class DeserializeCurrencyTest : TestBase(KOTLIN) {
@Test
fun `test deserializing currency`() {
val currency = CurrencyData(Currency.getInstance("GBP"))
val data = currency.serialize()
sandbox {
_contextSerializationEnv.set(createSandboxSerializationEnv(classLoader))
val sandboxCurrency = data.deserializeFor(classLoader)
val taskFactory = classLoader.createRawTaskFactory()
val showCurrency = classLoader.createTaskFor(taskFactory, ShowCurrency::class.java)
val result = showCurrency.apply(sandboxCurrency) ?: fail("Result cannot be null")
assertEquals(ShowCurrency().apply(currency), result.toString())
assertEquals(SANDBOX_STRING, result::class.java.name)
}
}
class ShowCurrency : Function<CurrencyData, String> {
override fun apply(data: CurrencyData): String {
return with(data) {
"Currency: $currency"
}
}
}
}
@CordaSerializable
data class CurrencyData(val currency: Currency)

View File

@ -0,0 +1,43 @@
package net.corda.serialization.djvm
import greymalkin.ExternalEnum
import net.corda.core.serialization.internal._contextSerializationEnv
import net.corda.core.serialization.serialize
import net.corda.serialization.djvm.SandboxType.KOTLIN
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.extension.ExtendWith
import org.junit.jupiter.api.fail
import org.junit.jupiter.params.ParameterizedTest
import org.junit.jupiter.params.provider.EnumSource
import java.util.EnumSet
import java.util.function.Function
@ExtendWith(LocalSerialization::class)
class DeserializeEnumSetTest : TestBase(KOTLIN) {
@ParameterizedTest
@EnumSource(ExternalEnum::class)
fun `test deserialize enum set`(value: ExternalEnum) {
val enumSet = EnumSet.of(value)
val data = enumSet.serialize()
sandbox {
_contextSerializationEnv.set(createSandboxSerializationEnv(classLoader))
val sandboxEnumSet = data.deserializeFor(classLoader)
val taskFactory = classLoader.createRawTaskFactory()
val showEnumSet = classLoader.createTaskFor(taskFactory, ShowEnumSet::class.java)
val result = showEnumSet.apply(sandboxEnumSet) ?: fail("Result cannot be null")
assertEquals(ShowEnumSet().apply(enumSet), result.toString())
assertEquals("EnumSet: [$value]'", result.toString())
assertEquals(SANDBOX_STRING, result::class.java.name)
}
}
class ShowEnumSet : Function<EnumSet<*>, String> {
override fun apply(input: EnumSet<*>): String {
return "EnumSet: $input'"
}
}
}

View File

@ -0,0 +1,55 @@
package net.corda.serialization.djvm
import net.corda.core.serialization.CordaSerializable
import net.corda.core.serialization.internal._contextSerializationEnv
import net.corda.core.serialization.serialize
import net.corda.serialization.djvm.SandboxType.KOTLIN
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.extension.ExtendWith
import org.junit.jupiter.api.fail
import org.junit.jupiter.params.ParameterizedTest
import org.junit.jupiter.params.provider.EnumSource
import java.util.function.Function
@ExtendWith(LocalSerialization::class)
class DeserializeEnumTest : TestBase(KOTLIN) {
@ParameterizedTest
@EnumSource(ExampleEnum::class)
fun `test deserialize basic enum`(value: ExampleEnum) {
val example = ExampleData(value)
val data = example.serialize()
sandbox {
_contextSerializationEnv.set(createSandboxSerializationEnv(classLoader))
val sandboxExample = data.deserializeFor(classLoader)
val taskFactory = classLoader.createRawTaskFactory()
val showExampleData = classLoader.createTaskFor(taskFactory, ShowExampleData::class.java)
val result = showExampleData.apply(sandboxExample) ?: fail("Result cannot be null")
assertEquals(ShowExampleData().apply(example), result.toString())
assertEquals("Example: name='${value.name}', ordinal='${value.ordinal}'", result.toString())
assertEquals(SANDBOX_STRING, result::class.java.name)
}
}
class ShowExampleData : Function<ExampleData, String> {
override fun apply(input: ExampleData): String {
return with(input) {
"Example: name='${value.name}', ordinal='${value.ordinal}'"
}
}
}
}
@Suppress("unused")
@CordaSerializable
enum class ExampleEnum {
ONE,
TWO,
THREE
}
@CordaSerializable
data class ExampleData(val value: ExampleEnum)

View File

@ -0,0 +1,170 @@
package net.corda.serialization.djvm
import net.corda.core.serialization.CordaSerializable
import net.corda.core.serialization.internal._contextSerializationEnv
import net.corda.core.serialization.serialize
import net.corda.serialization.djvm.SandboxType.KOTLIN
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
import org.junit.jupiter.api.fail
import java.util.function.Function
@ExtendWith(LocalSerialization::class)
class DeserializeGenericsTest : TestBase(KOTLIN) {
@Test
fun `test deserializing generic wrapper with String`() {
val wrappedString = GenericWrapper(data = "Hello World!")
val data = wrappedString.serialize()
sandbox {
_contextSerializationEnv.set(createSandboxSerializationEnv(classLoader))
val sandboxWrapper = data.deserializeFor(classLoader)
val taskFactory = classLoader.createRawTaskFactory()
val getGenericData = classLoader.createTaskFor(taskFactory, GetGenericData::class.java)
val result = getGenericData.apply(sandboxWrapper) ?: fail("Result cannot be null")
assertEquals("Hello World!", result.toString())
assertEquals(SANDBOX_STRING, result::class.java.name)
}
}
@Test
fun `test deserializing generic wrapper with Integer`() {
val wrappedInteger = GenericWrapper(data = 1000)
val data = wrappedInteger.serialize()
sandbox {
_contextSerializationEnv.set(createSandboxSerializationEnv(classLoader))
val sandboxWrapper = data.deserializeFor(classLoader)
val taskFactory = classLoader.createRawTaskFactory()
val getGenericData = classLoader.createTaskFor(taskFactory, GetGenericData::class.java)
val result = getGenericData.apply(sandboxWrapper) ?: fail("Result cannot be null")
assertEquals("sandbox.java.lang.Integer", result::class.java.name)
assertEquals(1000, classLoader.createBasicOutput().apply(result))
}
}
@Test
fun `test deserializing generic wrapper with array of Integer`() {
val wrappedArrayOfInteger = GenericWrapper(arrayOf(1000, 2000, 3000))
val data = wrappedArrayOfInteger.serialize()
sandbox {
_contextSerializationEnv.set(createSandboxSerializationEnv(classLoader))
val sandboxWrapper = data.deserializeFor(classLoader)
val taskFactory = classLoader.createRawTaskFactory()
val getGenericData = classLoader.createTaskFor(taskFactory, GetGenericData::class.java)
val result = getGenericData.apply(sandboxWrapper) ?: fail("Result cannot be null")
assertEquals("[Lsandbox.java.lang.Integer;", result::class.java.name)
assertThat(classLoader.createBasicOutput().apply(result))
.isEqualTo(arrayOf(1000, 2000, 3000))
}
}
@Test
fun `test deserializing generic wrapper with primitive int array`() {
val wrappedArrayOfInteger = GenericWrapper(intArrayOf(1000, 2000, 3000))
val data = wrappedArrayOfInteger.serialize()
sandbox {
_contextSerializationEnv.set(createSandboxSerializationEnv(classLoader))
val sandboxWrapper = data.deserializeFor(classLoader)
val taskFactory = classLoader.createRawTaskFactory()
val getGenericData = classLoader.createTaskFor(taskFactory, GetGenericData::class.java)
val result = getGenericData.apply(sandboxWrapper) ?: fail("Result cannot be null")
assertEquals("[I", result::class.java.name)
assertThat(classLoader.createBasicOutput().apply(result))
.isEqualTo(intArrayOf(1000, 2000, 3000))
}
}
@Test
fun `test deserializing generic list`() {
val wrappedList = GenericWrapper(data = listOf("Hello World!"))
val data = wrappedList.serialize()
sandbox {
_contextSerializationEnv.set(createSandboxSerializationEnv(classLoader))
val sandboxWrapper = data.deserializeFor(classLoader)
val taskFactory = classLoader.createRawTaskFactory()
val getGenericData = classLoader.createTaskFor(taskFactory, GetGenericData::class.java)
val dataResult = getGenericData.apply(sandboxWrapper) ?: fail("Result cannot be null")
assertEquals("[Hello World!]", dataResult.toString())
assertEquals("sandbox.java.util.Collections\$UnmodifiableRandomAccessList", dataResult::class.java.name)
val getGenericIterableData = classLoader.createTaskFor(taskFactory, GetGenericIterableData::class.java)
val dataItemResult = getGenericIterableData.apply(sandboxWrapper) ?: fail("Result cannot be null")
assertEquals(SANDBOX_STRING, dataItemResult::class.java.name)
}
}
class GetGenericData : Function<GenericWrapper<out Any>, Any?> {
override fun apply(input: GenericWrapper<out Any>): Any? {
return input.data
}
}
class GetGenericIterableData : Function<GenericWrapper<out Iterable<*>>, Any?> {
override fun apply(input: GenericWrapper<out Iterable<*>>): Any? {
return input.data.iterator().let {
if (it.hasNext()) {
it.next()
} else {
null
}
}
}
}
@Test
fun `test deserializing concrete wrapper`() {
val wrapped = ConcreteWrapper(
first = GenericWrapper("Hello World"),
second = GenericWrapper('!')
)
val data = wrapped.serialize()
sandbox {
_contextSerializationEnv.set(createSandboxSerializationEnv(classLoader))
val sandboxWrapped = data.deserializeFor(classLoader)
val taskFactory = classLoader.createRawTaskFactory()
val showConcreteWrapper = classLoader.createTaskFor(taskFactory, ShowConcreteWrapper::class.java)
val result = showConcreteWrapper.apply(sandboxWrapped) ?: fail("Result cannot be null")
assertEquals("Concrete: first='Hello World', second='!'", result.toString())
}
}
class ShowConcreteWrapper : Function<ConcreteWrapper, String> {
override fun apply(input: ConcreteWrapper): String {
return "Concrete: first='${input.first.data}', second='${input.second.data}'"
}
}
}
@CordaSerializable
data class GenericWrapper<T>(val data: T)
@CordaSerializable
data class ConcreteWrapper(
val first: GenericWrapper<String>,
val second: GenericWrapper<Char>
)

Some files were not shown because too many files have changed in this diff Show More