Remove links to Kryo from serialization "clients" (#1079)

This commit is contained in:
Rick Parker 2017-07-21 14:23:05 +01:00 committed by GitHub
parent e1551fc74e
commit fe9db6f1f7
92 changed files with 1454 additions and 722 deletions

View File

@ -6,6 +6,8 @@ apply plugin: 'com.jfrog.artifactory'
dependencies {
compile project(':core')
compile project(':finance')
testCompile project(':test-utils')
compile "org.jetbrains.kotlin:kotlin-stdlib-jre8:$kotlin_version"
testCompile "org.jetbrains.kotlin:kotlin-test:$kotlin_version"

View File

@ -7,6 +7,7 @@ import com.pholser.junit.quickcheck.runner.JUnitQuickcheck
import net.corda.core.contracts.Amount
import net.corda.core.contracts.USD
import net.corda.core.testing.PublicKeyGenerator
import net.corda.testing.TestDependencyInjectionBase
import net.i2p.crypto.eddsa.EdDSAPublicKey
import org.junit.Test
import org.junit.runner.RunWith
@ -15,7 +16,7 @@ import java.util.*
import kotlin.test.assertEquals
@RunWith(JUnitQuickcheck::class)
class JacksonSupportTest {
class JacksonSupportTest : TestDependencyInjectionBase() {
companion object {
val mapper = JacksonSupport.createNonRpcMapper()
}

View File

@ -51,7 +51,7 @@ class NodeMonitorModelTest : DriverBasedTest() {
lateinit var networkMapUpdates: Observable<NetworkMapCache.MapChange>
lateinit var newNode: (X500Name) -> NodeInfo
override fun setup() = driver {
override fun setup() = driver(initialiseSerialization = false) {
val cashUser = User("user1", "test", permissions = setOf(
startFlowPermission<CashIssueFlow>(),
startFlowPermission<CashPaymentFlow>(),
@ -72,14 +72,14 @@ class NodeMonitorModelTest : DriverBasedTest() {
vaultUpdates = monitor.vaultUpdates.bufferUntilSubscribed()
networkMapUpdates = monitor.networkMap.bufferUntilSubscribed()
monitor.register(aliceNodeHandle.configuration.rpcAddress!!, cashUser.username, cashUser.password)
monitor.register(aliceNodeHandle.configuration.rpcAddress!!, cashUser.username, cashUser.password, initialiseSerialization = false)
rpc = monitor.proxyObservable.value!!
val bobNodeHandle = startNode(BOB.name, rpcUsers = listOf(cashUser)).getOrThrow()
bobNode = bobNodeHandle.nodeInfo
val monitorBob = NodeMonitorModel()
stateMachineUpdatesBob = monitorBob.stateMachineUpdates.bufferUntilSubscribed()
monitorBob.register(bobNodeHandle.configuration.rpcAddress!!, cashUser.username, cashUser.password)
monitorBob.register(bobNodeHandle.configuration.rpcAddress!!, cashUser.username, cashUser.password, initialiseSerialization = false)
rpcBob = monitorBob.proxyObservable.value!!
runTest()
}

View File

@ -51,12 +51,13 @@ class NodeMonitorModel {
* Register for updates to/from a given vault.
* TODO provide an unsubscribe mechanism
*/
fun register(nodeHostAndPort: NetworkHostAndPort, username: String, password: String) {
fun register(nodeHostAndPort: NetworkHostAndPort, username: String, password: String, initialiseSerialization: Boolean = true) {
val client = CordaRPCClient(
hostAndPort = nodeHostAndPort,
configuration = CordaRPCClientConfiguration.default.copy(
connectionMaxRetryInterval = 10.seconds
)
),
initialiseSerialization = initialiseSerialization
)
val connection = client.start(username, password)
val proxy = connection.proxy

View File

@ -1,30 +1,38 @@
package net.corda.client.rpc;
import com.google.common.util.concurrent.*;
import net.corda.client.rpc.internal.*;
import net.corda.contracts.asset.*;
import net.corda.core.contracts.*;
import net.corda.core.messaging.*;
import net.corda.core.node.services.*;
import net.corda.core.node.services.vault.*;
import net.corda.core.utilities.*;
import net.corda.flows.*;
import net.corda.node.internal.*;
import net.corda.node.services.transactions.*;
import net.corda.nodeapi.*;
import net.corda.schemas.*;
import net.corda.testing.node.*;
import org.junit.*;
import com.google.common.util.concurrent.ListenableFuture;
import net.corda.client.rpc.internal.RPCClient;
import net.corda.contracts.asset.Cash;
import net.corda.core.contracts.Amount;
import net.corda.core.messaging.CordaRPCOps;
import net.corda.core.messaging.FlowHandle;
import net.corda.core.node.services.ServiceInfo;
import net.corda.core.node.services.Vault;
import net.corda.core.node.services.vault.Builder;
import net.corda.core.node.services.vault.QueryCriteria;
import net.corda.core.utilities.OpaqueBytes;
import net.corda.flows.AbstractCashFlow;
import net.corda.flows.CashIssueFlow;
import net.corda.flows.CashPaymentFlow;
import net.corda.node.internal.Node;
import net.corda.node.services.transactions.ValidatingNotaryService;
import net.corda.nodeapi.User;
import net.corda.schemas.CashSchemaV1;
import net.corda.testing.node.NodeBasedTest;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import java.io.*;
import java.lang.reflect.*;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.ExecutionException;
import static kotlin.test.AssertionsKt.*;
import static net.corda.client.rpc.CordaRPCClientConfiguration.*;
import static net.corda.node.services.RPCUserServiceKt.*;
import static net.corda.testing.TestConstants.*;
import static kotlin.test.AssertionsKt.assertEquals;
import static net.corda.client.rpc.CordaRPCClientConfiguration.getDefault;
import static net.corda.node.services.RPCUserServiceKt.startFlowPermission;
import static net.corda.testing.TestConstants.getALICE;
public class CordaRPCJavaClientTest extends NodeBasedTest {
private List<String> perms = Arrays.asList(startFlowPermission(CashPaymentFlow.class), startFlowPermission(CashIssueFlow.class));
@ -46,7 +54,7 @@ public class CordaRPCJavaClientTest extends NodeBasedTest {
Set<ServiceInfo> services = new HashSet<>(Collections.singletonList(new ServiceInfo(ValidatingNotaryService.Companion.getType(), null)));
ListenableFuture<Node> nodeFuture = startNode(getALICE().getName(), 1, services, Arrays.asList(rpcUser), Collections.emptyMap());
node = nodeFuture.get();
client = new CordaRPCClient(node.getConfiguration().getRpcAddress(), null, getDefault());
client = new CordaRPCClient(node.getConfiguration().getRpcAddress(), null, getDefault(), false);
}
@After

View File

@ -49,7 +49,7 @@ class CordaRPCClientTest : NodeBasedTest() {
@Before
fun setUp() {
node = startNode(ALICE.name, rpcUsers = listOf(rpcUser), advertisedServices = setOf(ServiceInfo(ValidatingNotaryService.type))).getOrThrow()
client = CordaRPCClient(node.configuration.rpcAddress!!)
client = CordaRPCClient(node.configuration.rpcAddress!!, initialiseSerialization = false)
}
@After

View File

@ -1,10 +1,5 @@
package net.corda.client.rpc
import com.esotericsoftware.kryo.Kryo
import com.esotericsoftware.kryo.Serializer
import com.esotericsoftware.kryo.io.Input
import com.esotericsoftware.kryo.io.Output
import com.esotericsoftware.kryo.pool.KryoPool
import com.google.common.util.concurrent.Futures
import net.corda.client.rpc.internal.RPCClient
import net.corda.client.rpc.internal.RPCClientConfiguration
@ -14,11 +9,11 @@ import net.corda.core.getOrThrow
import net.corda.core.messaging.RPCOps
import net.corda.core.utilities.millis
import net.corda.core.utilities.seconds
import net.corda.core.serialization.SerializationDefaults
import net.corda.core.utilities.NetworkHostAndPort
import net.corda.core.utilities.Try
import net.corda.node.services.messaging.RPCServerConfiguration
import net.corda.nodeapi.RPCApi
import net.corda.nodeapi.RPCKryo
import net.corda.testing.*
import net.corda.testing.driver.poll
import org.apache.activemq.artemis.api.core.SimpleString
@ -305,16 +300,8 @@ class RPCStabilityTests {
return Observable.interval(interval.toMillis(), TimeUnit.MILLISECONDS).map { chunk }
}
}
val dummyObservableSerialiser = object : Serializer<Observable<Any>>() {
override fun write(kryo: Kryo?, output: Output?, `object`: Observable<Any>?) {
}
override fun read(kryo: Kryo?, input: Input?, type: Class<Observable<Any>>?): Observable<Any> {
return Observable.empty()
}
}
@Test
fun `slow consumers are kicked`() {
val kryoPool = KryoPool.Builder { RPCKryo(dummyObservableSerialiser) }.build()
rpcDriver {
val server = startRpcServer(maxBufferedBytesPerClient = 10 * 1024 * 1024, ops = SlowConsumerRPCOpsImpl()).get()
@ -339,7 +326,7 @@ class RPCStabilityTests {
methodName = SlowConsumerRPCOps::streamAtInterval.name,
arguments = listOf(10.millis, 123456)
)
request.writeToClientMessage(kryoPool, message)
request.writeToClientMessage(SerializationDefaults.RPC_SERVER_CONTEXT, message)
producer.send(message)
session.commit()

View File

@ -2,11 +2,16 @@ package net.corda.client.rpc
import net.corda.client.rpc.internal.RPCClient
import net.corda.client.rpc.internal.RPCClientConfiguration
import net.corda.client.rpc.serialization.KryoClientSerializationScheme
import net.corda.core.messaging.CordaRPCOps
import net.corda.core.serialization.SerializationDefaults
import net.corda.core.utilities.NetworkHostAndPort
import net.corda.nodeapi.ArtemisTcpTransport.Companion.tcpTransport
import net.corda.nodeapi.ConnectionDirection
import net.corda.nodeapi.config.SSLConfiguration
import net.corda.nodeapi.serialization.KRYO_P2P_CONTEXT
import net.corda.nodeapi.serialization.KRYO_RPC_CLIENT_CONTEXT
import net.corda.nodeapi.serialization.SerializationFactoryImpl
import java.time.Duration
/** @see RPCClient.RPCConnection */
@ -35,11 +40,22 @@ data class CordaRPCClientConfiguration(
class CordaRPCClient(
hostAndPort: NetworkHostAndPort,
sslConfiguration: SSLConfiguration? = null,
configuration: CordaRPCClientConfiguration = CordaRPCClientConfiguration.default
configuration: CordaRPCClientConfiguration = CordaRPCClientConfiguration.default,
initialiseSerialization: Boolean = true
) {
init {
// Init serialization. It's plausible there are multiple clients in a single JVM, so be tolerant of
// others having registered first.
// TODO: allow clients to have serialization factory etc injected and align with RPC protocol version?
if (initialiseSerialization) {
initialiseSerialization()
}
}
private val rpcClient = RPCClient<CordaRPCOps>(
tcpTransport(ConnectionDirection.Outbound(), hostAndPort, sslConfiguration),
configuration.toRpcClientConfiguration()
configuration.toRpcClientConfiguration(),
KRYO_RPC_CLIENT_CONTEXT
)
fun start(username: String, password: String): CordaRPCConnection {
@ -49,4 +65,20 @@ class CordaRPCClient(
inline fun <A> use(username: String, password: String, block: (CordaRPCConnection) -> A): A {
return start(username, password).use(block)
}
companion object {
fun initialiseSerialization() {
try {
SerializationDefaults.SERIALIZATION_FACTORY = SerializationFactoryImpl().apply {
registerScheme(KryoClientSerializationScheme())
}
SerializationDefaults.P2P_CONTEXT = KRYO_P2P_CONTEXT
SerializationDefaults.RPC_CLIENT_CONTEXT = KRYO_RPC_CLIENT_CONTEXT
} catch(e: IllegalStateException) {
// Check that it's registered as we expect
check(SerializationDefaults.SERIALIZATION_FACTORY is SerializationFactoryImpl) { "RPC client encountered conflicting configuration of serialization subsystem." }
check((SerializationDefaults.SERIALIZATION_FACTORY as SerializationFactoryImpl).alreadyRegisteredSchemes.any { it is KryoClientSerializationScheme }) { "RPC client encountered conflicting configuration of serialization subsystem." }
}
}
}
}

View File

@ -1,9 +1,11 @@
package net.corda.client.rpc.internal
import net.corda.core.crypto.random63BitValue
import net.corda.core.logElapsedTime
import net.corda.core.messaging.RPCOps
import net.corda.core.serialization.SerializationContext
import net.corda.core.serialization.SerializationDefaults
import net.corda.core.utilities.minutes
import net.corda.core.crypto.random63BitValue
import net.corda.core.utilities.seconds
import net.corda.core.utilities.NetworkHostAndPort
import net.corda.core.utilities.loggerFor
@ -85,13 +87,15 @@ data class RPCClientConfiguration(
*/
class RPCClient<I : RPCOps>(
val transport: TransportConfiguration,
val rpcConfiguration: RPCClientConfiguration = RPCClientConfiguration.default
val rpcConfiguration: RPCClientConfiguration = RPCClientConfiguration.default,
val serializationContext: SerializationContext = SerializationDefaults.RPC_CLIENT_CONTEXT
) {
constructor(
hostAndPort: NetworkHostAndPort,
sslConfiguration: SSLConfiguration? = null,
configuration: RPCClientConfiguration = RPCClientConfiguration.default
) : this(tcpTransport(ConnectionDirection.Outbound(), hostAndPort, sslConfiguration), configuration)
configuration: RPCClientConfiguration = RPCClientConfiguration.default,
serializationContext: SerializationContext = SerializationDefaults.RPC_CLIENT_CONTEXT
) : this(tcpTransport(ConnectionDirection.Outbound(), hostAndPort, sslConfiguration), configuration, serializationContext)
companion object {
private val log = loggerFor<RPCClient<*>>()
@ -146,7 +150,7 @@ class RPCClient<I : RPCOps>(
minLargeMessageSize = rpcConfiguration.maxFileSize
}
val proxyHandler = RPCClientProxyHandler(rpcConfiguration, username, password, serverLocator, clientAddress, rpcOpsClass)
val proxyHandler = RPCClientProxyHandler(rpcConfiguration, username, password, serverLocator, clientAddress, rpcOpsClass, serializationContext)
try {
proxyHandler.start()

View File

@ -4,7 +4,6 @@ import com.esotericsoftware.kryo.Kryo
import com.esotericsoftware.kryo.Serializer
import com.esotericsoftware.kryo.io.Input
import com.esotericsoftware.kryo.io.Output
import com.esotericsoftware.kryo.pool.KryoPool
import com.google.common.cache.Cache
import com.google.common.cache.CacheBuilder
import com.google.common.cache.RemovalCause
@ -18,7 +17,7 @@ import net.corda.core.internal.LazyPool
import net.corda.core.internal.LazyStickyPool
import net.corda.core.internal.LifeCycle
import net.corda.core.messaging.RPCOps
import net.corda.core.serialization.KryoPoolWithContext
import net.corda.core.serialization.SerializationContext
import net.corda.core.utilities.*
import net.corda.nodeapi.*
import org.apache.activemq.artemis.api.core.SimpleString
@ -64,7 +63,8 @@ class RPCClientProxyHandler(
private val rpcPassword: String,
private val serverLocator: ServerLocator,
private val clientAddress: SimpleString,
private val rpcOpsClass: Class<out RPCOps>
private val rpcOpsClass: Class<out RPCOps>,
serializationContext: SerializationContext
) : InvocationHandler {
private enum class State {
@ -77,9 +77,6 @@ class RPCClientProxyHandler(
private companion object {
val log = loggerFor<RPCClientProxyHandler>()
// Note that this KryoPool is not yet capable of deserialising Observables, it requires Proxy-specific context
// to do that. However it may still be used for serialisation of RPC requests and related messages.
val kryoPool: KryoPool = KryoPool.Builder { RPCKryo(RpcClientObservableSerializer) }.build()
// To check whether toString() is being invoked
val toStringMethod: Method = Object::toString.javaMethod!!
}
@ -112,8 +109,7 @@ class RPCClientProxyHandler(
private val observablesToReap = ThreadBox(object {
var observables = ArrayList<RPCApi.ObservableId>()
})
// A Kryo pool that automatically adds the observable context when an instance is requested.
private val kryoPoolWithObservableContext = RpcClientObservableSerializer.createPoolWithContext(kryoPool, observableContext)
private val serializationContextWithObservableContext = RpcClientObservableSerializer.createContext(serializationContext, observableContext)
private fun createRpcObservableMap(): RpcObservableMap {
val onObservableRemove = RemovalListener<RPCApi.ObservableId, UnicastSubject<Notification<Any>>> {
@ -197,7 +193,7 @@ class RPCClientProxyHandler(
val replyFuture = SettableFuture.create<Any>()
sessionAndProducerPool.run {
val message = it.session.createMessage(false)
request.writeToClientMessage(kryoPool, message)
request.writeToClientMessage(serializationContextWithObservableContext, message)
log.debug {
val argumentsString = arguments?.joinToString() ?: ""
@ -224,7 +220,7 @@ class RPCClientProxyHandler(
// The handler for Artemis messages.
private fun artemisMessageHandler(message: ClientMessage) {
val serverToClient = RPCApi.ServerToClient.fromClientMessage(kryoPoolWithObservableContext, message)
val serverToClient = RPCApi.ServerToClient.fromClientMessage(serializationContextWithObservableContext, message)
log.debug { "Got message from RPC server $serverToClient" }
when (serverToClient) {
is RPCApi.ServerToClient.RpcReply -> {
@ -351,7 +347,7 @@ private typealias CallSiteMap = ConcurrentHashMap<Long, Throwable?>
* @param observableMap holds the Observables that are ultimately exposed to the user.
* @param hardReferenceStore holds references to Observables we want to keep alive while they are subscribed to.
*/
private data class ObservableContext(
data class ObservableContext(
val callSiteMap: CallSiteMap?,
val observableMap: RpcObservableMap,
val hardReferenceStore: MutableSet<Observable<*>>
@ -360,10 +356,11 @@ private data class ObservableContext(
/**
* A [Serializer] to deserialise Observables once the corresponding Kryo instance has been provided with an [ObservableContext].
*/
private object RpcClientObservableSerializer : Serializer<Observable<Any>>() {
object RpcClientObservableSerializer : Serializer<Observable<Any>>() {
private object RpcObservableContextKey
fun createPoolWithContext(kryoPool: KryoPool, observableContext: ObservableContext): KryoPool {
return KryoPoolWithContext(kryoPool, RpcObservableContextKey, observableContext)
fun createContext(serializationContext: SerializationContext, observableContext: ObservableContext): SerializationContext {
return serializationContext.withProperty(RpcObservableContextKey, observableContext)
}
override fun read(kryo: Kryo, input: Input, type: Class<Observable<Any>>): Observable<Any> {

View File

@ -0,0 +1,27 @@
package net.corda.client.rpc.serialization
import com.esotericsoftware.kryo.pool.KryoPool
import net.corda.client.rpc.internal.RpcClientObservableSerializer
import net.corda.core.serialization.DefaultKryoCustomizer
import net.corda.core.serialization.SerializationContext
import net.corda.core.utilities.ByteSequence
import net.corda.nodeapi.RPCKryo
import net.corda.nodeapi.serialization.AbstractKryoSerializationScheme
import net.corda.nodeapi.serialization.KryoHeaderV0_1
class KryoClientSerializationScheme : AbstractKryoSerializationScheme() {
override fun canDeserializeVersion(byteSequence: ByteSequence, target: SerializationContext.UseCase): Boolean {
return byteSequence == KryoHeaderV0_1 && (target == SerializationContext.UseCase.RPCClient || target == SerializationContext.UseCase.P2P)
}
override fun rpcClientKryoPool(context: SerializationContext): KryoPool {
return KryoPool.Builder {
DefaultKryoCustomizer.customize(RPCKryo(RpcClientObservableSerializer, context.whitelist)).apply { classLoader = context.deserializationClassLoader }
}.build()
}
// We're on the client and don't have access to server classes.
override fun rpcServerKryoPool(context: SerializationContext): KryoPool {
throw UnsupportedOperationException()
}
}

View File

@ -27,7 +27,9 @@ import kotlin.test.assertTrue
class ClientRPCInfrastructureTests : AbstractRPCTest() {
// TODO: Test that timeouts work
private fun RPCDriverExposedDSLInterface.testProxy() = testProxy<TestOps>(TestOpsImpl()).ops
private fun RPCDriverExposedDSLInterface.testProxy(): TestOps {
return testProxy<TestOps>(TestOpsImpl()).ops
}
interface TestOps : RPCOps {
@Throws(IllegalArgumentException::class)

View File

@ -1,8 +1,8 @@
package net.corda.client.rpc
import net.corda.core.messaging.RPCOps
import net.corda.node.services.messaging.requirePermission
import net.corda.node.services.messaging.getRpcContext
import net.corda.node.services.messaging.requirePermission
import net.corda.nodeapi.PermissionException
import net.corda.nodeapi.User
import net.corda.testing.RPCDriverExposedDSLInterface

View File

@ -1,8 +1,8 @@
package net.corda.core.crypto
import net.corda.core.serialization.CordaSerializable
import net.corda.core.utilities.opaque
import net.corda.core.serialization.serialize
import net.corda.core.utilities.sequence
import java.security.PublicKey
import java.time.Instant
import java.util.*
@ -51,7 +51,7 @@ open class MetaData(
if (timestamp != other.timestamp) return false
if (visibleInputs != other.visibleInputs) return false
if (signedInputs != other.signedInputs) return false
if (merkleRoot.opaque() != other.merkleRoot.opaque()) return false
if (merkleRoot.sequence() != other.merkleRoot.sequence()) return false
if (publicKey != other.publicKey) return false
return true
}

View File

@ -23,7 +23,8 @@ open class SignedData<T : Any>(val raw: SerializedBytes<T>, val sig: DigitalSign
@Throws(SignatureException::class)
fun verified(): T {
sig.by.verify(raw.bytes, sig)
val data = raw.deserialize()
@Suppress("UNCHECKED_CAST")
val data = raw.deserialize<Any>() as T
verifyData(data)
return data
}

View File

@ -4,13 +4,12 @@ import net.corda.core.crypto.Crypto
import net.corda.core.crypto.composite.CompositeKey.NodeAndWeight
import net.corda.core.crypto.keys
import net.corda.core.crypto.provider.CordaObjectIdentifier
import net.corda.core.crypto.toSHA256Bytes
import net.corda.core.crypto.toStringShort
import net.corda.core.serialization.CordaSerializable
import net.corda.core.utilities.sequence
import org.bouncycastle.asn1.*
import org.bouncycastle.asn1.x509.AlgorithmIdentifier
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo
import java.nio.ByteBuffer
import java.security.PublicKey
import java.util.*
@ -145,7 +144,7 @@ class CompositeKey private constructor(val threshold: Int, children: List<NodeAn
override fun compareTo(other: NodeAndWeight): Int {
return if (weight == other.weight)
ByteBuffer.wrap(node.toSHA256Bytes()).compareTo(ByteBuffer.wrap(other.node.toSHA256Bytes()))
node.encoded.sequence().compareTo(other.node.encoded.sequence())
else
weight.compareTo(other.weight)
}

View File

@ -0,0 +1,18 @@
package net.corda.core.internal
import kotlin.reflect.KProperty
/**
* A write-once property to be used as delegate for Kotlin var properties. The expectation is that this is initialised
* prior to the spawning of any threads that may access it and so there's no need for it to be volatile.
*/
class WriteOnceProperty<T : Any>() {
private var v: T? = null
operator fun getValue(thisRef: Any?, property: KProperty<*>) = v ?: throw IllegalStateException("Write-once property $property not set.")
operator fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
check(v == null) { "Cannot set write-once property $property more than once." }
v = value
}
}

View File

@ -3,17 +3,16 @@ package net.corda.core.serialization
import com.esotericsoftware.kryo.*
import com.esotericsoftware.kryo.io.Input
import com.esotericsoftware.kryo.io.Output
import com.esotericsoftware.kryo.pool.KryoCallback
import com.esotericsoftware.kryo.pool.KryoPool
import com.esotericsoftware.kryo.util.MapReferenceResolver
import com.google.common.annotations.VisibleForTesting
import net.corda.core.contracts.*
import net.corda.core.crypto.*
import net.corda.core.crypto.Crypto
import net.corda.core.crypto.MetaData
import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.SignatureType
import net.corda.core.crypto.composite.CompositeKey
import net.corda.core.identity.Party
import net.corda.core.transactions.WireTransaction
import net.corda.core.internal.LazyPool
import net.corda.core.utilities.OpaqueBytes
import net.i2p.crypto.eddsa.EdDSAPrivateKey
import net.i2p.crypto.eddsa.EdDSAPublicKey
import net.i2p.crypto.eddsa.spec.EdDSANamedCurveSpec
@ -25,11 +24,8 @@ import org.bouncycastle.cert.X509CertificateHolder
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import java.io.ByteArrayInputStream
import java.io.ByteArrayOutputStream
import java.io.InputStream
import java.lang.reflect.InvocationTargetException
import java.nio.file.Files
import java.nio.file.Path
import java.security.PrivateKey
import java.security.PublicKey
import java.security.cert.CertPath
@ -77,58 +73,6 @@ import kotlin.reflect.jvm.javaType
* TODO: eliminate internal, storage related whitelist issues, such as private keys in blob storage.
*/
// A convenient instance of Kryo pre-configured with some useful things. Used as a default by various functions.
fun p2PKryo(): KryoPool = kryoPool
// Same again, but this has whitelisting turned off for internal storage use only.
fun storageKryo(): KryoPool = internalKryoPool
/**
* A type safe wrapper around a byte array that contains a serialised object. You can call [SerializedBytes.deserialize]
* to get the original object back.
*/
@Suppress("unused") // Type parameter is just for documentation purposes.
class SerializedBytes<T : Any>(bytes: ByteArray, val internalOnly: Boolean = false) : OpaqueBytes(bytes) {
// It's OK to use lazy here because SerializedBytes is configured to use the ImmutableClassSerializer.
val hash: SecureHash by lazy { bytes.sha256() }
fun writeToFile(path: Path): Path = Files.write(path, bytes)
}
// "corda" + majorVersionByte + minorVersionMSB + minorVersionLSB
private val KryoHeaderV0_1: OpaqueBytes = OpaqueBytes("corda\u0000\u0000\u0001".toByteArray())
// Some extension functions that make deserialisation convenient and provide auto-casting of the result.
fun <T : Any> ByteArray.deserialize(kryo: KryoPool = p2PKryo()): T {
Input(this).use {
val header = OpaqueBytes(it.readBytes(8))
if (header != KryoHeaderV0_1) {
throw KryoException("Serialized bytes header does not match any known format.")
}
@Suppress("UNCHECKED_CAST")
return kryo.run { k -> k.readClassAndObject(it) as T }
}
}
// TODO: The preferred usage is with a pool. Try and eliminate use of this from RPC.
fun <T : Any> ByteArray.deserialize(kryo: Kryo): T = deserialize(kryo.asPool())
fun <T : Any> OpaqueBytes.deserialize(kryo: KryoPool = p2PKryo()): T {
return this.bytes.deserialize(kryo)
}
// The more specific deserialize version results in the bytes being cached, which is faster.
@JvmName("SerializedBytesWireTransaction")
fun SerializedBytes<WireTransaction>.deserialize(kryo: KryoPool = p2PKryo()): WireTransaction = WireTransaction.deserialize(this, kryo)
fun <T : Any> SerializedBytes<T>.deserialize(kryo: KryoPool = if (internalOnly) storageKryo() else p2PKryo()): T = bytes.deserialize(kryo)
fun <T : Any> SerializedBytes<T>.deserialize(kryo: Kryo): T = bytes.deserialize(kryo.asPool())
// Internal adapter for use when we haven't yet converted to a pool, or for tests.
private fun Kryo.asPool(): KryoPool = (KryoPool.Builder { this }.build())
/**
* A serialiser that avoids writing the wrapper class to the byte stream, thus ensuring [SerializedBytes] is a pure
* type safety hack.
@ -144,36 +88,6 @@ object SerializedBytesSerializer : Serializer<SerializedBytes<Any>>() {
}
}
/**
* Can be called on any object to convert it to a byte array (wrapped by [SerializedBytes]), regardless of whether
* the type is marked as serializable or was designed for it (so be careful!).
*/
fun <T : Any> T.serialize(kryo: KryoPool = p2PKryo(), internalOnly: Boolean = false): SerializedBytes<T> {
return kryo.run { k -> serialize(k, internalOnly) }
}
private val serializeBufferPool = LazyPool(
newInstance = { ByteArray(64 * 1024) }
)
private val serializeOutputStreamPool = LazyPool(
clear = ByteArrayOutputStream::reset,
shouldReturnToPool = { it.size() < 256 * 1024 }, // Discard if it grew too large
newInstance = { ByteArrayOutputStream(64 * 1024) }
)
fun <T : Any> T.serialize(kryo: Kryo, internalOnly: Boolean = false): SerializedBytes<T> {
return serializeOutputStreamPool.run { stream ->
serializeBufferPool.run { buffer ->
Output(buffer).use {
it.outputStream = stream
it.writeBytes(KryoHeaderV0_1.bytes)
kryo.writeClassAndObject(it, this)
}
SerializedBytes(stream.toByteArray(), internalOnly)
}
}
}
/**
* Serializes properties and deserializes by using the constructor. This assumes that all backed properties are
* set via the constructor and the class is immutable.
@ -463,14 +377,6 @@ inline fun <reified T> readListOfLength(kryo: Kryo, input: Input, minLen: Int =
return list
}
// No ClassResolver only constructor. MapReferenceResolver is the default as used by Kryo in other constructors.
private val internalKryoPool = KryoPool.Builder { DefaultKryoCustomizer.customize(CordaKryo(makeAllButBlacklistedClassResolver())) }.build()
private val kryoPool = KryoPool.Builder { DefaultKryoCustomizer.customize(CordaKryo(makeStandardClassResolver())) }.build()
// No ClassResolver only constructor. MapReferenceResolver is the default as used by Kryo in other constructors.
@VisibleForTesting
fun createTestKryo(): Kryo = DefaultKryoCustomizer.customize(CordaKryo(makeNoWhitelistClassResolver()))
/**
* We need to disable whitelist checking during calls from our Kryo code to register a serializer, since it checks
* for existing registrations and then will enter our [CordaClassResolver.getRegistration] method.
@ -649,25 +555,3 @@ object X509CertificateSerializer : Serializer<X509CertificateHolder>() {
output.writeBytes(obj.encoded)
}
}
class KryoPoolWithContext(val baseKryoPool: KryoPool, val contextKey: Any, val context: Any) : KryoPool {
override fun <T : Any?> run(callback: KryoCallback<T>): T {
val kryo = borrow()
try {
return callback.execute(kryo)
} finally {
release(kryo)
}
}
override fun borrow(): Kryo {
val kryo = baseKryoPool.borrow()
require(kryo.context.put(contextKey, context) == null) { "KryoPool already has context" }
return kryo
}
override fun release(kryo: Kryo) {
requireNotNull(kryo.context.remove(contextKey)) { "Kryo instance lost context while borrowed" }
baseKryoPool.release(kryo)
}
}

View File

@ -0,0 +1,141 @@
package net.corda.core.serialization
import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.sha256
import net.corda.core.internal.WriteOnceProperty
import net.corda.core.serialization.SerializationDefaults.P2P_CONTEXT
import net.corda.core.serialization.SerializationDefaults.SERIALIZATION_FACTORY
import net.corda.core.transactions.WireTransaction
import net.corda.core.utilities.ByteSequence
import net.corda.core.utilities.OpaqueBytes
import net.corda.core.utilities.sequence
/**
* An abstraction for serializing and deserializing objects, with support for versioning of the wire format via
* a header / prefix in the bytes.
*/
interface SerializationFactory {
/**
* Deserialize the bytes in to an object, using the prefixed bytes to determine the format.
*
* @param byteSequence The bytes to deserialize, including a format header prefix.
* @param clazz The class or superclass or the object to be deserialized, or [Any] or [Object] if unknown.
* @param context A context that configures various parameters to deserialization.
*/
fun <T : Any> deserialize(byteSequence: ByteSequence, clazz: Class<T>, context: SerializationContext): T
/**
* Serialize an object to bytes using the preferred serialization format version from the context.
*
* @param obj The object to be serialized.
* @param context A context that configures various parameters to serialization, including the serialization format version.
*/
fun <T : Any> serialize(obj: T, context: SerializationContext): SerializedBytes<T>
}
/**
* Parameters to serialization and deserialization.
*/
interface SerializationContext {
/**
* When serializing, use the format this header sequence represents.
*/
val preferedSerializationVersion: ByteSequence
/**
* The class loader to use for deserialization.
*/
val deserializationClassLoader: ClassLoader
/**
* A whitelist that contains (mostly for security purposes) which classes can be serialized and deserialized.
*/
val whitelist: ClassWhitelist
/**
* A map of any addition properties specific to the particular use case.
*/
val properties: Map<Any, Any>
/**
* Duplicate references to the same object preserved in the wire format and when deserialized when this is true,
* otherwise they appear as new copies of the object.
*/
val objectReferencesEnabled: Boolean
/**
* The use case we are serializing or deserializing for. See [UseCase].
*/
val useCase: UseCase
/**
* Helper method to return a new context based on this context with the property added.
*/
fun withProperty(property: Any, value: Any): SerializationContext
/**
* Helper method to return a new context based on this context with object references disabled.
*/
fun withoutReferences(): SerializationContext
/**
* Helper method to return a new context based on this context with the deserialization class loader changed.
*/
fun withClassLoader(classLoader: ClassLoader): SerializationContext
/**
* Helper method to return a new context based on this context with the given class specifically whitelisted.
*/
fun withWhitelisted(clazz: Class<*>): SerializationContext
/**
* The use case that we are serializing for, since it influences the implementations chosen.
*/
enum class UseCase { P2P, RPCServer, RPCClient, Storage, Checkpoint }
}
/**
* Global singletons to be used as defaults that are injected elsewhere (generally, in the node or in RPC client).
*/
object SerializationDefaults {
var SERIALIZATION_FACTORY: SerializationFactory by WriteOnceProperty()
var P2P_CONTEXT: SerializationContext by WriteOnceProperty()
var RPC_SERVER_CONTEXT: SerializationContext by WriteOnceProperty()
var RPC_CLIENT_CONTEXT: SerializationContext by WriteOnceProperty()
var STORAGE_CONTEXT: SerializationContext by WriteOnceProperty()
var CHECKPOINT_CONTEXT: SerializationContext by WriteOnceProperty()
}
/**
* Convenience extension method for deserializing a ByteSequence, utilising the defaults.
*/
inline fun <reified T : Any> ByteSequence.deserialize(serializationFactory: SerializationFactory = SERIALIZATION_FACTORY, context: SerializationContext = P2P_CONTEXT): T {
return serializationFactory.deserialize(this, T::class.java, context)
}
/**
* Convenience extension method for deserializing SerializedBytes with type matching, utilising the defaults.
*/
inline fun <reified T : Any> SerializedBytes<T>.deserialize(serializationFactory: SerializationFactory = SERIALIZATION_FACTORY, context: SerializationContext = P2P_CONTEXT): T {
return serializationFactory.deserialize(this, T::class.java, context)
}
/**
* Convenience extension method for deserializing a ByteArray, utilising the defaults.
*/
inline fun <reified T : Any> ByteArray.deserialize(serializationFactory: SerializationFactory = SERIALIZATION_FACTORY, context: SerializationContext = P2P_CONTEXT): T = this.sequence().deserialize(serializationFactory, context)
/**
* Convenience extension method for serializing an object of type T, utilising the defaults.
*/
fun <T : Any> T.serialize(serializationFactory: SerializationFactory = SERIALIZATION_FACTORY, context: SerializationContext = P2P_CONTEXT): SerializedBytes<T> {
return serializationFactory.serialize(this, context)
}
/**
* A type safe wrapper around a byte array that contains a serialised object. You can call [SerializedBytes.deserialize]
* to get the original object back.
*/
@Suppress("unused") // Type parameter is just for documentation purposes.
class SerializedBytes<T : Any>(bytes: ByteArray) : OpaqueBytes(bytes) {
// It's OK to use lazy here because SerializedBytes is configured to use the ImmutableClassSerializer.
val hash: SecureHash by lazy { bytes.sha256() }
}
// The more specific deserialize version results in the bytes being cached, which is faster.
@JvmName("SerializedBytesWireTransaction")
fun SerializedBytes<WireTransaction>.deserialize(serializationFactory: SerializationFactory = SERIALIZATION_FACTORY, context: SerializationContext = P2P_CONTEXT): WireTransaction = WireTransaction.deserialize(this, serializationFactory, context)

View File

@ -5,7 +5,6 @@ import com.esotericsoftware.kryo.KryoException
import com.esotericsoftware.kryo.Serializer
import com.esotericsoftware.kryo.io.Input
import com.esotericsoftware.kryo.io.Output
import com.esotericsoftware.kryo.pool.KryoPool
import net.corda.core.node.ServiceHub
import net.corda.core.serialization.SingletonSerializationToken.Companion.singletonSerializationToken
@ -57,17 +56,9 @@ class SerializeAsTokenSerializer<T : SerializeAsToken> : Serializer<T>() {
private val serializationContextKey = SerializeAsTokenContext::class.java
fun Kryo.serializationContext() = context.get(serializationContextKey) as? SerializeAsTokenContext
fun SerializationContext.withTokenContext(serializationContext: SerializeAsTokenContext): SerializationContext = this.withProperty(serializationContextKey, serializationContext)
fun <T> Kryo.withSerializationContext(serializationContext: SerializeAsTokenContext, block: () -> T) = run {
context.containsKey(serializationContextKey) && throw IllegalStateException("There is already a serialization context.")
context.put(serializationContextKey, serializationContext)
try {
block()
} finally {
context.remove(serializationContextKey)
}
}
fun Kryo.serializationContext(): SerializeAsTokenContext? = context.get(serializationContextKey) as? SerializeAsTokenContext
/**
* A context for mapping SerializationTokens to/from SerializeAsTokens.
@ -79,12 +70,8 @@ fun <T> Kryo.withSerializationContext(serializationContext: SerializeAsTokenCont
* on the Kryo instance when serializing to enable/disable tokenization.
*/
class SerializeAsTokenContext internal constructor(val serviceHub: ServiceHub, init: SerializeAsTokenContext.() -> Unit) {
constructor(toBeTokenized: Any, kryoPool: KryoPool, serviceHub: ServiceHub) : this(serviceHub, {
kryoPool.run { kryo ->
kryo.withSerializationContext(this) {
toBeTokenized.serialize(kryo)
}
}
constructor(toBeTokenized: Any, serializationFactory: SerializationFactory, context: SerializationContext, serviceHub: ServiceHub) : this(serviceHub, {
serializationFactory.serialize(toBeTokenized, context.withTokenContext(this))
})
private val classNameToSingleton = mutableMapOf<String, SerializeAsToken>()

View File

@ -7,14 +7,13 @@ import net.corda.core.crypto.PartialMerkleTree
import net.corda.core.crypto.SecureHash
import net.corda.core.identity.Party
import net.corda.core.serialization.CordaSerializable
import net.corda.core.serialization.p2PKryo
import net.corda.core.serialization.SerializationDefaults.P2P_CONTEXT
import net.corda.core.serialization.serialize
import net.corda.core.serialization.withoutReferences
import java.security.PublicKey
import java.util.function.Predicate
fun <T : Any> serializedHash(x: T): SecureHash {
return p2PKryo().run { kryo -> kryo.withoutReferences { x.serialize(kryo).hash } }
return x.serialize(context = P2P_CONTEXT.withoutReferences()).hash
}
/**

View File

@ -1,6 +1,5 @@
package net.corda.core.transactions
import com.esotericsoftware.kryo.pool.KryoPool
import net.corda.core.contracts.*
import net.corda.core.crypto.DigitalSignature
import net.corda.core.crypto.MerkleTree
@ -9,10 +8,9 @@ import net.corda.core.crypto.keys
import net.corda.core.identity.Party
import net.corda.core.indexOfOrThrow
import net.corda.core.node.ServicesForResolution
import net.corda.core.serialization.SerializedBytes
import net.corda.core.serialization.deserialize
import net.corda.core.serialization.p2PKryo
import net.corda.core.serialization.serialize
import net.corda.core.serialization.*
import net.corda.core.serialization.SerializationDefaults.P2P_CONTEXT
import net.corda.core.serialization.SerializationDefaults.SERIALIZATION_FACTORY
import net.corda.core.internal.Emoji
import java.security.PublicKey
import java.security.SignatureException
@ -48,8 +46,8 @@ class WireTransaction(
override val id: SecureHash by lazy { merkleTree.hash }
companion object {
fun deserialize(data: SerializedBytes<WireTransaction>, kryo: KryoPool = p2PKryo()): WireTransaction {
val wtx = data.bytes.deserialize<WireTransaction>(kryo)
fun deserialize(data: SerializedBytes<WireTransaction>, serializationFactory: SerializationFactory = SERIALIZATION_FACTORY, context: SerializationContext = P2P_CONTEXT): WireTransaction {
val wtx = data.deserialize<WireTransaction>(serializationFactory, context)
wtx.cachedBytes = data
return wtx
}

View File

@ -5,39 +5,143 @@ package net.corda.core.utilities
import com.google.common.io.BaseEncoding
import net.corda.core.serialization.CordaSerializable
import java.io.ByteArrayInputStream
import java.util.*
/**
* An abstraction of a byte array, with offset and size that does no copying of bytes unless asked to.
*
* The data of interest typically starts at position [offset] within the [bytes] and is [size] bytes long.
*/
@CordaSerializable
sealed class ByteSequence : Comparable<ByteSequence> {
/**
* The underlying bytes.
*/
abstract val bytes: ByteArray
/**
* The number of bytes this sequence represents.
*/
abstract val size: Int
/**
* The start position of the sequence within the byte array.
*/
abstract val offset: Int
/** Returns a [ByteArrayInputStream] of the bytes */
fun open() = ByteArrayInputStream(bytes, offset, size)
/**
* Create a sub-sequence backed by the same array.
*
* @param offset The offset within this sequence to start the new sequence. Note: not the offset within the backing array.
* @param size The size of the intended sub sequence.
*/
fun subSequence(offset: Int, size: Int): ByteSequence {
require(offset >= 0)
require(offset + size <= this.size)
return if (offset == 0 && size == this.size) this else of(bytes, this.offset + offset, size)
}
companion object {
/**
* Construct a [ByteSequence] given a [ByteArray] and optional offset and size, that represents that potentially
* sub-sequence of bytes. The returned implementation is optimised when the whole [ByteArray] is the sequence.
*/
@JvmStatic
@JvmOverloads
fun of(bytes: ByteArray, offset: Int = 0, size: Int = bytes.size): ByteSequence {
return if (offset == 0 && size == bytes.size && size != 0) OpaqueBytes(bytes) else OpaqueBytesSubSequence(bytes, offset, size)
}
}
/**
* Take the first n bytes of this sequence as a sub-sequence. See [subSequence] for further semantics.
*/
fun take(n: Int): ByteSequence {
require(size >= n)
return subSequence(0, n)
}
/**
* Copy this sequence, complete with new backing array. This can be helpful to break references to potentially
* large backing arrays from small sub-sequences.
*/
fun copy(): ByteSequence = of(bytes.copyOfRange(offset, offset + size))
/**
* Compare byte arrays byte by byte. Arrays that are shorter are deemed less than longer arrays if all the bytes
* of the shorter array equal those in the same position of the longer array.
*/
override fun compareTo(other: ByteSequence): Int {
val min = minOf(this.size, other.size)
// Compare min bytes
for (index in 0 until min) {
val unsignedThis = java.lang.Byte.toUnsignedInt(this.bytes[this.offset + index])
val unsignedOther = java.lang.Byte.toUnsignedInt(other.bytes[other.offset + index])
if (unsignedThis != unsignedOther) {
return Integer.signum(unsignedThis - unsignedOther)
}
}
// First min bytes is the same, so now resort to size
return Integer.signum(this.size - other.size)
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is ByteSequence) return false
if (this.size != other.size) return false
return subArraysEqual(this.bytes, this.offset, this.size, other.bytes, other.offset)
}
private fun subArraysEqual(a: ByteArray, aOffset: Int, length: Int, b: ByteArray, bOffset: Int): Boolean {
var bytesRemaining = length
var aPos = aOffset
var bPos = bOffset
while (bytesRemaining-- > 0) {
if (a[aPos++] != b[bPos++]) return false
}
return true
}
override fun hashCode(): Int {
var result = 1
for (index in offset until (offset + size)) {
result = 31 * result + bytes[index]
}
return result
}
override fun toString(): String = "[${BaseEncoding.base16().encode(bytes, offset, size)}]"
}
/**
* A simple class that wraps a byte array and makes the equals/hashCode/toString methods work as you actually expect.
* In an ideal JVM this would be a value type and be completely overhead free. Project Valhalla is adding such
* functionality to Java, but it won't arrive for a few years yet!
*/
@CordaSerializable
open class OpaqueBytes(val bytes: ByteArray) {
open class OpaqueBytes(override val bytes: ByteArray) : ByteSequence() {
companion object {
@JvmStatic
fun of(vararg b: Byte) = OpaqueBytes(byteArrayOf(*b))
}
init {
check(bytes.isNotEmpty())
require(bytes.isNotEmpty())
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is OpaqueBytes) return false
return Arrays.equals(bytes, other.bytes)
}
override fun hashCode() = Arrays.hashCode(bytes)
override fun toString() = "[" + bytes.toHexString() + "]"
val size: Int get() = bytes.size
/** Returns a [ByteArrayInputStream] of the bytes */
fun open() = ByteArrayInputStream(bytes)
override val size: Int get() = bytes.size
override val offset: Int get() = 0
}
@Deprecated("Use sequence instead")
fun ByteArray.opaque(): OpaqueBytes = OpaqueBytes(this)
fun ByteArray.sequence(offset: Int = 0, size: Int = this.size) = ByteSequence.of(this, offset, size)
fun ByteArray.toHexString(): String = BaseEncoding.base16().encode(this)
fun String.parseAsHex(): ByteArray = BaseEncoding.base16().decode(this)
private class OpaqueBytesSubSequence(override val bytes: ByteArray, override val offset: Int, override val size: Int) : ByteSequence() {
init {
require(offset >= 0 && offset < bytes.size)
require(size >= 0 && size <= bytes.size)
}
}

View File

@ -7,7 +7,6 @@ import net.corda.core.transactions.LedgerTransaction
import net.corda.testing.MEGA_CORP
import net.corda.testing.MINI_CORP
import net.corda.testing.ledger
import net.corda.testing.transaction
import org.junit.Test
import java.time.Instant
import java.time.temporal.ChronoUnit
@ -115,22 +114,26 @@ class TransactionEncumbranceTests {
@Test
fun `state cannot be encumbered by itself`() {
transaction {
input { state }
output(encumbrance = 0) { stateWithNewOwner }
command(MEGA_CORP.owningKey) { Cash.Commands.Move() }
this `fails with` "Missing required encumbrance 0 in OUTPUT"
ledger {
transaction {
input { state }
output(encumbrance = 0) { stateWithNewOwner }
command(MEGA_CORP.owningKey) { Cash.Commands.Move() }
this `fails with` "Missing required encumbrance 0 in OUTPUT"
}
}
}
@Test
fun `encumbrance state index must be valid`() {
transaction {
input { state }
output(encumbrance = 2) { stateWithNewOwner }
output { timeLock }
command(MEGA_CORP.owningKey) { Cash.Commands.Move() }
this `fails with` "Missing required encumbrance 2 in OUTPUT"
ledger {
transaction {
input { state }
output(encumbrance = 2) { stateWithNewOwner }
output { timeLock }
command(MEGA_CORP.owningKey) { Cash.Commands.Move() }
this `fails with` "Missing required encumbrance 2 in OUTPUT"
}
}
}

View File

@ -1,20 +1,17 @@
package net.corda.core.contracts
import net.corda.testing.contracts.DummyContract
import net.corda.testing.contracts.DummyState
import net.corda.core.crypto.newSecureRandom
import net.corda.core.transactions.SignedTransaction
import net.corda.core.transactions.WireTransaction
import net.corda.testing.DUMMY_NOTARY
import net.corda.testing.DUMMY_NOTARY_KEY
import net.corda.testing.MEGA_CORP_KEY
import net.corda.testing.MEGA_CORP_PUBKEY
import net.corda.testing.*
import net.corda.testing.contracts.DummyContract
import net.corda.testing.contracts.DummyState
import net.corda.testing.node.MockServices
import net.corda.testing.node.MockTransactionStorage
import org.junit.Test
import kotlin.test.assertEquals
class TransactionGraphSearchTests {
class TransactionGraphSearchTests : TestDependencyInjectionBase() {
class GraphTransactionStorage(val originTx: SignedTransaction, val inputTx: SignedTransaction) : MockTransactionStorage() {
init {
addTransaction(originTx)

View File

@ -1,9 +1,8 @@
package net.corda.core.contracts
import net.corda.contracts.asset.DUMMY_CASH_ISSUER_KEY
import net.corda.testing.contracts.DummyContract
import net.corda.core.crypto.composite.CompositeKey
import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.composite.CompositeKey
import net.corda.core.crypto.generateKeyPair
import net.corda.core.crypto.sign
import net.corda.core.identity.Party
@ -12,13 +11,13 @@ import net.corda.core.transactions.LedgerTransaction
import net.corda.core.transactions.SignedTransaction
import net.corda.core.transactions.WireTransaction
import net.corda.testing.*
import net.corda.testing.contracts.DummyContract
import org.junit.Test
import java.security.KeyPair
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith
class TransactionTests {
class TransactionTests : TestDependencyInjectionBase() {
private fun makeSigned(wtx: WireTransaction, vararg keys: KeyPair): SignedTransaction {
val bytes: SerializedBytes<WireTransaction> = wtx.serialized
return SignedTransaction(bytes, keys.map { it.sign(wtx.id.bytes) })

View File

@ -11,6 +11,7 @@ import net.corda.core.utilities.OpaqueBytes
import net.corda.node.utilities.loadKeyStore
import net.corda.node.utilities.loadOrCreateKeyStore
import net.corda.node.utilities.save
import net.corda.testing.TestDependencyInjectionBase
import org.bouncycastle.asn1.x500.X500Name
import org.junit.Rule
import org.junit.Test
@ -21,7 +22,7 @@ import kotlin.test.assertFailsWith
import kotlin.test.assertFalse
import kotlin.test.assertTrue
class CompositeKeyTests {
class CompositeKeyTests : TestDependencyInjectionBase() {
@Rule
@JvmField
val tempFolder: TemporaryFolder = TemporaryFolder()

View File

@ -6,21 +6,24 @@ import net.corda.contracts.asset.Cash
import net.corda.core.contracts.*
import net.corda.core.crypto.SecureHash.Companion.zeroHash
import net.corda.core.identity.Party
import net.corda.core.serialization.p2PKryo
import net.corda.core.serialization.serialize
import net.corda.core.transactions.WireTransaction
import net.corda.testing.DUMMY_NOTARY
import net.corda.testing.DUMMY_PUBKEY_1
import net.corda.testing.TEST_TX_TIME
import net.corda.testing.*
import org.junit.Test
import java.security.PublicKey
import java.util.function.Predicate
import kotlin.test.*
class PartialMerkleTreeTest {
class PartialMerkleTreeTest : TestDependencyInjectionBase() {
val nodes = "abcdef"
val hashed = nodes.map { it.serialize().sha256() }
val hashed = nodes.map {
initialiseTestSerialization()
try {
it.serialize().sha256()
} finally {
resetTestSerialization()
}
}
val expectedRoot = MerkleTree.getMerkleTree(hashed.toMutableList() + listOf(zeroHash, zeroHash)).hash
val merkleTree = MerkleTree.getMerkleTree(hashed)
@ -215,9 +218,7 @@ class PartialMerkleTreeTest {
@Test(expected = KryoException::class)
fun `hash map serialization not allowed`() {
val hm1 = hashMapOf("a" to 1, "b" to 2, "c" to 3, "e" to 4)
p2PKryo().run { kryo ->
hm1.serialize(kryo)
}
hm1.serialize()
}
private fun makeSimpleCashWtx(notary: Party, timeWindow: TimeWindow? = null, attachments: List<SecureHash> = emptyList()): WireTransaction {

View File

@ -1,13 +1,21 @@
package net.corda.core.crypto
import net.corda.core.serialization.SerializedBytes
import net.corda.core.serialization.serialize
import net.corda.testing.TestDependencyInjectionBase
import org.junit.Before
import org.junit.Test
import java.security.SignatureException
import kotlin.test.assertEquals
class SignedDataTest {
class SignedDataTest : TestDependencyInjectionBase() {
@Before
fun initialise() {
serialized = data.serialize()
}
val data = "Just a simple test string"
val serialized = data.serialize()
lateinit var serialized: SerializedBytes<String>
@Test
fun `make sure correctly signed data is released`() {

View File

@ -1,5 +1,6 @@
package net.corda.core.crypto
import net.corda.testing.TestDependencyInjectionBase
import org.junit.Test
import java.security.SignatureException
import java.time.Instant
@ -8,7 +9,7 @@ import kotlin.test.assertTrue
/**
* Digital signature MetaData tests
*/
class TransactionSignatureTest {
class TransactionSignatureTest : TestDependencyInjectionBase() {
val testBytes = "12345678901234567890123456789012".toByteArray()

View File

@ -116,7 +116,7 @@ class ContractUpgradeFlowTest {
@Test
fun `2 parties contract upgrade using RPC`() {
rpcDriver {
rpcDriver(initialiseSerialization = false) {
// Create dummy contract.
val twoPartyDummyContract = DummyContract.generateInitial(0, notary, a.info.legalIdentity.ref(1), b.info.legalIdentity.ref(1))
val signedByA = a.services.signInitialTransaction(twoPartyDummyContract)

View File

@ -1,15 +1,15 @@
package net.corda.core.flows
import net.corda.testing.contracts.DummyContract
import net.corda.core.crypto.SecureHash
import net.corda.core.getOrThrow
import net.corda.core.identity.Party
import net.corda.core.utilities.opaque
import net.corda.core.transactions.SignedTransaction
import net.corda.core.utilities.sequence
import net.corda.testing.DUMMY_NOTARY_KEY
import net.corda.testing.MEGA_CORP
import net.corda.testing.MEGA_CORP_KEY
import net.corda.testing.MINI_CORP
import net.corda.testing.contracts.DummyContract
import net.corda.testing.node.MockNetwork
import net.corda.testing.node.MockServices
import org.junit.After
@ -17,7 +17,6 @@ import org.junit.Before
import org.junit.Test
import java.io.ByteArrayOutputStream
import java.io.InputStream
import java.security.SignatureException
import java.util.jar.JarEntry
import java.util.jar.JarOutputStream
import kotlin.test.assertEquals
@ -141,7 +140,7 @@ class ResolveTransactionsFlowTest {
jar.write("Some test file".toByteArray())
jar.closeEntry()
jar.close()
return bs.toByteArray().opaque().open()
return bs.toByteArray().sequence().open()
}
// TODO: this operation should not require an explicit transaction
val id = a.database.transaction {

View File

@ -8,8 +8,6 @@ import net.corda.testing.ALICE
import net.corda.testing.BOB
import net.corda.testing.DUMMY_NOTARY
import net.corda.testing.node.MockNetwork
import org.junit.After
import org.junit.Before
import org.junit.Test
import kotlin.test.assertEquals
import kotlin.test.assertFalse
@ -17,22 +15,10 @@ import kotlin.test.assertNotEquals
import kotlin.test.assertTrue
class TransactionKeyFlowTests {
lateinit var mockNet: MockNetwork
@Before
fun before() {
mockNet = MockNetwork(false)
}
@After
fun cleanUp() {
mockNet.stopNodes()
}
@Test
fun `issue key`() {
// We run this in parallel threads to help catch any race conditions that may exist.
mockNet = MockNetwork(false, true)
val mockNet = MockNetwork(false, true)
// Set up values we'll need
val notaryNode = mockNet.createNotaryNode(null, DUMMY_NOTARY.name)
@ -66,5 +52,7 @@ class TransactionKeyFlowTests {
assertTrue { bobAnonymousIdentity.party.owningKey in bobNode.services.keyManagementService.keys }
assertFalse { aliceAnonymousIdentity.party.owningKey in bobNode.services.keyManagementService.keys }
assertFalse { bobAnonymousIdentity.party.owningKey in aliceNode.services.keyManagementService.keys }
mockNet.stopNodes()
}
}

View File

@ -1,6 +1,5 @@
package net.corda.core.node
import com.esotericsoftware.kryo.Kryo
import com.nhaarman.mockito_kotlin.mock
import com.nhaarman.mockito_kotlin.whenever
import net.corda.core.contracts.*
@ -9,14 +8,15 @@ import net.corda.core.identity.AbstractParty
import net.corda.core.identity.Party
import net.corda.core.node.services.AttachmentStorage
import net.corda.core.serialization.*
import net.corda.core.serialization.SerializationDefaults.P2P_CONTEXT
import net.corda.core.transactions.LedgerTransaction
import net.corda.core.transactions.TransactionBuilder
import net.corda.testing.DUMMY_NOTARY
import net.corda.testing.MEGA_CORP
import net.corda.testing.TestDependencyInjectionBase
import net.corda.testing.node.MockAttachmentStorage
import org.apache.commons.io.IOUtils
import org.junit.Assert
import org.junit.Before
import org.junit.Test
import java.io.ByteArrayInputStream
import java.io.ByteArrayOutputStream
@ -36,15 +36,14 @@ interface DummyContractBackdoor {
val ATTACHMENT_TEST_PROGRAM_ID = AttachmentClassLoaderTests.AttachmentDummyContract()
class AttachmentClassLoaderTests {
class AttachmentClassLoaderTests : TestDependencyInjectionBase() {
companion object {
val ISOLATED_CONTRACTS_JAR_PATH: URL = AttachmentClassLoaderTests::class.java.getResource("isolated.jar")
private fun <T> Kryo.withAttachmentStorage(attachmentStorage: AttachmentStorage, block: () -> T) = run {
context.put(WireTransactionSerializer.attachmentsClassLoaderEnabled, true)
private fun SerializationContext.withAttachmentStorage(attachmentStorage: AttachmentStorage): SerializationContext {
val serviceHub = mock<ServiceHub>()
whenever(serviceHub.attachments).thenReturn(attachmentStorage)
withSerializationContext(SerializeAsTokenContext(serviceHub) {}, block)
return this.withTokenContext(SerializeAsTokenContext(serviceHub) {}).withProperty(WireTransactionSerializer.attachmentsClassLoaderEnabled, true)
}
}
@ -89,16 +88,6 @@ class AttachmentClassLoaderTests {
class ClassLoaderForTests : URLClassLoader(arrayOf(ISOLATED_CONTRACTS_JAR_PATH), FilteringClassLoader)
lateinit var kryo: Kryo
lateinit var kryo2: Kryo
@Before
fun setup() {
// Do not release these back to the pool, since we do some unorthodox modifications to them below.
kryo = p2PKryo().borrow()
kryo2 = p2PKryo().borrow()
}
@Test
fun `dynamically load AnotherDummyContract from isolated contracts jar`() {
val child = ClassLoaderForTests()
@ -229,10 +218,8 @@ class AttachmentClassLoaderTests {
val cl = AttachmentsClassLoader(arrayOf(att0, att1, att2).map { storage.openAttachment(it)!! }, FilteringClassLoader)
kryo.classLoader = cl
kryo.addToWhitelist(contract.javaClass)
val state2 = bytes.deserialize(kryo)
val context = P2P_CONTEXT.withClassLoader(cl).withWhitelisted(contract.javaClass)
val state2 = bytes.deserialize(context = context)
assertTrue(state2.javaClass.classLoader is AttachmentsClassLoader)
assertNotNull(state2)
}
@ -247,8 +234,9 @@ class AttachmentClassLoaderTests {
assertNotNull(data.contract)
kryo2.addToWhitelist(data.contract.javaClass)
val bytes = data.serialize(kryo2)
val context2 = P2P_CONTEXT.withWhitelisted(data.contract.javaClass)
val bytes = data.serialize(context = context2)
val storage = MockAttachmentStorage()
@ -258,20 +246,18 @@ class AttachmentClassLoaderTests {
val cl = AttachmentsClassLoader(arrayOf(att0, att1, att2).map { storage.openAttachment(it)!! }, FilteringClassLoader)
kryo.classLoader = cl
kryo.addToWhitelist(Class.forName("net.corda.contracts.isolated.AnotherDummyContract", true, cl))
val context = P2P_CONTEXT.withClassLoader(cl).withWhitelisted(Class.forName("net.corda.contracts.isolated.AnotherDummyContract", true, cl))
val state2 = bytes.deserialize(kryo)
val state2 = bytes.deserialize(context = context)
assertEquals(cl, state2.contract.javaClass.classLoader)
assertNotNull(state2)
// We should be able to load same class from a different class loader and have them be distinct.
val cl2 = AttachmentsClassLoader(arrayOf(att0, att1, att2).map { storage.openAttachment(it)!! }, FilteringClassLoader)
kryo.classLoader = cl2
kryo.addToWhitelist(Class.forName("net.corda.contracts.isolated.AnotherDummyContract", true, cl2))
val context3 = P2P_CONTEXT.withClassLoader(cl2).withWhitelisted(Class.forName("net.corda.contracts.isolated.AnotherDummyContract", true, cl2))
val state3 = bytes.deserialize(kryo)
val state3 = bytes.deserialize(context = context3)
assertEquals(cl2, state3.contract.javaClass.classLoader)
assertNotNull(state3)
}
@ -295,30 +281,22 @@ class AttachmentClassLoaderTests {
val contract = contractClass.newInstance() as DummyContractBackdoor
val tx = contract.generateInitial(MEGA_CORP.ref(0), 42, DUMMY_NOTARY)
val storage = MockAttachmentStorage()
kryo.addToWhitelist(contract.javaClass)
kryo.addToWhitelist(Class.forName("net.corda.contracts.isolated.AnotherDummyContract\$State", true, child))
kryo.addToWhitelist(Class.forName("net.corda.contracts.isolated.AnotherDummyContract\$Commands\$Create", true, child))
val context = P2P_CONTEXT.withWhitelisted(contract.javaClass)
.withWhitelisted(Class.forName("net.corda.contracts.isolated.AnotherDummyContract\$State", true, child))
.withWhitelisted(Class.forName("net.corda.contracts.isolated.AnotherDummyContract\$Commands\$Create", true, child))
.withAttachmentStorage(storage)
// todo - think about better way to push attachmentStorage down to serializer
val bytes = kryo.withAttachmentStorage(storage) {
val bytes = run {
val attachmentRef = importJar(storage)
tx.addAttachment(storage.openAttachment(attachmentRef)!!.id)
val wireTransaction = tx.toWireTransaction()
wireTransaction.serialize(kryo)
}
// use empty attachmentStorage
kryo2.withAttachmentStorage(storage) {
val copiedWireTransaction = bytes.deserialize(kryo2)
assertEquals(1, copiedWireTransaction.outputs.size)
val contract2 = copiedWireTransaction.outputs[0].data.contract as DummyContractBackdoor
assertEquals(42, contract2.inspectState(copiedWireTransaction.outputs[0].data))
wireTransaction.serialize(context = context)
}
val copiedWireTransaction = bytes.deserialize(context = context)
assertEquals(1, copiedWireTransaction.outputs.size)
val contract2 = copiedWireTransaction.outputs[0].data.contract as DummyContractBackdoor
assertEquals(42, contract2.inspectState(copiedWireTransaction.outputs[0].data))
}
@Test
@ -331,21 +309,19 @@ class AttachmentClassLoaderTests {
// todo - think about better way to push attachmentStorage down to serializer
val attachmentRef = importJar(storage)
val bytes = kryo.withAttachmentStorage(storage) {
val bytes = run {
tx.addAttachment(storage.openAttachment(attachmentRef)!!.id)
val wireTransaction = tx.toWireTransaction()
wireTransaction.serialize(kryo)
wireTransaction.serialize(context = P2P_CONTEXT.withAttachmentStorage(storage))
}
// use empty attachmentStorage
kryo2.withAttachmentStorage(MockAttachmentStorage()) {
val e = assertFailsWith(MissingAttachmentsException::class) {
bytes.deserialize(kryo2)
}
assertEquals(attachmentRef, e.ids.single())
val e = assertFailsWith(MissingAttachmentsException::class) {
bytes.deserialize(context = P2P_CONTEXT.withAttachmentStorage(MockAttachmentStorage()))
}
assertEquals(attachmentRef, e.ids.single())
}
}

View File

@ -1,10 +1,13 @@
package net.corda.core.serialization
import com.esotericsoftware.kryo.Kryo
import com.google.common.primitives.Ints
import net.corda.core.crypto.*
import net.corda.core.utilities.opaque
import net.corda.core.utilities.sequence
import net.corda.node.serialization.KryoServerSerializationScheme
import net.corda.node.services.persistence.NodeAttachmentService
import net.corda.nodeapi.serialization.KryoHeaderV0_1
import net.corda.nodeapi.serialization.SerializationContextImpl
import net.corda.nodeapi.serialization.SerializationFactoryImpl
import net.corda.testing.ALICE
import net.corda.testing.BOB
import net.corda.testing.BOB_PUBKEY
@ -19,62 +22,66 @@ import java.io.InputStream
import java.security.cert.CertPath
import java.security.cert.CertificateFactory
import java.time.Instant
import java.util.*
import kotlin.test.assertEquals
import kotlin.test.assertTrue
class KryoTests {
private lateinit var kryo: Kryo
private lateinit var factory: SerializationFactory
private lateinit var context: SerializationContext
@Before
fun setup() {
// We deliberately do not return this, since we do some unorthodox registering below and do not want to pollute the pool.
kryo = p2PKryo().borrow()
factory = SerializationFactoryImpl().apply { registerScheme(KryoServerSerializationScheme()) }
context = SerializationContextImpl(KryoHeaderV0_1,
javaClass.classLoader,
AllWhitelist,
emptyMap(),
true,
SerializationContext.UseCase.P2P)
}
@Test
fun ok() {
val birthday = Instant.parse("1984-04-17T00:30:00.00Z")
val mike = Person("mike", birthday)
val bits = mike.serialize(kryo)
assertThat(bits.deserialize(kryo)).isEqualTo(Person("mike", birthday))
val bits = mike.serialize(factory, context)
assertThat(bits.deserialize(factory, context)).isEqualTo(Person("mike", birthday))
}
@Test
fun nullables() {
val bob = Person("bob", null)
val bits = bob.serialize(kryo)
assertThat(bits.deserialize(kryo)).isEqualTo(Person("bob", null))
val bits = bob.serialize(factory, context)
assertThat(bits.deserialize(factory, context)).isEqualTo(Person("bob", null))
}
@Test
fun `serialised form is stable when the same object instance is added to the deserialised object graph`() {
kryo.noReferencesWithin<ArrayList<*>>()
val obj = Ints.toByteArray(0x01234567).opaque()
val noReferencesContext = context.withoutReferences()
val obj = Ints.toByteArray(0x01234567).sequence()
val originalList = arrayListOf(obj)
val deserialisedList = originalList.serialize(kryo).deserialize(kryo)
val deserialisedList = originalList.serialize(factory, noReferencesContext).deserialize(factory, noReferencesContext)
originalList += obj
deserialisedList += obj
assertThat(deserialisedList.serialize(kryo)).isEqualTo(originalList.serialize(kryo))
assertThat(deserialisedList.serialize(factory, noReferencesContext)).isEqualTo(originalList.serialize(factory, noReferencesContext))
}
@Test
fun `serialised form is stable when the same object instance occurs more than once, and using java serialisation`() {
kryo.noReferencesWithin<ArrayList<*>>()
val noReferencesContext = context.withoutReferences()
val instant = Instant.ofEpochMilli(123)
val instantCopy = Instant.ofEpochMilli(123)
assertThat(instant).isNotSameAs(instantCopy)
val listWithCopies = arrayListOf(instant, instantCopy)
val listWithSameInstances = arrayListOf(instant, instant)
assertThat(listWithSameInstances.serialize(kryo)).isEqualTo(listWithCopies.serialize(kryo))
assertThat(listWithSameInstances.serialize(factory, noReferencesContext)).isEqualTo(listWithCopies.serialize(factory, noReferencesContext))
}
@Test
fun `cyclic object graph`() {
val cyclic = Cyclic(3)
val bits = cyclic.serialize(kryo)
assertThat(bits.deserialize(kryo)).isEqualTo(cyclic)
val bits = cyclic.serialize(factory, context)
assertThat(bits.deserialize(factory, context)).isEqualTo(cyclic)
}
@Test
@ -86,7 +93,7 @@ class KryoTests {
signature.verify(bitsToSign)
assertThatThrownBy { signature.verify(wrongBits) }
val deserialisedKeyPair = keyPair.serialize(kryo).deserialize(kryo)
val deserialisedKeyPair = keyPair.serialize(factory, context).deserialize(factory, context)
val deserialisedSignature = deserialisedKeyPair.sign(bitsToSign)
deserialisedSignature.verify(bitsToSign)
assertThatThrownBy { deserialisedSignature.verify(wrongBits) }
@ -94,15 +101,15 @@ class KryoTests {
@Test
fun `write and read Kotlin object singleton`() {
val serialised = TestSingleton.serialize(kryo)
val deserialised = serialised.deserialize(kryo)
val serialised = TestSingleton.serialize(factory, context)
val deserialised = serialised.deserialize(factory, context)
assertThat(deserialised).isSameAs(TestSingleton)
}
@Test
fun `InputStream serialisation`() {
val rubbish = ByteArray(12345, { (it * it * 0.12345).toByte() })
val readRubbishStream: InputStream = rubbish.inputStream().serialize(kryo).deserialize(kryo)
val readRubbishStream: InputStream = rubbish.inputStream().serialize(factory, context).deserialize(factory, context)
for (i in 0..12344) {
assertEquals(rubbish[i], readRubbishStream.read().toByte())
}
@ -118,15 +125,16 @@ class KryoTests {
bitSet.set(3)
val meta = MetaData("ECDSA_SECP256K1_SHA256", "M9", SignatureType.FULL, Instant.now(), bitSet, bitSet, testBytes, keyPair1.public)
val serializedMetaData = meta.bytes()
val meta2 = serializedMetaData.deserialize<MetaData>()
val serializedMetaData = meta.serialize(factory, context).bytes
val meta2 = serializedMetaData.deserialize<MetaData>(factory, context)
assertEquals(meta2, meta)
}
@Test
fun `serialize - deserialize Logger`() {
val storageContext: SerializationContext = context // TODO: make it storage context
val logger = LoggerFactory.getLogger("aName")
val logger2 = logger.serialize(storageKryo()).deserialize(storageKryo())
val logger2 = logger.serialize(factory, storageContext).deserialize(factory, storageContext)
assertEquals(logger.name, logger2.name)
assertTrue(logger === logger2)
}
@ -134,7 +142,7 @@ class KryoTests {
@Test
fun `HashCheckingStream (de)serialize`() {
val rubbish = ByteArray(12345, { (it * it * 0.12345).toByte() })
val readRubbishStream: InputStream = NodeAttachmentService.HashCheckingStream(SecureHash.sha256(rubbish), rubbish.size, ByteArrayInputStream(rubbish)).serialize(kryo).deserialize(kryo)
val readRubbishStream: InputStream = NodeAttachmentService.HashCheckingStream(SecureHash.sha256(rubbish), rubbish.size, ByteArrayInputStream(rubbish)).serialize(factory, context).deserialize(factory, context)
for (i in 0..12344) {
assertEquals(rubbish[i], readRubbishStream.read().toByte())
}
@ -144,8 +152,8 @@ class KryoTests {
@Test
fun `serialize - deserialize X509CertififcateHolder`() {
val expected: X509CertificateHolder = X509Utilities.createSelfSignedCACertificate(ALICE.name, Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME))
val serialized = expected.serialize(kryo).bytes
val actual: X509CertificateHolder = serialized.deserialize(kryo)
val serialized = expected.serialize(factory, context).bytes
val actual: X509CertificateHolder = serialized.deserialize(factory, context)
assertEquals(expected, actual)
}
@ -156,8 +164,8 @@ class KryoTests {
val rootCACert = X509Utilities.createSelfSignedCACertificate(ALICE.name, rootCAKey)
val certificate = X509Utilities.createCertificate(CertificateType.TLS, rootCACert, rootCAKey, BOB.name, BOB_PUBKEY)
val expected = certFactory.generateCertPath(listOf(certificate.cert, rootCACert.cert))
val serialized = expected.serialize(kryo).bytes
val actual: CertPath = serialized.deserialize(kryo)
val serialized = expected.serialize(factory, context).bytes
val actual: CertPath = serialized.deserialize(factory, context)
assertEquals(expected, actual)
}

View File

@ -6,24 +6,29 @@ import com.esotericsoftware.kryo.io.Output
import com.nhaarman.mockito_kotlin.mock
import net.corda.core.node.ServiceHub
import net.corda.core.utilities.OpaqueBytes
import net.corda.node.serialization.KryoServerSerializationScheme
import net.corda.nodeapi.serialization.KryoHeaderV0_1
import net.corda.nodeapi.serialization.SerializationContextImpl
import net.corda.nodeapi.serialization.SerializationFactoryImpl
import org.assertj.core.api.Assertions.assertThat
import org.junit.After
import org.junit.Before
import org.junit.Test
import java.io.ByteArrayOutputStream
class SerializationTokenTest {
lateinit var kryo: Kryo
lateinit var factory: SerializationFactory
lateinit var context: SerializationContext
@Before
fun setup() {
kryo = storageKryo().borrow()
}
@After
fun cleanup() {
storageKryo().release(kryo)
factory = SerializationFactoryImpl().apply { registerScheme(KryoServerSerializationScheme()) }
context = SerializationContextImpl(KryoHeaderV0_1,
javaClass.classLoader,
AllWhitelist,
emptyMap(),
true,
SerializationContext.UseCase.P2P)
}
// Large tokenizable object so we can tell from the smaller number of serialized bytes it was actually tokenized
@ -38,20 +43,18 @@ class SerializationTokenTest {
override fun equals(other: Any?) = other is LargeTokenizable && other.bytes.size == this.bytes.size
}
companion object {
private fun serializeAsTokenContext(toBeTokenized: Any) = SerializeAsTokenContext(toBeTokenized, storageKryo(), mock<ServiceHub>())
}
private fun serializeAsTokenContext(toBeTokenized: Any) = SerializeAsTokenContext(toBeTokenized, factory, context, mock<ServiceHub>())
@Test
fun `write token and read tokenizable`() {
val tokenizableBefore = LargeTokenizable()
val context = serializeAsTokenContext(tokenizableBefore)
kryo.withSerializationContext(context) {
val serializedBytes = tokenizableBefore.serialize(kryo)
assertThat(serializedBytes.size).isLessThan(tokenizableBefore.numBytes)
val tokenizableAfter = serializedBytes.deserialize(kryo)
assertThat(tokenizableAfter).isSameAs(tokenizableBefore)
}
val testContext = this.context.withTokenContext(context)
val serializedBytes = tokenizableBefore.serialize(factory, testContext)
assertThat(serializedBytes.size).isLessThan(tokenizableBefore.numBytes)
val tokenizableAfter = serializedBytes.deserialize(factory, testContext)
assertThat(tokenizableAfter).isSameAs(tokenizableBefore)
}
private class UnitSerializeAsToken : SingletonSerializeAsToken()
@ -60,51 +63,50 @@ class SerializationTokenTest {
fun `write and read singleton`() {
val tokenizableBefore = UnitSerializeAsToken()
val context = serializeAsTokenContext(tokenizableBefore)
kryo.withSerializationContext(context) {
val serializedBytes = tokenizableBefore.serialize(kryo)
val tokenizableAfter = serializedBytes.deserialize(kryo)
val testContext = this.context.withTokenContext(context)
val serializedBytes = tokenizableBefore.serialize(factory, testContext)
val tokenizableAfter = serializedBytes.deserialize(factory, testContext)
assertThat(tokenizableAfter).isSameAs(tokenizableBefore)
}
}
@Test(expected = UnsupportedOperationException::class)
fun `new token encountered after context init`() {
val tokenizableBefore = UnitSerializeAsToken()
val context = serializeAsTokenContext(emptyList<Any>())
kryo.withSerializationContext(context) {
tokenizableBefore.serialize(kryo)
}
val testContext = this.context.withTokenContext(context)
tokenizableBefore.serialize(factory, testContext)
}
@Test(expected = UnsupportedOperationException::class)
fun `deserialize unregistered token`() {
val tokenizableBefore = UnitSerializeAsToken()
val context = serializeAsTokenContext(emptyList<Any>())
kryo.withSerializationContext(context) {
val serializedBytes = tokenizableBefore.toToken(serializeAsTokenContext(emptyList<Any>())).serialize(kryo)
serializedBytes.deserialize(kryo)
}
val testContext = this.context.withTokenContext(context)
val serializedBytes = tokenizableBefore.toToken(serializeAsTokenContext(emptyList<Any>())).serialize(factory, testContext)
serializedBytes.deserialize(factory, testContext)
}
@Test(expected = KryoException::class)
fun `no context set`() {
val tokenizableBefore = UnitSerializeAsToken()
tokenizableBefore.serialize(kryo)
tokenizableBefore.serialize(factory, context)
}
@Test(expected = KryoException::class)
fun `deserialize non-token`() {
val tokenizableBefore = UnitSerializeAsToken()
val context = serializeAsTokenContext(tokenizableBefore)
kryo.withSerializationContext(context) {
val stream = ByteArrayOutputStream()
val testContext = this.context.withTokenContext(context)
val kryo: Kryo = DefaultKryoCustomizer.customize(CordaKryo(makeNoWhitelistClassResolver()))
val stream = ByteArrayOutputStream()
Output(stream).use {
it.write(KryoHeaderV0_1.bytes)
kryo.writeClass(it, SingletonSerializeAsToken::class.java)
kryo.writeObject(it, emptyList<Any>())
}
val serializedBytes = SerializedBytes<Any>(stream.toByteArray())
serializedBytes.deserialize(kryo)
}
val serializedBytes = SerializedBytes<Any>(stream.toByteArray())
serializedBytes.deserialize(factory, testContext)
}
private class WrongTypeSerializeAsToken : SerializeAsToken {
@ -119,9 +121,8 @@ class SerializationTokenTest {
fun `token returns unexpected type`() {
val tokenizableBefore = WrongTypeSerializeAsToken()
val context = serializeAsTokenContext(tokenizableBefore)
kryo.withSerializationContext(context) {
val serializedBytes = tokenizableBefore.serialize(kryo)
serializedBytes.deserialize(kryo)
}
val testContext = this.context.withTokenContext(context)
val serializedBytes = tokenizableBefore.serialize(factory, testContext)
serializedBytes.deserialize(factory, testContext)
}
}

View File

@ -17,7 +17,7 @@ import kotlin.test.assertFailsWith
val TEST_PROGRAM_ID = TransactionSerializationTests.TestCash()
class TransactionSerializationTests {
class TransactionSerializationTests : TestDependencyInjectionBase() {
class TestCash : Contract {
override val legalContractReference = SecureHash.sha256("TestCash")

View File

@ -4,10 +4,11 @@ import net.corda.core.crypto.random63BitValue
import net.corda.core.serialization.CordaSerializable
import net.corda.core.serialization.deserialize
import net.corda.core.serialization.serialize
import net.corda.testing.TestDependencyInjectionBase
import org.assertj.core.api.Assertions.assertThat
import org.junit.Test
class KotlinUtilsTest {
class KotlinUtilsTest : TestDependencyInjectionBase() {
@Test
fun `transient property which is null`() {
val test = NullTransientProperty()

View File

@ -7,6 +7,8 @@ import com.google.common.collect.testing.features.CollectionSize
import junit.framework.TestSuite
import net.corda.core.serialization.deserialize
import net.corda.core.serialization.serialize
import net.corda.testing.initialiseTestSerialization
import net.corda.testing.resetTestSerialization
import org.assertj.core.api.Assertions.assertThat
import org.assertj.core.api.Assertions.assertThatThrownBy
import org.junit.Test
@ -47,9 +49,15 @@ class NonEmptySetTest {
@Test
fun `serialize deserialize`() {
val original = NonEmptySet.of(-17, 22, 17)
val copy = original.serialize().deserialize()
assertThat(copy).isEqualTo(original).isNotSameAs(original)
initialiseTestSerialization()
try {
val original = NonEmptySet.of(-17, 22, 17)
val copy = original.serialize().deserialize()
assertThat(copy).isEqualTo(original).isNotSameAs(original)
} finally {
resetTestSerialization()
}
}
}

View File

@ -4,8 +4,13 @@ import com.esotericsoftware.kryo.Kryo
import com.esotericsoftware.kryo.KryoSerializable
import com.esotericsoftware.kryo.io.Input
import com.esotericsoftware.kryo.io.Output
import net.corda.core.serialization.createTestKryo
import net.corda.core.serialization.AllWhitelist
import net.corda.core.serialization.SerializationContext
import net.corda.core.serialization.serialize
import net.corda.node.serialization.KryoServerSerializationScheme
import net.corda.nodeapi.serialization.KryoHeaderV0_1
import net.corda.nodeapi.serialization.SerializationContextImpl
import net.corda.nodeapi.serialization.SerializationFactoryImpl
import org.junit.Before
import org.junit.Test
import java.util.*
@ -106,10 +111,6 @@ class ProgressTrackerTest {
}
}
val kryo = createTestKryo().apply {
// This is required to make sure Kryo walks through the auto-generated members for the lambda below.
fieldSerializerConfig.isIgnoreSyntheticFields = false
}
pt.setChildProgressTracker(SimpleSteps.TWO, pt2)
class Tmp {
val unserializable = Unserializable()
@ -119,6 +120,13 @@ class ProgressTrackerTest {
}
}
Tmp()
pt.serialize(kryo)
val factory = SerializationFactoryImpl().apply { registerScheme(KryoServerSerializationScheme()) }
val context = SerializationContextImpl(KryoHeaderV0_1,
javaClass.classLoader,
AllWhitelist,
emptyMap(),
true,
SerializationContext.UseCase.P2P)
pt.serialize(factory, context)
}
}

View File

@ -15,10 +15,10 @@ import net.corda.testing.BOB
import net.corda.testing.DUMMY_NOTARY
import net.corda.flows.CashIssueFlow
import net.corda.flows.CashPaymentFlow
import net.corda.testing.driver.driver
import net.corda.node.services.startFlowPermission
import net.corda.node.services.transactions.ValidatingNotaryService
import net.corda.nodeapi.User
import net.corda.testing.driver.driver
import net.corda.testing.expect
import net.corda.testing.expectEvents
import net.corda.testing.parallel

View File

@ -209,7 +209,7 @@ class CommercialPaperTestsGeneric {
@Test
fun `issue move and then redeem`() {
initialiseTestSerialization()
val dataSourcePropsAlice = makeTestDataSourceProperties()
val databaseAlice = configureDatabase(dataSourcePropsAlice)
databaseAlice.transaction {
@ -307,5 +307,6 @@ class CommercialPaperTestsGeneric {
validRedemption.toLedgerTransaction(aliceServices).verify()
// soft lock not released after success either!!! (as transaction not recorded)
}
resetTestSerialization()
}
}

View File

@ -1,8 +1,6 @@
package net.corda.contracts.asset
import net.corda.testing.contracts.fillWithSomeTestCash
import net.corda.core.contracts.*
import net.corda.testing.contracts.DummyState
import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.generateKeyPair
import net.corda.core.identity.AbstractParty
@ -10,13 +8,15 @@ import net.corda.core.identity.AnonymousParty
import net.corda.core.identity.Party
import net.corda.core.node.services.VaultService
import net.corda.core.node.services.unconsumedStates
import net.corda.core.utilities.OpaqueBytes
import net.corda.core.transactions.SignedTransaction
import net.corda.core.transactions.WireTransaction
import net.corda.core.utilities.OpaqueBytes
import net.corda.node.services.vault.NodeVaultService
import net.corda.node.utilities.CordaPersistence
import net.corda.node.utilities.configureDatabase
import net.corda.testing.*
import net.corda.testing.contracts.DummyState
import net.corda.testing.contracts.fillWithSomeTestCash
import net.corda.testing.node.MockKeyManagementService
import net.corda.testing.node.MockServices
import net.corda.testing.node.makeTestDataSourceProperties
@ -26,7 +26,7 @@ import java.security.KeyPair
import java.util.*
import kotlin.test.*
class CashTests {
class CashTests : TestDependencyInjectionBase() {
val defaultRef = OpaqueBytes(ByteArray(1, { 1 }))
val defaultIssuer = MEGA_CORP.ref(defaultRef)
val inState = Cash.State(
@ -76,6 +76,7 @@ class CashTests {
vaultStatesUnconsumed = miniCorpServices.vaultService.unconsumedStates<Cash.State>().toList()
}
resetTestSerialization()
}
@Test
@ -152,6 +153,7 @@ class CashTests {
@Test
fun generateIssueRaw() {
initialiseTestSerialization()
// Test generation works.
val tx: WireTransaction = TransactionType.General.Builder(notary = null).apply {
Cash().generateIssue(this, 100.DOLLARS `issued by` MINI_CORP.ref(12, 34), owner = AnonymousParty(DUMMY_PUBKEY_1), notary = DUMMY_NOTARY)
@ -167,6 +169,7 @@ class CashTests {
@Test
fun generateIssueFromAmount() {
initialiseTestSerialization()
// Test issuance from an issued amount
val amount = 100.DOLLARS `issued by` MINI_CORP.ref(12, 34)
val tx: WireTransaction = TransactionType.General.Builder(notary = null).apply {
@ -239,6 +242,7 @@ class CashTests {
*/
@Test(expected = IllegalStateException::class)
fun `reject issuance with inputs`() {
initialiseTestSerialization()
// Issue some cash
var ptx = TransactionType.General.Builder(DUMMY_NOTARY)
@ -490,6 +494,7 @@ class CashTests {
*/
@Test
fun generateSimpleExit() {
initialiseTestSerialization()
val wtx = makeExit(100.DOLLARS, MEGA_CORP, 1)
assertEquals(WALLET[0].ref, wtx.inputs[0])
assertEquals(0, wtx.outputs.size)
@ -505,6 +510,7 @@ class CashTests {
*/
@Test
fun generatePartialExit() {
initialiseTestSerialization()
val wtx = makeExit(50.DOLLARS, MEGA_CORP, 1)
assertEquals(WALLET[0].ref, wtx.inputs[0])
assertEquals(1, wtx.outputs.size)
@ -516,6 +522,7 @@ class CashTests {
*/
@Test
fun generateAbsentExit() {
initialiseTestSerialization()
assertFailsWith<InsufficientBalanceException> { makeExit(100.POUNDS, MEGA_CORP, 1) }
}
@ -524,6 +531,7 @@ class CashTests {
*/
@Test
fun generateInvalidReferenceExit() {
initialiseTestSerialization()
assertFailsWith<InsufficientBalanceException> { makeExit(100.POUNDS, MEGA_CORP, 2) }
}
@ -532,6 +540,7 @@ class CashTests {
*/
@Test
fun generateInsufficientExit() {
initialiseTestSerialization()
assertFailsWith<InsufficientBalanceException> { makeExit(1000.DOLLARS, MEGA_CORP, 1) }
}
@ -540,6 +549,7 @@ class CashTests {
*/
@Test
fun generateOwnerWithNoStatesExit() {
initialiseTestSerialization()
assertFailsWith<InsufficientBalanceException> { makeExit(100.POUNDS, CHARLIE, 1) }
}
@ -548,6 +558,7 @@ class CashTests {
*/
@Test
fun generateExitWithEmptyVault() {
initialiseTestSerialization()
assertFailsWith<InsufficientBalanceException> {
val tx = TransactionType.General.Builder(DUMMY_NOTARY)
Cash().generateExit(tx, Amount(100, Issued(CHARLIE.ref(1), GBP)), emptyList())
@ -556,9 +567,8 @@ class CashTests {
@Test
fun generateSimpleDirectSpend() {
initialiseTestSerialization()
database.transaction {
val wtx = makeSpend(100.DOLLARS, THEIR_IDENTITY_1)
@Suppress("UNCHECKED_CAST")
@ -571,7 +581,7 @@ class CashTests {
@Test
fun generateSimpleSpendWithParties() {
initialiseTestSerialization()
database.transaction {
val tx = TransactionType.General.Builder(DUMMY_NOTARY)
@ -583,7 +593,7 @@ class CashTests {
@Test
fun generateSimpleSpendWithChange() {
initialiseTestSerialization()
database.transaction {
val wtx = makeSpend(10.DOLLARS, THEIR_IDENTITY_1)
@ -599,7 +609,7 @@ class CashTests {
@Test
fun generateSpendWithTwoInputs() {
initialiseTestSerialization()
database.transaction {
val wtx = makeSpend(500.DOLLARS, THEIR_IDENTITY_1)
@ -615,7 +625,7 @@ class CashTests {
@Test
fun generateSpendMixedDeposits() {
initialiseTestSerialization()
database.transaction {
val wtx = makeSpend(580.DOLLARS, THEIR_IDENTITY_1)
assertEquals(3, wtx.inputs.size)
@ -636,7 +646,7 @@ class CashTests {
@Test
fun generateSpendInsufficientBalance() {
initialiseTestSerialization()
database.transaction {
val e: InsufficientBalanceException = assertFailsWith("balance") {

View File

@ -13,6 +13,7 @@ import net.corda.core.utilities.OpaqueBytes
import net.corda.core.utilities.days
import net.corda.core.utilities.hours
import net.corda.testing.*
import org.junit.After
import net.corda.testing.contracts.DummyState
import net.corda.testing.node.MockServices
import org.junit.Test
@ -57,6 +58,11 @@ class ObligationTests {
}
}
@After
fun reset() {
resetTestSerialization()
}
@Test
fun trivial() {
transaction {
@ -127,6 +133,7 @@ class ObligationTests {
this.verifies()
}
initialiseTestSerialization()
// Test generation works.
val tx = TransactionType.General.Builder(notary = null).apply {
Obligation<Currency>().generateIssue(this, MINI_CORP, megaCorpDollarSettlement, 100.DOLLARS.quantity,
@ -142,6 +149,7 @@ class ObligationTests {
assertEquals(tx.outputs[0].data, expected)
assertTrue(tx.commands[0].value is Obligation.Commands.Issue)
assertEquals(MINI_CORP_PUBKEY, tx.commands[0].signers[0])
resetTestSerialization()
// We can consume $1000 in a transaction and output $2000 as long as it's signed by an issuer.
transaction {
@ -204,6 +212,7 @@ class ObligationTests {
*/
@Test(expected = IllegalStateException::class)
fun `reject issuance with inputs`() {
initialiseTestSerialization()
// Issue some obligation
val tx = TransactionType.General.Builder(DUMMY_NOTARY).apply {
Obligation<Currency>().generateIssue(this, MINI_CORP, megaCorpDollarSettlement, 100.DOLLARS.quantity,
@ -221,6 +230,7 @@ class ObligationTests {
/** Test generating a transaction to net two obligations of the same size, and therefore there are no outputs. */
@Test
fun `generate close-out net transaction`() {
initialiseTestSerialization()
val obligationAliceToBob = oneMillionDollars.OBLIGATION between Pair(ALICE, BOB)
val obligationBobToAlice = oneMillionDollars.OBLIGATION between Pair(BOB, ALICE)
val tx = TransactionType.General.Builder(DUMMY_NOTARY).apply {
@ -232,6 +242,7 @@ class ObligationTests {
/** Test generating a transaction to net two obligations of the different sizes, and confirm the balance is correct. */
@Test
fun `generate close-out net transaction with remainder`() {
initialiseTestSerialization()
val obligationAliceToBob = (2000000.DOLLARS `issued by` defaultIssuer).OBLIGATION between Pair(ALICE, BOB)
val obligationBobToAlice = oneMillionDollars.OBLIGATION between Pair(BOB, ALICE)
val tx = TransactionType.General.Builder(DUMMY_NOTARY).apply {
@ -246,6 +257,7 @@ class ObligationTests {
/** Test generating a transaction to net two obligations of the same size, and therefore there are no outputs. */
@Test
fun `generate payment net transaction`() {
initialiseTestSerialization()
val obligationAliceToBob = oneMillionDollars.OBLIGATION between Pair(ALICE, BOB)
val obligationBobToAlice = oneMillionDollars.OBLIGATION between Pair(BOB, ALICE)
val tx = TransactionType.General.Builder(DUMMY_NOTARY).apply {
@ -257,6 +269,7 @@ class ObligationTests {
/** Test generating a transaction to two obligations, where one is bigger than the other and therefore there is a remainder. */
@Test
fun `generate payment net transaction with remainder`() {
initialiseTestSerialization()
val obligationAliceToBob = oneMillionDollars.OBLIGATION between Pair(ALICE, BOB)
val obligationBobToAlice = (2000000.DOLLARS `issued by` defaultIssuer).OBLIGATION between Pair(BOB, ALICE)
val tx = TransactionType.General.Builder(null).apply {
@ -271,6 +284,7 @@ class ObligationTests {
/** Test generating a transaction to mark outputs as having defaulted. */
@Test
fun `generate set lifecycle`() {
initialiseTestSerialization()
// We don't actually verify the states, this is just here to make things look sensible
val dueBefore = TEST_TX_TIME - 7.days
@ -309,6 +323,7 @@ class ObligationTests {
/** Test generating a transaction to settle an obligation. */
@Test
fun `generate settlement transaction`() {
initialiseTestSerialization()
val cashTx = TransactionType.General.Builder(null).apply {
Cash().generateIssue(this, 100.DOLLARS `issued by` defaultIssuer, MINI_CORP, DUMMY_NOTARY)
}.toWireTransaction()
@ -857,6 +872,7 @@ class ObligationTests {
@Test
fun `summing balances due between parties`() {
initialiseTestSerialization()
val simple: Map<Pair<AbstractParty, AbstractParty>, Amount<Currency>> = mapOf(Pair(Pair(ALICE, BOB), Amount(100000000, GBP)))
val expected: Map<AbstractParty, Long> = mapOf(Pair(ALICE, -100000000L), Pair(BOB, 100000000L))
val actual = sumAmountsDue(simple)

View File

@ -10,6 +10,9 @@ import net.corda.contracts.testing.SignedTransactionGenerator
import net.corda.core.flows.BroadcastTransactionFlow.NotifyTxRequest
import net.corda.core.serialization.deserialize
import net.corda.core.serialization.serialize
import net.corda.testing.initialiseTestSerialization
import net.corda.testing.resetTestSerialization
import org.junit.After
import org.junit.runner.RunWith
import kotlin.test.assertEquals
@ -18,10 +21,16 @@ class BroadcastTransactionFlowTest {
class NotifyTxRequestMessageGenerator : Generator<NotifyTxRequest>(NotifyTxRequest::class.java) {
override fun generate(random: SourceOfRandomness, status: GenerationStatus): NotifyTxRequest {
initialiseTestSerialization()
return NotifyTxRequest(tx = SignedTransactionGenerator().generate(random, status))
}
}
@After
fun teardown() {
resetTestSerialization()
}
@Property
fun serialiseDeserialiseOfNotifyMessageWorks(@From(NotifyTxRequestMessageGenerator::class) message: NotifyTxRequest) {
val serialized = message.serialize().bytes

View File

@ -1,7 +1,6 @@
package net.corda.nodeapi
import com.esotericsoftware.kryo.pool.KryoPool
import net.corda.core.serialization.KryoPoolWithContext
import net.corda.core.serialization.SerializationContext
import net.corda.core.serialization.deserialize
import net.corda.core.serialization.serialize
import net.corda.core.utilities.Try
@ -96,12 +95,12 @@ object RPCApi {
val methodName: String,
val arguments: List<Any?>
) : ClientToServer() {
fun writeToClientMessage(kryoPool: KryoPool, message: ClientMessage) {
fun writeToClientMessage(context: SerializationContext, message: ClientMessage) {
MessageUtil.setJMSReplyTo(message, clientAddress)
message.putIntProperty(TAG_FIELD_NAME, Tag.RPC_REQUEST.ordinal)
message.putLongProperty(RPC_ID_FIELD_NAME, id.toLong)
message.putStringProperty(METHOD_NAME_FIELD_NAME, methodName)
message.bodyBuffer.writeBytes(arguments.serialize(kryoPool).bytes)
message.bodyBuffer.writeBytes(arguments.serialize(context = context).bytes)
}
}
@ -119,14 +118,14 @@ object RPCApi {
}
companion object {
fun fromClientMessage(kryoPool: KryoPool, message: ClientMessage): ClientToServer {
fun fromClientMessage(context: SerializationContext, message: ClientMessage): ClientToServer {
val tag = Tag.values()[message.getIntProperty(TAG_FIELD_NAME)]
return when (tag) {
RPCApi.ClientToServer.Tag.RPC_REQUEST -> RpcRequest(
clientAddress = MessageUtil.getJMSReplyTo(message),
id = RpcRequestId(message.getLongProperty(RPC_ID_FIELD_NAME)),
methodName = message.getStringProperty(METHOD_NAME_FIELD_NAME),
arguments = message.getBodyAsByteArray().deserialize(kryoPool)
arguments = message.getBodyAsByteArray().deserialize(context = context)
)
RPCApi.ClientToServer.Tag.OBSERVABLES_CLOSED -> {
val ids = ArrayList<ObservableId>()
@ -148,16 +147,16 @@ object RPCApi {
OBSERVATION
}
abstract fun writeToClientMessage(kryoPool: KryoPool, message: ClientMessage)
abstract fun writeToClientMessage(context: SerializationContext, message: ClientMessage)
data class RpcReply(
val id: RpcRequestId,
val result: Try<Any?>
) : ServerToClient() {
override fun writeToClientMessage(kryoPool: KryoPool, message: ClientMessage) {
override fun writeToClientMessage(context: SerializationContext, message: ClientMessage) {
message.putIntProperty(TAG_FIELD_NAME, Tag.RPC_REPLY.ordinal)
message.putLongProperty(RPC_ID_FIELD_NAME, id.toLong)
message.bodyBuffer.writeBytes(result.serialize(kryoPool).bytes)
message.bodyBuffer.writeBytes(result.serialize(context = context).bytes)
}
}
@ -165,31 +164,31 @@ object RPCApi {
val id: ObservableId,
val content: Notification<Any>
) : ServerToClient() {
override fun writeToClientMessage(kryoPool: KryoPool, message: ClientMessage) {
override fun writeToClientMessage(context: SerializationContext, message: ClientMessage) {
message.putIntProperty(TAG_FIELD_NAME, Tag.OBSERVATION.ordinal)
message.putLongProperty(OBSERVABLE_ID_FIELD_NAME, id.toLong)
message.bodyBuffer.writeBytes(content.serialize(kryoPool).bytes)
message.bodyBuffer.writeBytes(content.serialize(context = context).bytes)
}
}
companion object {
fun fromClientMessage(kryoPool: KryoPool, message: ClientMessage): ServerToClient {
fun fromClientMessage(context: SerializationContext, message: ClientMessage): ServerToClient {
val tag = Tag.values()[message.getIntProperty(TAG_FIELD_NAME)]
return when (tag) {
RPCApi.ServerToClient.Tag.RPC_REPLY -> {
val id = RpcRequestId(message.getLongProperty(RPC_ID_FIELD_NAME))
val poolWithIdContext = KryoPoolWithContext(kryoPool, RpcRequestOrObservableIdKey, id.toLong)
val poolWithIdContext = context.withProperty(RpcRequestOrObservableIdKey, id.toLong)
RpcReply(
id = id,
result = message.getBodyAsByteArray().deserialize(poolWithIdContext)
result = message.getBodyAsByteArray().deserialize(context = poolWithIdContext)
)
}
RPCApi.ServerToClient.Tag.OBSERVATION -> {
val id = ObservableId(message.getLongProperty(OBSERVABLE_ID_FIELD_NAME))
val poolWithIdContext = KryoPoolWithContext(kryoPool, RpcRequestOrObservableIdKey, id.toLong)
val poolWithIdContext = context.withProperty(RpcRequestOrObservableIdKey, id.toLong)
Observation(
id = id,
content = message.getBodyAsByteArray().deserialize(poolWithIdContext)
content = message.getBodyAsByteArray().deserialize(context = poolWithIdContext)
)
}
}

View File

@ -46,7 +46,7 @@ class PermissionException(msg: String) : RuntimeException(msg)
// The Kryo used for the RPC wire protocol. Every type in the wire protocol is listed here explicitly.
// This is annoying to write out, but will make it easier to formalise the wire protocol when the time comes,
// because we can see everything we're using in one place.
class RPCKryo(observableSerializer: Serializer<Observable<Any>>) : CordaKryo(makeStandardClassResolver()) {
class RPCKryo(observableSerializer: Serializer<Observable<Any>>, whitelist: ClassWhitelist) : CordaKryo(CordaClassResolver(whitelist)) {
init {
DefaultKryoCustomizer.customize(this)

View File

@ -0,0 +1,263 @@
package net.corda.nodeapi.serialization
import co.paralleluniverse.fibers.Fiber
import co.paralleluniverse.io.serialization.kryo.KryoSerializer
import com.esotericsoftware.kryo.Kryo
import com.esotericsoftware.kryo.KryoException
import com.esotericsoftware.kryo.Serializer
import com.esotericsoftware.kryo.io.Input
import com.esotericsoftware.kryo.io.Output
import com.esotericsoftware.kryo.pool.KryoPool
import io.requery.util.CloseableIterator
import net.corda.core.internal.LazyPool
import net.corda.core.serialization.*
import net.corda.core.utilities.ByteSequence
import net.corda.core.utilities.OpaqueBytes
import java.io.ByteArrayOutputStream
import java.io.NotSerializableException
import java.util.*
import java.util.concurrent.ConcurrentHashMap
object NotSupportedSeralizationScheme : SerializationScheme {
private fun doThrow(): Nothing = throw UnsupportedOperationException("Serialization scheme not supported.")
override fun canDeserializeVersion(byteSequence: ByteSequence, target: SerializationContext.UseCase): Boolean = doThrow()
override fun <T : Any> deserialize(byteSequence: ByteSequence, clazz: Class<T>, context: SerializationContext): T = doThrow()
override fun <T : Any> serialize(obj: T, context: SerializationContext): SerializedBytes<T> = doThrow()
}
data class SerializationContextImpl(override val preferedSerializationVersion: ByteSequence,
override val deserializationClassLoader: ClassLoader,
override val whitelist: ClassWhitelist,
override val properties: Map<Any, Any>,
override val objectReferencesEnabled: Boolean,
override val useCase: SerializationContext.UseCase) : SerializationContext {
override fun withProperty(property: Any, value: Any): SerializationContext {
return copy(properties = properties + (property to value))
}
override fun withoutReferences(): SerializationContext {
return copy(objectReferencesEnabled = false)
}
override fun withClassLoader(classLoader: ClassLoader): SerializationContext {
return copy(deserializationClassLoader = classLoader)
}
override fun withWhitelisted(clazz: Class<*>): SerializationContext {
return copy(whitelist = object : ClassWhitelist {
override fun hasListed(type: Class<*>): Boolean = whitelist.hasListed(type) || type.name == clazz.name
})
}
}
open class SerializationFactoryImpl : SerializationFactory {
private val creator: List<StackTraceElement> = Exception().stackTrace.asList()
private val registeredSchemes: MutableCollection<SerializationScheme> = Collections.synchronizedCollection(mutableListOf())
// TODO: This is read-mostly. Probably a faster implementation to be found.
private val schemes: ConcurrentHashMap<Pair<ByteSequence, SerializationContext.UseCase>, SerializationScheme> = ConcurrentHashMap()
private fun schemeFor(byteSequence: ByteSequence, target: SerializationContext.UseCase): SerializationScheme {
// truncate sequence to 8 bytes
return schemes.computeIfAbsent(byteSequence.take(8).copy() to target) {
for (scheme in registeredSchemes) {
if (scheme.canDeserializeVersion(it.first, it.second)) {
return@computeIfAbsent scheme
}
}
NotSupportedSeralizationScheme
}
}
@Throws(NotSerializableException::class)
override fun <T : Any> deserialize(byteSequence: ByteSequence, clazz: Class<T>, context: SerializationContext): T = schemeFor(byteSequence, context.useCase).deserialize(byteSequence, clazz, context)
override fun <T : Any> serialize(obj: T, context: SerializationContext): SerializedBytes<T> {
return schemeFor(context.preferedSerializationVersion, context.useCase).serialize(obj, context)
}
fun registerScheme(scheme: SerializationScheme) {
check(schemes.isEmpty()) { "All serialization schemes must be registered before any scheme is used." }
registeredSchemes += scheme
}
val alreadyRegisteredSchemes: Collection<SerializationScheme> get() = Collections.unmodifiableCollection(registeredSchemes)
override fun toString(): String {
return "${this.javaClass.name} registeredSchemes=$registeredSchemes ${creator.joinToString("\n")}"
}
override fun equals(other: Any?): Boolean {
return other is SerializationFactoryImpl &&
other.registeredSchemes == this.registeredSchemes
}
override fun hashCode(): Int = registeredSchemes.hashCode()
}
private object AutoCloseableSerialisationDetector : Serializer<AutoCloseable>() {
override fun write(kryo: Kryo, output: Output, closeable: AutoCloseable) {
val message = if (closeable is CloseableIterator<*>) {
"A live Iterator pointing to the database has been detected during flow checkpointing. This may be due " +
"to a Vault query - move it into a private method."
} else {
"${closeable.javaClass.name}, which is a closeable resource, has been detected during flow checkpointing. " +
"Restoring such resources across node restarts is not supported. Make sure code accessing it is " +
"confined to a private method or the reference is nulled out."
}
throw UnsupportedOperationException(message)
}
override fun read(kryo: Kryo, input: Input, type: Class<AutoCloseable>) = throw IllegalStateException("Should not reach here!")
}
abstract class AbstractKryoSerializationScheme : SerializationScheme {
private val kryoPoolsForContexts = ConcurrentHashMap<Pair<ClassWhitelist, ClassLoader>, KryoPool>()
protected abstract fun rpcClientKryoPool(context: SerializationContext): KryoPool
protected abstract fun rpcServerKryoPool(context: SerializationContext): KryoPool
private fun getPool(context: SerializationContext): KryoPool {
return kryoPoolsForContexts.computeIfAbsent(Pair(context.whitelist, context.deserializationClassLoader)) {
when (context.useCase) {
SerializationContext.UseCase.Checkpoint ->
KryoPool.Builder {
val serializer = Fiber.getFiberSerializer(false) as KryoSerializer
val classResolver = makeNoWhitelistClassResolver().apply { setKryo(serializer.kryo) }
// TODO The ClassResolver can only be set in the Kryo constructor and Quasar doesn't provide us with a way of doing that
val field = Kryo::class.java.getDeclaredField("classResolver").apply { isAccessible = true }
serializer.kryo.apply {
field.set(this, classResolver)
DefaultKryoCustomizer.customize(this)
addDefaultSerializer(AutoCloseable::class.java, AutoCloseableSerialisationDetector)
classLoader = it.second
}
}.build()
SerializationContext.UseCase.RPCClient ->
rpcClientKryoPool(context)
SerializationContext.UseCase.RPCServer ->
rpcServerKryoPool(context)
else ->
KryoPool.Builder {
DefaultKryoCustomizer.customize(CordaKryo(CordaClassResolver(context.whitelist))).apply { classLoader = it.second }
}.build()
}
}
}
private fun <T : Any> withContext(kryo: Kryo, context: SerializationContext, block: (Kryo) -> T): T {
kryo.context.ensureCapacity(context.properties.size)
context.properties.forEach { kryo.context.put(it.key, it.value) }
try {
return block(kryo)
} finally {
kryo.context.clear()
}
}
override fun <T : Any> deserialize(byteSequence: ByteSequence, clazz: Class<T>, context: SerializationContext): T {
val pool = getPool(context)
Input(byteSequence.bytes, byteSequence.offset, byteSequence.size).use { input ->
val header = OpaqueBytes(input.readBytes(8))
if (header != KryoHeaderV0_1) {
throw KryoException("Serialized bytes header does not match expected format.")
}
return pool.run { kryo ->
withContext(kryo, context) {
@Suppress("UNCHECKED_CAST")
if (context.objectReferencesEnabled) {
kryo.readClassAndObject(input) as T
} else {
kryo.withoutReferences { kryo.readClassAndObject(input) as T }
}
}
}
}
}
override fun <T : Any> serialize(obj: T, context: SerializationContext): SerializedBytes<T> {
val pool = getPool(context)
return pool.run { kryo ->
withContext(kryo, context) {
serializeOutputStreamPool.run { stream ->
serializeBufferPool.run { buffer ->
Output(buffer).use {
it.outputStream = stream
it.writeBytes(KryoHeaderV0_1.bytes)
if (context.objectReferencesEnabled) {
kryo.writeClassAndObject(it, obj)
} else {
kryo.withoutReferences { kryo.writeClassAndObject(it, obj) }
}
}
SerializedBytes(stream.toByteArray())
}
}
}
}
}
}
private val serializeBufferPool = LazyPool(
newInstance = { ByteArray(64 * 1024) }
)
private val serializeOutputStreamPool = LazyPool(
clear = ByteArrayOutputStream::reset,
shouldReturnToPool = { it.size() < 256 * 1024 }, // Discard if it grew too large
newInstance = { ByteArrayOutputStream(64 * 1024) }
)
// "corda" + majorVersionByte + minorVersionMSB + minorVersionLSB
val KryoHeaderV0_1: OpaqueBytes = OpaqueBytes("corda\u0000\u0000\u0001".toByteArray(Charsets.UTF_8))
val KRYO_P2P_CONTEXT = SerializationContextImpl(KryoHeaderV0_1,
SerializationDefaults.javaClass.classLoader,
GlobalTransientClassWhiteList(BuiltInExceptionsWhitelist()),
emptyMap(),
true,
SerializationContext.UseCase.P2P)
val KRYO_RPC_SERVER_CONTEXT = SerializationContextImpl(KryoHeaderV0_1,
SerializationDefaults.javaClass.classLoader,
GlobalTransientClassWhiteList(BuiltInExceptionsWhitelist()),
emptyMap(),
true,
SerializationContext.UseCase.RPCServer)
val KRYO_RPC_CLIENT_CONTEXT = SerializationContextImpl(KryoHeaderV0_1,
SerializationDefaults.javaClass.classLoader,
GlobalTransientClassWhiteList(BuiltInExceptionsWhitelist()),
emptyMap(),
true,
SerializationContext.UseCase.RPCClient)
val KRYO_STORAGE_CONTEXT = SerializationContextImpl(KryoHeaderV0_1,
SerializationDefaults.javaClass.classLoader,
AllButBlacklisted,
emptyMap(),
true,
SerializationContext.UseCase.Storage)
val KRYO_CHECKPOINT_CONTEXT = SerializationContextImpl(KryoHeaderV0_1,
SerializationDefaults.javaClass.classLoader,
QuasarWhitelist,
emptyMap(),
true,
SerializationContext.UseCase.Checkpoint)
object QuasarWhitelist : ClassWhitelist {
override fun hasListed(type: Class<*>): Boolean = true
}
interface SerializationScheme {
// byteSequence expected to just be the 8 bytes necessary for versioning
fun canDeserializeVersion(byteSequence: ByteSequence, target: SerializationContext.UseCase): Boolean
@Throws(NotSerializableException::class)
fun <T : Any> deserialize(byteSequence: ByteSequence, clazz: Class<T>, context: SerializationContext): T
@Throws(NotSerializableException::class)
fun <T : Any> serialize(obj: T, context: SerializationContext): SerializedBytes<T>
}

View File

@ -21,10 +21,7 @@ import net.corda.core.serialization.deserialize
import net.corda.core.serialization.serialize
import net.corda.core.transactions.LedgerTransaction
import net.corda.node.services.vault.schemas.requery.*
import net.corda.testing.ALICE
import net.corda.testing.BOB
import net.corda.testing.DUMMY_NOTARY
import net.corda.testing.DUMMY_NOTARY_KEY
import net.corda.testing.*
import net.corda.testing.contracts.DummyContract
import org.h2.jdbcx.JdbcDataSource
import org.junit.After
@ -40,7 +37,7 @@ import kotlin.test.assertNotNull
import kotlin.test.assertNull
import kotlin.test.assertTrue
class VaultSchemaTest {
class VaultSchemaTest : TestDependencyInjectionBase() {
var instance: KotlinEntityDataStore<Persistable>? = null
val data: KotlinEntityDataStore<Persistable> get() = instance!!

View File

@ -9,13 +9,13 @@ import net.corda.core.messaging.startFlow
import net.corda.core.node.services.ServiceInfo
import net.corda.core.node.services.ServiceType
import net.corda.testing.ALICE
import net.corda.testing.driver.driver
import net.corda.node.internal.NodeStartup
import net.corda.node.services.startFlowPermission
import net.corda.nodeapi.User
import net.corda.testing.driver.ListenProcessDeathException
import net.corda.testing.driver.NetworkMapStartStrategy
import net.corda.testing.ProjectStructure.projectRootDir
import net.corda.testing.driver.driver
import org.assertj.core.api.Assertions.assertThat
import org.assertj.core.api.Assertions.assertThatThrownBy
import org.junit.Test

View File

@ -10,7 +10,10 @@ import com.google.common.collect.testing.features.MapFeature
import com.google.common.collect.testing.features.SetFeature
import com.google.common.collect.testing.testers.*
import junit.framework.TestSuite
import net.corda.testing.TestDependencyInjectionBase
import net.corda.testing.initialiseTestSerialization
import net.corda.testing.node.makeTestDataSourceProperties
import net.corda.testing.resetTestSerialization
import org.assertj.core.api.Assertions.assertThat
import org.jetbrains.exposed.sql.Transaction
import org.jetbrains.exposed.sql.transactions.TransactionManager
@ -42,6 +45,7 @@ class JDBCHashMapTestSuite {
@JvmStatic
@BeforeClass
fun before() {
initialiseTestSerialization()
database = configureDatabase(makeTestDataSourceProperties())
setUpDatabaseTx()
loadOnInitFalseMap = JDBCHashMap<String, String>("test_map_false", loadOnInit = false)
@ -57,6 +61,7 @@ class JDBCHashMapTestSuite {
fun after() {
closeDatabaseTx()
database.close()
resetTestSerialization()
}
@JvmStatic
@ -198,7 +203,7 @@ class JDBCHashMapTestSuite {
*
* If the Map reloads, then so will the Set as it just delegates.
*/
class MapCanBeReloaded {
class MapCanBeReloaded : TestDependencyInjectionBase() {
private val ops = listOf(Triple(AddOrRemove.ADD, "A", "1"),
Triple(AddOrRemove.ADD, "B", "2"),
Triple(AddOrRemove.ADD, "C", "3"),
@ -235,7 +240,6 @@ class JDBCHashMapTestSuite {
database.close()
}
@Test
fun `fill map and check content after reconstruction`() {
database.transaction {

View File

@ -151,7 +151,7 @@ abstract class MQSecurityTest : NodeBasedTest() {
}
fun loginToRPC(target: NetworkHostAndPort, rpcUser: User, sslConfiguration: SSLConfiguration? = null): CordaRPCOps {
return CordaRPCClient(target, sslConfiguration).start(rpcUser.username, rpcUser.password).proxy
return CordaRPCClient(target, sslConfiguration, initialiseSerialization = false).start(rpcUser.username, rpcUser.password).proxy
}
fun loginToRPCAndGetClientQueue(): String {

View File

@ -4,12 +4,15 @@ import com.codahale.metrics.JmxReporter
import com.google.common.util.concurrent.Futures
import com.google.common.util.concurrent.ListenableFuture
import com.google.common.util.concurrent.SettableFuture
import net.corda.core.*
import net.corda.core.flatMap
import net.corda.core.messaging.RPCOps
import net.corda.core.node.ServiceHub
import net.corda.core.node.services.ServiceInfo
import net.corda.core.serialization.SerializationDefaults
import net.corda.core.thenMatch
import net.corda.core.utilities.*
import net.corda.node.VersionInfo
import net.corda.node.serialization.KryoServerSerializationScheme
import net.corda.node.serialization.NodeClock
import net.corda.node.services.RPCUserService
import net.corda.node.services.RPCUserServiceImpl
@ -29,6 +32,7 @@ import net.corda.nodeapi.ArtemisTcpTransport
import net.corda.nodeapi.ConnectionDirection
import net.corda.nodeapi.internal.ShutdownHook
import net.corda.nodeapi.internal.addShutdownHook
import net.corda.nodeapi.serialization.*
import org.apache.activemq.artemis.api.core.ActiveMQNotConnectedException
import org.apache.activemq.artemis.api.core.RoutingType
import org.apache.activemq.artemis.api.core.client.ActiveMQClient
@ -54,7 +58,8 @@ import kotlin.system.exitProcess
open class Node(override val configuration: FullNodeConfiguration,
advertisedServices: Set<ServiceInfo>,
val versionInfo: VersionInfo,
clock: Clock = NodeClock()) : AbstractNode(configuration, advertisedServices, clock) {
clock: Clock = NodeClock(),
val initialiseSerialization: Boolean = true) : AbstractNode(configuration, advertisedServices, clock) {
companion object {
private val logger = loggerFor<Node>()
var renderBasicInfoToConsole = true
@ -290,6 +295,9 @@ open class Node(override val configuration: FullNodeConfiguration,
val startupComplete: ListenableFuture<Unit> = SettableFuture.create()
override fun start(): Node {
if (initialiseSerialization) {
initialiseSerialization()
}
super.start()
networkMapRegistrationFuture.thenMatch({
@ -321,6 +329,16 @@ open class Node(override val configuration: FullNodeConfiguration,
return this
}
private fun initialiseSerialization() {
SerializationDefaults.SERIALIZATION_FACTORY = SerializationFactoryImpl().apply {
registerScheme(KryoServerSerializationScheme())
}
SerializationDefaults.P2P_CONTEXT = KRYO_P2P_CONTEXT
SerializationDefaults.RPC_SERVER_CONTEXT = KRYO_RPC_SERVER_CONTEXT
SerializationDefaults.STORAGE_CONTEXT = KRYO_STORAGE_CONTEXT
SerializationDefaults.CHECKPOINT_CONTEXT = KRYO_CHECKPOINT_CONTEXT
}
/** Starts a blocking event loop for message dispatch. */
fun run() {
(network as NodeMessagingClient).run(messageBroker!!.serverControl)

View File

@ -0,0 +1,26 @@
package net.corda.node.serialization
import com.esotericsoftware.kryo.pool.KryoPool
import net.corda.core.serialization.DefaultKryoCustomizer
import net.corda.core.serialization.SerializationContext
import net.corda.core.utilities.ByteSequence
import net.corda.node.services.messaging.RpcServerObservableSerializer
import net.corda.nodeapi.RPCKryo
import net.corda.nodeapi.serialization.AbstractKryoSerializationScheme
import net.corda.nodeapi.serialization.KryoHeaderV0_1
class KryoServerSerializationScheme : AbstractKryoSerializationScheme() {
override fun canDeserializeVersion(byteSequence: ByteSequence, target: SerializationContext.UseCase): Boolean {
return byteSequence.equals(KryoHeaderV0_1) && target != SerializationContext.UseCase.RPCClient
}
override fun rpcClientKryoPool(context: SerializationContext): KryoPool {
throw UnsupportedOperationException()
}
override fun rpcServerKryoPool(context: SerializationContext): KryoPool {
return KryoPool.Builder {
DefaultKryoCustomizer.customize(RPCKryo(RpcServerObservableSerializer, context.whitelist)).apply { classLoader = context.deserializationClassLoader }
}.build()
}
}

View File

@ -154,7 +154,8 @@ fun <M : Any> MessagingService.onNext(topic: String, sessionId: Long): Listenabl
val messageFuture = SettableFuture.create<M>()
runOnNextMessage(topic, sessionId) { message ->
messageFuture.catch {
message.data.deserialize<M>()
@Suppress("UNCHECKED_CAST")
message.data.deserialize<Any>() as M
}
}
return messageFuture

View File

@ -1,18 +1,21 @@
package net.corda.node.services.messaging
import com.google.common.util.concurrent.ListenableFuture
import net.corda.core.*
import net.corda.core.ThreadBox
import net.corda.core.andForget
import net.corda.core.crypto.random63BitValue
import net.corda.core.getOrThrow
import net.corda.core.messaging.CordaRPCOps
import net.corda.core.messaging.MessageRecipients
import net.corda.core.messaging.RPCOps
import net.corda.core.messaging.SingleMessageRecipient
import net.corda.core.node.services.PartyInfo
import net.corda.core.node.services.TransactionVerifierService
import net.corda.core.utilities.opaque
import net.corda.core.thenMatch
import net.corda.core.transactions.LedgerTransaction
import net.corda.core.utilities.NetworkHostAndPort
import net.corda.core.utilities.loggerFor
import net.corda.core.utilities.sequence
import net.corda.core.utilities.trace
import net.corda.node.VersionInfo
import net.corda.node.services.RPCUserService
@ -346,7 +349,7 @@ class NodeMessagingClient(override val config: NodeConfiguration,
private val message: ClientMessage) : ReceivedMessage {
override val data: ByteArray by lazy { ByteArray(message.bodySize).apply { message.bodyBuffer.readBytes(this) } }
override val debugTimestamp: Instant get() = Instant.ofEpochMilli(message.timestamp)
override fun toString() = "${topicSession.topic}#${data.opaque()}"
override fun toString() = "${topicSession.topic}#${data.sequence()}"
}
private fun deliver(msg: ReceivedMessage): Boolean {

View File

@ -4,7 +4,6 @@ import com.esotericsoftware.kryo.Kryo
import com.esotericsoftware.kryo.Serializer
import com.esotericsoftware.kryo.io.Input
import com.esotericsoftware.kryo.io.Output
import com.esotericsoftware.kryo.pool.KryoPool
import com.google.common.cache.Cache
import com.google.common.cache.CacheBuilder
import com.google.common.cache.RemovalListener
@ -17,7 +16,8 @@ import net.corda.core.internal.LazyStickyPool
import net.corda.core.internal.LifeCycle
import net.corda.core.messaging.RPCOps
import net.corda.core.utilities.seconds
import net.corda.core.serialization.KryoPoolWithContext
import net.corda.core.serialization.SerializationContext
import net.corda.core.serialization.SerializationDefaults.RPC_SERVER_CONTEXT
import net.corda.core.utilities.*
import net.corda.node.services.RPCUserService
import net.corda.nodeapi.*
@ -81,7 +81,6 @@ class RPCServer(
) {
private companion object {
val log = loggerFor<RPCServer>()
val kryoPool = KryoPool.Builder { RPCKryo(RpcServerObservableSerializer) }.build()
}
private enum class State {
UNSTARTED,
@ -258,7 +257,7 @@ class RPCServer(
private fun clientArtemisMessageHandler(artemisMessage: ClientMessage) {
lifeCycle.requireState(State.STARTED)
val clientToServer = RPCApi.ClientToServer.fromClientMessage(kryoPool, artemisMessage)
val clientToServer = RPCApi.ClientToServer.fromClientMessage(RPC_SERVER_CONTEXT, artemisMessage)
log.debug { "-> RPC -> $clientToServer" }
when (clientToServer) {
is RPCApi.ClientToServer.RpcRequest -> {
@ -302,8 +301,7 @@ class RPCServer(
clientAddress,
serverControl!!,
sessionAndProducerPool,
observationSendExecutor!!,
kryoPool
observationSendExecutor!!
)
val buffered = bufferIfQueueNotBound(clientAddress, reply, observableContext)
@ -385,19 +383,19 @@ class ObservableContext(
val clientAddress: SimpleString,
val serverControl: ActiveMQServerControl,
val sessionAndProducerPool: LazyStickyPool<ArtemisProducer>,
val observationSendExecutor: ExecutorService,
kryoPool: KryoPool
val observationSendExecutor: ExecutorService
) {
private companion object {
val log = loggerFor<ObservableContext>()
}
private val kryoPoolWithObservableContext = RpcServerObservableSerializer.createPoolWithContext(kryoPool, this)
private val serializationContextWithObservableContext = RpcServerObservableSerializer.createContext(this)
fun sendMessage(serverToClient: RPCApi.ServerToClient) {
try {
sessionAndProducerPool.run(rpcRequestId) {
val artemisMessage = it.session.createMessage(false)
serverToClient.writeToClientMessage(kryoPoolWithObservableContext, artemisMessage)
serverToClient.writeToClientMessage(serializationContextWithObservableContext, artemisMessage)
it.producer.send(clientAddress, artemisMessage)
log.debug("<- RPC <- $serverToClient")
}
@ -408,12 +406,12 @@ class ObservableContext(
}
}
private object RpcServerObservableSerializer : Serializer<Observable<Any>>() {
object RpcServerObservableSerializer : Serializer<Observable<Any>>() {
private object RpcObservableContextKey
private val log = loggerFor<RpcServerObservableSerializer>()
fun createPoolWithContext(kryoPool: KryoPool, observableContext: ObservableContext): KryoPool {
return KryoPoolWithContext(kryoPool, RpcObservableContextKey, observableContext)
fun createContext(observableContext: ObservableContext): SerializationContext {
return RPC_SERVER_CONTEXT.withProperty(RpcServerObservableSerializer.RpcObservableContextKey, observableContext)
}
override fun read(kryo: Kryo?, input: Input?, type: Class<Observable<Any>>?): Observable<Any> {

View File

@ -1,10 +1,10 @@
package net.corda.node.services.persistence
import net.corda.core.crypto.SecureHash
import net.corda.core.serialization.SerializationDefaults.CHECKPOINT_CONTEXT
import net.corda.core.serialization.SerializedBytes
import net.corda.core.serialization.deserialize
import net.corda.core.serialization.serialize
import net.corda.core.serialization.storageKryo
import net.corda.node.services.api.Checkpoint
import net.corda.node.services.api.CheckpointStorage
import net.corda.node.utilities.*
@ -39,7 +39,7 @@ class DBCheckpointStorage : CheckpointStorage {
private val checkpointStorage = synchronizedMap(CheckpointMap())
override fun addCheckpoint(checkpoint: Checkpoint) {
checkpointStorage.put(checkpoint.id, checkpoint.serialize(storageKryo(), true))
checkpointStorage.put(checkpoint.id, checkpoint.serialize(context = CHECKPOINT_CONTEXT))
}
override fun removeCheckpoint(checkpoint: Checkpoint) {
@ -49,7 +49,7 @@ class DBCheckpointStorage : CheckpointStorage {
override fun forEach(block: (Checkpoint) -> Boolean) {
synchronized(checkpointStorage) {
for (checkpoint in checkpointStorage.values) {
if (!block(checkpoint.deserialize())) {
if (!block(checkpoint.deserialize(context = CHECKPOINT_CONTEXT))) {
break
}
}

View File

@ -2,20 +2,12 @@ package net.corda.node.services.statemachine
import co.paralleluniverse.fibers.Fiber
import co.paralleluniverse.fibers.FiberExecutorScheduler
import co.paralleluniverse.io.serialization.kryo.KryoSerializer
import co.paralleluniverse.strands.Strand
import com.codahale.metrics.Gauge
import com.esotericsoftware.kryo.ClassResolver
import com.esotericsoftware.kryo.Kryo
import com.esotericsoftware.kryo.KryoException
import com.esotericsoftware.kryo.Serializer
import com.esotericsoftware.kryo.io.Input
import com.esotericsoftware.kryo.io.Output
import com.esotericsoftware.kryo.pool.KryoPool
import com.google.common.collect.HashMultimap
import com.google.common.util.concurrent.ListenableFuture
import com.google.common.util.concurrent.MoreExecutors
import io.requery.util.CloseableIterator
import net.corda.core.ThreadBox
import net.corda.core.bufferUntilSubscribed
import net.corda.core.crypto.SecureHash
@ -25,9 +17,10 @@ import net.corda.core.flows.FlowInitiator
import net.corda.core.flows.FlowLogic
import net.corda.core.flows.StateMachineRunId
import net.corda.core.identity.Party
import net.corda.core.internal.declaredField
import net.corda.core.messaging.DataFeed
import net.corda.core.serialization.*
import net.corda.core.serialization.SerializationDefaults.CHECKPOINT_CONTEXT
import net.corda.core.serialization.SerializationDefaults.SERIALIZATION_FACTORY
import net.corda.core.then
import net.corda.core.utilities.Try
import net.corda.core.utilities.debug
@ -85,34 +78,6 @@ class StateMachineManager(val serviceHub: ServiceHubInternal,
inner class FiberScheduler : FiberExecutorScheduler("Same thread scheduler", executor)
private val quasarKryoPool = KryoPool.Builder {
val serializer = Fiber.getFiberSerializer(false) as KryoSerializer
val classResolver = makeNoWhitelistClassResolver().apply { setKryo(serializer.kryo) }
serializer.kryo.apply {
// TODO The ClassResolver can only be set in the Kryo constructor and Quasar doesn't provide us with a way of doing that
declaredField<ClassResolver>(Kryo::class, "classResolver").value = classResolver
DefaultKryoCustomizer.customize(this)
addDefaultSerializer(AutoCloseable::class.java, AutoCloseableSerialisationDetector)
}
}.build()
// TODO Move this into the blacklist and upgrade the blacklist to allow custom messages
private object AutoCloseableSerialisationDetector : Serializer<AutoCloseable>() {
override fun write(kryo: Kryo, output: Output, closeable: AutoCloseable) {
val message = if (closeable is CloseableIterator<*>) {
"A live Iterator pointing to the database has been detected during flow checkpointing. This may be due " +
"to a Vault query - move it into a private method."
} else {
"${closeable.javaClass.name}, which is a closeable resource, has been detected during flow checkpointing. " +
"Restoring such resources across node restarts is not supported. Make sure code accessing it is " +
"confined to a private method or the reference is nulled out."
}
throw UnsupportedOperationException(message)
}
override fun read(kryo: Kryo, input: Input, type: Class<AutoCloseable>) = throw IllegalStateException("Should not reach here!")
}
companion object {
private val logger = loggerFor<StateMachineManager>()
internal val sessionTopic = TopicSession("platform.session")
@ -173,7 +138,7 @@ class StateMachineManager(val serviceHub: ServiceHubInternal,
internal val tokenizableServices = ArrayList<Any>()
// Context for tokenized services in checkpoints
private val serializationContext by lazy {
SerializeAsTokenContext(tokenizableServices, quasarKryoPool, serviceHub)
SerializeAsTokenContext(tokenizableServices, SERIALIZATION_FACTORY, CHECKPOINT_CONTEXT, serviceHub)
}
/** Returns a list of all state machines executing the given flow logic at the top level (subflows do not count) */
@ -410,22 +375,12 @@ class StateMachineManager(val serviceHub: ServiceHubInternal,
}
private fun serializeFiber(fiber: FlowStateMachineImpl<*>): SerializedBytes<FlowStateMachineImpl<*>> {
return quasarKryoPool.run { kryo ->
// add the map of tokens -> tokenizedServices to the kyro context
kryo.withSerializationContext(serializationContext) {
fiber.serialize(kryo)
}
}
return fiber.serialize(context = CHECKPOINT_CONTEXT.withTokenContext(serializationContext))
}
private fun deserializeFiber(checkpoint: Checkpoint, logger: Logger): FlowStateMachineImpl<*>? {
return try {
quasarKryoPool.run { kryo ->
// put the map of token -> tokenized into the kryo context
kryo.withSerializationContext(serializationContext) {
checkpoint.serializedFiber.deserialize(kryo)
}.apply { fromCheckpoint = true }
}
checkpoint.serializedFiber.deserialize<FlowStateMachineImpl<*>>(context = CHECKPOINT_CONTEXT.withTokenContext(serializationContext)).apply { fromCheckpoint = true }
} catch (t: Throwable) {
logger.error("Encountered unrestorable checkpoint!", t)
null

View File

@ -13,9 +13,9 @@ import net.corda.core.node.services.VaultQueryException
import net.corda.core.node.services.VaultQueryService
import net.corda.core.node.services.vault.*
import net.corda.core.node.services.vault.QueryCriteria.VaultCustomQueryCriteria
import net.corda.core.serialization.SerializationDefaults.STORAGE_CONTEXT
import net.corda.core.serialization.SingletonSerializeAsToken
import net.corda.core.serialization.deserialize
import net.corda.core.serialization.storageKryo
import net.corda.core.utilities.debug
import net.corda.core.utilities.loggerFor
import net.corda.node.services.database.HibernateConfiguration
@ -96,7 +96,7 @@ class HibernateVaultQueryImpl(hibernateConfig: HibernateConfiguration,
return@forEachIndexed
val vaultState = result[0] as VaultSchemaV1.VaultStates
val stateRef = StateRef(SecureHash.parse(vaultState.stateRef!!.txId!!), vaultState.stateRef!!.index!!)
val state = vaultState.contractState.deserialize<TransactionState<T>>(storageKryo())
val state = vaultState.contractState.deserialize<TransactionState<T>>(context = STORAGE_CONTEXT)
statesMeta.add(Vault.StateMetadata(stateRef, vaultState.contractStateClassName, vaultState.recordedTime, vaultState.consumedTime, vaultState.stateStatus, vaultState.notaryName, vaultState.notaryKey, vaultState.lockId, vaultState.lockUpdateTime))
statesAndRefs.add(StateAndRef(state, stateRef))
}

View File

@ -26,10 +26,10 @@ import net.corda.core.node.services.StatesNotAvailableException
import net.corda.core.node.services.Vault
import net.corda.core.node.services.VaultService
import net.corda.core.node.services.unconsumedStates
import net.corda.core.serialization.SerializationDefaults.STORAGE_CONTEXT
import net.corda.core.serialization.SingletonSerializeAsToken
import net.corda.core.serialization.deserialize
import net.corda.core.serialization.serialize
import net.corda.core.serialization.storageKryo
import net.corda.core.tee
import net.corda.core.transactions.TransactionBuilder
import net.corda.core.transactions.WireTransaction
@ -95,7 +95,7 @@ class NodeVaultService(private val services: ServiceHub, dataSourceProperties: P
index = it.key.index
stateStatus = Vault.StateStatus.UNCONSUMED
contractStateClassName = it.value.state.data.javaClass.name
contractState = it.value.state.serialize(storageKryo()).bytes
contractState = it.value.state.serialize(context = STORAGE_CONTEXT).bytes
notaryName = it.value.state.notary.name.toString()
notaryKey = it.value.state.notary.owningKey.toBase58String()
recordedTime = services.clock.instant()
@ -198,7 +198,7 @@ class NodeVaultService(private val services: ServiceHub, dataSourceProperties: P
Sequence { iterator }
.map { it ->
val stateRef = StateRef(SecureHash.parse(it.txId), it.index)
val state = it.contractState.deserialize<TransactionState<T>>(storageKryo())
val state = it.contractState.deserialize<TransactionState<T>>(context = STORAGE_CONTEXT)
Vault.StateMetadata(stateRef, it.contractStateClassName, it.recordedTime, it.consumedTime, it.stateStatus, it.notaryName, it.notaryKey, it.lockId, it.lockUpdateTime)
StateAndRef(state, stateRef)
}
@ -217,7 +217,7 @@ class NodeVaultService(private val services: ServiceHub, dataSourceProperties: P
.and(VaultSchema.VaultStates::index eq it.index)
result.get()?.each {
val stateRef = StateRef(SecureHash.parse(it.txId), it.index)
val state = it.contractState.deserialize<TransactionState<*>>(storageKryo())
val state = it.contractState.deserialize<TransactionState<*>>(context = STORAGE_CONTEXT)
results += StateAndRef(state, stateRef)
}
}
@ -380,7 +380,7 @@ class NodeVaultService(private val services: ServiceHub, dataSourceProperties: P
val txHash = SecureHash.parse(rs.getString(1))
val index = rs.getInt(2)
val stateRef = StateRef(txHash, index)
val state = rs.getBytes(3).deserialize<TransactionState<T>>(storageKryo())
val state = rs.getBytes(3).deserialize<TransactionState<T>>(context = STORAGE_CONTEXT)
val pennies = rs.getLong(4)
totalPennies = rs.getLong(5)
val rowLockId = rs.getString(6)
@ -435,7 +435,7 @@ class NodeVaultService(private val services: ServiceHub, dataSourceProperties: P
query.get()
.map { it ->
val stateRef = StateRef(SecureHash.parse(it.txId), it.index)
val state = it.contractState.deserialize<TransactionState<T>>(storageKryo())
val state = it.contractState.deserialize<TransactionState<T>>(context = STORAGE_CONTEXT)
StateAndRef(state, stateRef)
}.toList()
}
@ -480,7 +480,7 @@ class NodeVaultService(private val services: ServiceHub, dataSourceProperties: P
result.get().forEach {
val txHash = SecureHash.parse(it.txId)
val index = it.index
val state = it.contractState.deserialize<TransactionState<ContractState>>(storageKryo())
val state = it.contractState.deserialize<TransactionState<ContractState>>(context = STORAGE_CONTEXT)
consumedStates.add(StateAndRef(state, StateRef(txHash, index)))
}
}

View File

@ -1,9 +1,9 @@
package net.corda.node.utilities
import net.corda.core.serialization.SerializationDefaults.STORAGE_CONTEXT
import net.corda.core.serialization.SerializedBytes
import net.corda.core.serialization.deserialize
import net.corda.core.serialization.serialize
import net.corda.core.serialization.storageKryo
import net.corda.core.utilities.loggerFor
import net.corda.core.utilities.trace
import org.jetbrains.exposed.sql.*
@ -65,17 +65,18 @@ fun bytesToBlob(value: SerializedBytes<*>, finalizables: MutableList<() -> Unit>
return blob
}
fun serializeToBlob(value: Any, finalizables: MutableList<() -> Unit>): Blob = bytesToBlob(value.serialize(storageKryo(), true), finalizables)
fun serializeToBlob(value: Any, finalizables: MutableList<() -> Unit>): Blob = bytesToBlob(value.serialize(context = STORAGE_CONTEXT), finalizables)
fun <T : Any> bytesFromBlob(blob: Blob): SerializedBytes<T> {
try {
return SerializedBytes(blob.getBytes(0, blob.length().toInt()), true)
return SerializedBytes(blob.getBytes(0, blob.length().toInt()))
} finally {
blob.free()
}
}
fun <T : Any> deserializeFromBlob(blob: Blob): T = bytesFromBlob<T>(blob).deserialize()
@Suppress("UNCHECKED_CAST")
fun <T : Any> deserializeFromBlob(blob: Blob): T = bytesFromBlob<Any>(blob).deserialize(context = STORAGE_CONTEXT) as T
/**
* A convenient JDBC table backed hash set with iteration order based on insertion order.

View File

@ -37,7 +37,6 @@ object ServiceIdentityGenerator {
keyPairs.zip(dirs) { keyPair, dir ->
Files.createDirectories(dir)
Files.write(dir.resolve(compositeKeyFile), notaryKey.encoded)
// Use storageKryo as our whitelist is not available in the gradle build environment:
Files.write(dir.resolve(privateKeyFile), keyPair.private.encoded)
Files.write(dir.resolve(publicKeyFile), keyPair.public.encoded)
}

View File

@ -1,47 +1,60 @@
package net.corda.node.services.vault;
import com.google.common.collect.*;
import kotlin.*;
import net.corda.contracts.*;
import net.corda.contracts.asset.*;
import com.google.common.collect.ImmutableSet;
import net.corda.contracts.DealState;
import net.corda.contracts.asset.Cash;
import net.corda.core.contracts.*;
import net.corda.core.crypto.*;
import net.corda.core.identity.*;
import net.corda.core.messaging.*;
import net.corda.core.node.services.*;
import net.corda.core.crypto.EncodingUtils;
import net.corda.core.crypto.SecureHash;
import net.corda.core.identity.AbstractParty;
import net.corda.core.messaging.DataFeed;
import net.corda.core.node.services.Vault;
import net.corda.core.node.services.VaultQueryException;
import net.corda.core.node.services.VaultQueryService;
import net.corda.core.node.services.VaultService;
import net.corda.core.node.services.vault.*;
import net.corda.core.node.services.vault.QueryCriteria.*;
import net.corda.testing.contracts.DummyLinearContract;
import net.corda.core.schemas.*;
import net.corda.core.transactions.*;
import net.corda.core.utilities.*;
import net.corda.core.node.services.vault.QueryCriteria.LinearStateQueryCriteria;
import net.corda.core.node.services.vault.QueryCriteria.VaultCustomQueryCriteria;
import net.corda.core.node.services.vault.QueryCriteria.VaultQueryCriteria;
import net.corda.core.schemas.MappedSchema;
import net.corda.core.transactions.SignedTransaction;
import net.corda.core.transactions.WireTransaction;
import net.corda.core.utilities.OpaqueBytes;
import net.corda.node.services.database.HibernateConfiguration;
import net.corda.node.services.schema.NodeSchemaService;
import net.corda.node.utilities.CordaPersistence;
import net.corda.node.services.database.*;
import net.corda.node.services.schema.*;
import net.corda.schemas.*;
import net.corda.testing.*;
import net.corda.testing.contracts.*;
import net.corda.testing.node.*;
import net.corda.schemas.CashSchemaV1;
import net.corda.testing.TestConstants;
import net.corda.testing.TestDependencyInjectionBase;
import net.corda.testing.contracts.DummyLinearContract;
import net.corda.testing.contracts.VaultFiller;
import net.corda.testing.node.MockServices;
import net.corda.testing.schemas.DummyLinearStateSchemaV1;
import org.jetbrains.annotations.*;
import org.junit.*;
import org.jetbrains.annotations.NotNull;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import rx.Observable;
import java.io.*;
import java.lang.reflect.*;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.*;
import java.util.stream.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
import static net.corda.contracts.asset.CashKt.*;
import static net.corda.core.contracts.ContractsDSL.*;
import static net.corda.core.node.services.vault.QueryCriteriaUtils.*;
import static net.corda.contracts.asset.CashKt.getDUMMY_CASH_ISSUER;
import static net.corda.contracts.asset.CashKt.getDUMMY_CASH_ISSUER_KEY;
import static net.corda.core.contracts.ContractsDSL.USD;
import static net.corda.core.node.services.vault.QueryCriteriaUtils.DEFAULT_PAGE_NUM;
import static net.corda.core.node.services.vault.QueryCriteriaUtils.MAX_PAGE_SIZE;
import static net.corda.core.utilities.ByteArrays.toHexString;
import static net.corda.node.utilities.CordaPersistenceKt.configureDatabase;
import static net.corda.testing.CoreTestUtils.*;
import static net.corda.testing.node.MockServicesKt.*;
import static net.corda.core.utilities.ByteArrays.toHexString;
import static org.assertj.core.api.Assertions.*;
import static net.corda.testing.node.MockServicesKt.makeTestDataSourceProperties;
import static org.assertj.core.api.Assertions.assertThat;
public class VaultQueryJavaTests {
public class VaultQueryJavaTests extends TestDependencyInjectionBase {
private MockServices services;
private VaultService vaultSvc;

View File

@ -8,6 +8,8 @@ import net.corda.node.services.messaging.createMessage
import net.corda.node.services.network.NetworkMapService
import net.corda.testing.node.MockNetwork
import org.junit.After
import net.corda.testing.resetTestSerialization
import org.junit.Before
import org.junit.Test
import java.util.*
import kotlin.test.assertEquals
@ -15,11 +17,20 @@ import kotlin.test.assertFails
import kotlin.test.assertTrue
class InMemoryMessagingTests {
val mockNet = MockNetwork()
lateinit var mockNet: MockNetwork
@Before
fun setUp() {
mockNet = MockNetwork()
}
@After
fun cleanUp() {
mockNet.stopNodes()
fun tearDown() {
if (mockNet.nodes.isNotEmpty()) {
mockNet.stopNodes()
} else {
resetTestSerialization()
}
}
@Test

View File

@ -76,7 +76,6 @@ class TwoPartyTradeFlowTests {
@Before
fun before() {
mockNet = MockNetwork(false)
LogHelper.setLevel("platform.trade", "core.contract.TransactionGroup", "recordingmap")
}
@ -93,7 +92,7 @@ class TwoPartyTradeFlowTests {
// allow interruption half way through.
mockNet = MockNetwork(false, true)
ledger {
ledger(initialiseSerialization = false) {
val basketOfNodes = mockNet.createSomeNodes(3)
val notaryNode = basketOfNodes.notaryNode
val aliceNode = basketOfNodes.partyNodes[0]
@ -140,7 +139,7 @@ class TwoPartyTradeFlowTests {
fun `trade cash for commercial paper fails using soft locking`() {
mockNet = MockNetwork(false, true)
ledger {
ledger(initialiseSerialization = false) {
val notaryNode = mockNet.createNotaryNode(null, DUMMY_NOTARY.name)
val aliceNode = mockNet.createPartyNode(notaryNode.network.myAddress, ALICE.name)
val bobNode = mockNet.createPartyNode(notaryNode.network.myAddress, BOB.name)
@ -191,7 +190,8 @@ class TwoPartyTradeFlowTests {
@Test
fun `shutdown and restore`() {
ledger {
mockNet = MockNetwork(false)
ledger(initialiseSerialization = false) {
val notaryNode = mockNet.createNotaryNode(null, DUMMY_NOTARY.name)
val aliceNode = mockNet.createPartyNode(notaryNode.network.myAddress, ALICE.name)
var bobNode = mockNet.createPartyNode(notaryNode.network.myAddress, BOB.name)
@ -313,13 +313,15 @@ class TwoPartyTradeFlowTests {
@Test
fun `check dependencies of sale asset are resolved`() {
mockNet = MockNetwork(false)
val notaryNode = mockNet.createNotaryNode(null, DUMMY_NOTARY.name)
val aliceNode = makeNodeWithTracking(notaryNode.network.myAddress, ALICE.name)
val bobNode = makeNodeWithTracking(notaryNode.network.myAddress, BOB.name)
val bankNode = makeNodeWithTracking(notaryNode.network.myAddress, BOC.name)
val issuer = bankNode.info.legalIdentity.ref(1, 2, 3)
ledger(aliceNode.services) {
ledger(aliceNode.services, initialiseSerialization = false) {
// Insert a prospectus type attachment into the commercial paper transaction.
val stream = ByteArrayOutputStream()
@ -412,13 +414,15 @@ class TwoPartyTradeFlowTests {
@Test
fun `track works`() {
mockNet = MockNetwork(false)
val notaryNode = mockNet.createNotaryNode(null, DUMMY_NOTARY.name)
val aliceNode = makeNodeWithTracking(notaryNode.network.myAddress, ALICE.name)
val bobNode = makeNodeWithTracking(notaryNode.network.myAddress, BOB.name)
val bankNode = makeNodeWithTracking(notaryNode.network.myAddress, BOC.name)
val issuer = bankNode.info.legalIdentity.ref(1, 2, 3)
ledger(aliceNode.services) {
ledger(aliceNode.services, initialiseSerialization = false) {
// Insert a prospectus type attachment into the commercial paper transaction.
val stream = ByteArrayOutputStream()
@ -487,14 +491,16 @@ class TwoPartyTradeFlowTests {
@Test
fun `dependency with error on buyer side`() {
ledger {
mockNet = MockNetwork(false)
ledger(initialiseSerialization = false) {
runWithError(true, false, "at least one asset input")
}
}
@Test
fun `dependency with error on seller side`() {
ledger {
mockNet = MockNetwork(false)
ledger(initialiseSerialization = false) {
runWithError(false, true, "Issuances must have a time-window")
}
}

View File

@ -34,7 +34,7 @@ class NotaryChangeTests {
lateinit var clientNodeB: MockNetwork.MockNode
@Before
fun setup() {
fun setUp() {
mockNet = MockNetwork()
oldNotaryNode = mockNet.createNode(
legalName = DUMMY_NOTARY.name,

View File

@ -3,39 +3,32 @@ package net.corda.node.services.database
import net.corda.contracts.asset.Cash
import net.corda.contracts.asset.DUMMY_CASH_ISSUER
import net.corda.contracts.asset.DummyFungibleContract
import net.corda.testing.contracts.consumeCash
import net.corda.testing.contracts.fillWithSomeTestCash
import net.corda.testing.contracts.fillWithSomeTestDeals
import net.corda.testing.contracts.fillWithSomeTestLinearStates
import net.corda.core.contracts.*
import net.corda.core.crypto.toBase58String
import net.corda.core.node.services.Vault
import net.corda.core.node.services.VaultService
import net.corda.core.schemas.CommonSchemaV1
import net.corda.core.schemas.PersistentStateRef
import net.corda.testing.schemas.DummyLinearStateSchemaV1
import net.corda.testing.schemas.DummyLinearStateSchemaV2
import net.corda.core.serialization.storageKryo
import net.corda.core.serialization.deserialize
import net.corda.core.transactions.SignedTransaction
import net.corda.testing.ALICE
import net.corda.testing.BOB
import net.corda.testing.BOB_KEY
import net.corda.testing.DUMMY_NOTARY
import net.corda.node.services.schema.HibernateObserver
import net.corda.node.services.schema.NodeSchemaService
import net.corda.node.services.vault.NodeVaultService
import net.corda.core.schemas.CommonSchemaV1
import net.corda.core.serialization.deserialize
import net.corda.node.services.vault.VaultSchemaV1
import net.corda.node.utilities.CordaPersistence
import net.corda.node.utilities.configureDatabase
import net.corda.schemas.CashSchemaV1
import net.corda.schemas.SampleCashSchemaV2
import net.corda.schemas.SampleCashSchemaV3
import net.corda.testing.BOB_PUBKEY
import net.corda.testing.BOC
import net.corda.testing.BOC_KEY
import net.corda.testing.*
import net.corda.testing.contracts.consumeCash
import net.corda.testing.contracts.fillWithSomeTestCash
import net.corda.testing.contracts.fillWithSomeTestDeals
import net.corda.testing.contracts.fillWithSomeTestLinearStates
import net.corda.testing.node.MockServices
import net.corda.testing.node.makeTestDataSourceProperties
import net.corda.testing.schemas.DummyLinearStateSchemaV1
import net.corda.testing.schemas.DummyLinearStateSchemaV2
import org.assertj.core.api.Assertions
import org.assertj.core.api.Assertions.assertThat
import org.hibernate.SessionFactory
@ -48,7 +41,7 @@ import javax.persistence.EntityManager
import javax.persistence.Tuple
import javax.persistence.criteria.CriteriaBuilder
class HibernateConfigurationTest {
class HibernateConfigurationTest : TestDependencyInjectionBase() {
lateinit var services: MockServices
lateinit var database: CordaPersistence
@ -655,7 +648,7 @@ class HibernateConfigurationTest {
val queryResults = entityManager.createQuery(criteriaQuery).resultList
queryResults.forEach {
val contractState = it.contractState.deserialize<TransactionState<ContractState>>(storageKryo())
val contractState = it.contractState.deserialize<TransactionState<ContractState>>()
val cashState = contractState.data as Cash.State
println("${it.stateRef} with owner: ${cashState.owner.owningKey.toBase58String()}") }
@ -739,7 +732,7 @@ class HibernateConfigurationTest {
// execute query
val queryResults = entityManager.createQuery(criteriaQuery).resultList
queryResults.forEach {
val contractState = it.contractState.deserialize<TransactionState<ContractState>>(storageKryo())
val contractState = it.contractState.deserialize<TransactionState<ContractState>>()
val cashState = contractState.data as Cash.State
println("${it.stateRef} with owner ${cashState.owner.owningKey.toBase58String()} and participants ${cashState.participants.map { it.owningKey.toBase58String() }}")
}

View File

@ -5,7 +5,6 @@ import io.requery.kotlin.eq
import io.requery.sql.KotlinEntityDataStore
import net.corda.core.contracts.StateRef
import net.corda.core.contracts.TransactionType
import net.corda.testing.contracts.DummyContract
import net.corda.core.crypto.DigitalSignature
import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.testing.NullPublicKey
@ -13,11 +12,8 @@ import net.corda.core.crypto.toBase58String
import net.corda.core.identity.AnonymousParty
import net.corda.core.node.services.Vault
import net.corda.core.serialization.serialize
import net.corda.core.serialization.storageKryo
import net.corda.core.transactions.SignedTransaction
import net.corda.core.transactions.WireTransaction
import net.corda.testing.DUMMY_NOTARY
import net.corda.testing.DUMMY_PUBKEY_1
import net.corda.node.services.persistence.DBTransactionStorage
import net.corda.node.services.vault.schemas.requery.Models
import net.corda.node.services.vault.schemas.requery.VaultCashBalancesEntity
@ -25,6 +21,10 @@ import net.corda.node.services.vault.schemas.requery.VaultSchema
import net.corda.node.services.vault.schemas.requery.VaultStatesEntity
import net.corda.node.utilities.CordaPersistence
import net.corda.node.utilities.configureDatabase
import net.corda.testing.DUMMY_NOTARY
import net.corda.testing.DUMMY_PUBKEY_1
import net.corda.testing.TestDependencyInjectionBase
import net.corda.testing.contracts.DummyContract
import net.corda.testing.node.makeTestDataSourceProperties
import org.assertj.core.api.Assertions
import org.junit.After
@ -35,7 +35,7 @@ import org.junit.Test
import java.time.Instant
import java.util.*
class RequeryConfigurationTest {
class RequeryConfigurationTest : TestDependencyInjectionBase() {
lateinit var database: CordaPersistence
lateinit var transactionStorage: DBTransactionStorage
@ -175,7 +175,7 @@ class RequeryConfigurationTest {
index = txnState.index
stateStatus = Vault.StateStatus.UNCONSUMED
contractStateClassName = DummyContract.SingleOwnerState::class.java.name
contractState = DummyContract.SingleOwnerState(owner = AnonymousParty(DUMMY_PUBKEY_1)).serialize(storageKryo()).bytes
contractState = DummyContract.SingleOwnerState(owner = AnonymousParty(DUMMY_PUBKEY_1)).serialize().bytes
notaryName = txn.tx.notary!!.name.toString()
notaryKey = txn.tx.notary!!.owningKey.toBase58String()
recordedTime = Instant.now()

View File

@ -25,6 +25,8 @@ import net.corda.testing.node.MockKeyManagementService
import net.corda.testing.node.TestClock
import net.corda.testing.node.makeTestDataSourceProperties
import net.corda.testing.testNodeConfiguration
import net.corda.testing.initialiseTestSerialization
import net.corda.testing.resetTestSerialization
import org.assertj.core.api.Assertions.assertThat
import org.bouncycastle.asn1.x500.X500Name
import org.junit.After
@ -67,6 +69,7 @@ class NodeSchedulerServiceTest : SingletonSerializeAsToken() {
@Before
fun setup() {
initialiseTestSerialization()
countDown = CountDownLatch(1)
smmHasRemovedAllFlows = CountDownLatch(1)
calls = 0
@ -114,6 +117,7 @@ class NodeSchedulerServiceTest : SingletonSerializeAsToken() {
smmExecutor.shutdown()
smmExecutor.awaitTermination(60, TimeUnit.SECONDS)
database.close()
resetTestSerialization()
}
class TestState(val flowLogicRef: FlowLogicRef, val instant: Instant) : LinearState, SchedulableState {

View File

@ -37,7 +37,7 @@ import kotlin.test.assertEquals
import kotlin.test.assertNull
//TODO This needs to be merged into P2PMessagingTest as that creates a more realistic environment
class ArtemisMessagingTests {
class ArtemisMessagingTests : TestDependencyInjectionBase() {
@Rule @JvmField val temporaryFolder = TemporaryFolder()
val serverPort = freePort()

View File

@ -80,16 +80,18 @@ class InMemoryIdentityServiceTests {
*/
@Test
fun `assert unknown anonymous key is unrecognised`() {
val rootKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
val rootCert = X509Utilities.createSelfSignedCACertificate(ALICE.name, rootKey)
val txKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
val service = InMemoryIdentityService(trustRoot = DUMMY_CA.certificate)
// TODO: Generate certificate with an EdDSA key rather than ECDSA
val identity = Party(CertificateAndKeyPair(rootCert, rootKey))
val txIdentity = AnonymousParty(txKey.public)
withTestSerialization {
val rootKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
val rootCert = X509Utilities.createSelfSignedCACertificate(ALICE.name, rootKey)
val txKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
val service = InMemoryIdentityService(trustRoot = DUMMY_CA.certificate)
// TODO: Generate certificate with an EdDSA key rather than ECDSA
val identity = Party(CertificateAndKeyPair(rootCert, rootKey))
val txIdentity = AnonymousParty(txKey.public)
assertFailsWith<IdentityService.UnknownAnonymousPartyException> {
service.assertOwnership(identity, txIdentity)
assertFailsWith<IdentityService.UnknownAnonymousPartyException> {
service.assertOwnership(identity, txIdentity)
}
}
}
@ -122,38 +124,40 @@ class InMemoryIdentityServiceTests {
*/
@Test
fun `assert ownership`() {
val trustRoot = DUMMY_CA
val (alice, aliceTxIdentity) = createParty(ALICE.name, trustRoot)
withTestSerialization {
val trustRoot = DUMMY_CA
val (alice, aliceTxIdentity) = createParty(ALICE.name, trustRoot)
val certFactory = CertificateFactory.getInstance("X509")
val bobRootKey = Crypto.generateKeyPair()
val bobRoot = getTestPartyAndCertificate(BOB.name, bobRootKey.public)
val bobRootCert = bobRoot.certificate
val bobTxKey = Crypto.generateKeyPair()
val bobTxCert = X509Utilities.createCertificate(CertificateType.IDENTITY, bobRootCert, bobRootKey, BOB.name, bobTxKey.public)
val bobCertPath = certFactory.generateCertPath(listOf(bobTxCert.cert, bobRootCert.cert))
val bob = PartyAndCertificate(BOB.name, bobRootKey.public, bobRootCert, bobCertPath)
val certFactory = CertificateFactory.getInstance("X509")
val bobRootKey = Crypto.generateKeyPair()
val bobRoot = getTestPartyAndCertificate(BOB.name, bobRootKey.public)
val bobRootCert = bobRoot.certificate
val bobTxKey = Crypto.generateKeyPair()
val bobTxCert = X509Utilities.createCertificate(CertificateType.IDENTITY, bobRootCert, bobRootKey, BOB.name, bobTxKey.public)
val bobCertPath = certFactory.generateCertPath(listOf(bobTxCert.cert, bobRootCert.cert))
val bob = PartyAndCertificate(BOB.name, bobRootKey.public, bobRootCert, bobCertPath)
// Now we have identities, construct the service and let it know about both
val service = InMemoryIdentityService(setOf(alice, bob), emptyMap(), trustRoot.certificate.cert)
service.verifyAndRegisterAnonymousIdentity(aliceTxIdentity, alice.party)
// Now we have identities, construct the service and let it know about both
val service = InMemoryIdentityService(setOf(alice, bob), emptyMap(), trustRoot.certificate.cert)
service.verifyAndRegisterAnonymousIdentity(aliceTxIdentity, alice.party)
val anonymousBob = AnonymousPartyAndPath(AnonymousParty(bobTxKey.public),bobCertPath)
service.verifyAndRegisterAnonymousIdentity(anonymousBob, bob.party)
val anonymousBob = AnonymousPartyAndPath(AnonymousParty(bobTxKey.public),bobCertPath)
service.verifyAndRegisterAnonymousIdentity(anonymousBob, bob.party)
// Verify that paths are verified
service.assertOwnership(alice.party, aliceTxIdentity.party)
service.assertOwnership(bob.party, anonymousBob.party)
assertFailsWith<IllegalArgumentException> {
service.assertOwnership(alice.party, anonymousBob.party)
}
assertFailsWith<IllegalArgumentException> {
service.assertOwnership(bob.party, aliceTxIdentity.party)
}
// Verify that paths are verified
service.assertOwnership(alice.party, aliceTxIdentity.party)
service.assertOwnership(bob.party, anonymousBob.party)
assertFailsWith<IllegalArgumentException> {
service.assertOwnership(alice.party, anonymousBob.party)
}
assertFailsWith<IllegalArgumentException> {
service.assertOwnership(bob.party, aliceTxIdentity.party)
}
assertFailsWith<IllegalArgumentException> {
val owningKey = Crypto.decodePublicKey(trustRoot.certificate.subjectPublicKeyInfo.encoded)
service.assertOwnership(Party(trustRoot.certificate.subject, owningKey), aliceTxIdentity.party)
assertFailsWith<IllegalArgumentException> {
val owningKey = Crypto.decodePublicKey(trustRoot.certificate.subjectPublicKeyInfo.encoded)
service.assertOwnership(Party(trustRoot.certificate.subject, owningKey), aliceTxIdentity.party)
}
}
}

View File

@ -7,12 +7,18 @@ import net.corda.testing.ALICE
import net.corda.testing.BOB
import net.corda.testing.node.MockNetwork
import org.junit.After
import org.junit.Before
import org.junit.Test
import java.math.BigInteger
import kotlin.test.assertEquals
class InMemoryNetworkMapCacheTest {
private val mockNet = MockNetwork()
lateinit var mockNet: MockNetwork
@Before
fun setUp() {
mockNet = MockNetwork()
}
@After
fun teardown() {

View File

@ -2,12 +2,13 @@ package net.corda.node.services.persistence
import com.google.common.primitives.Ints
import net.corda.core.serialization.SerializedBytes
import net.corda.testing.LogHelper
import net.corda.node.services.api.Checkpoint
import net.corda.node.services.api.CheckpointStorage
import net.corda.node.services.transactions.PersistentUniquenessProvider
import net.corda.node.utilities.CordaPersistence
import net.corda.node.utilities.configureDatabase
import net.corda.testing.LogHelper
import net.corda.testing.TestDependencyInjectionBase
import net.corda.testing.node.makeTestDataSourceProperties
import org.assertj.core.api.Assertions.assertThat
import org.assertj.core.api.Assertions.assertThatExceptionOfType
@ -24,7 +25,7 @@ internal fun CheckpointStorage.checkpoints(): List<Checkpoint> {
return checkpoints
}
class DBCheckpointStorageTests {
class DBCheckpointStorageTests : TestDependencyInjectionBase() {
lateinit var checkpointStorage: DBCheckpointStorage
lateinit var database: CordaPersistence

View File

@ -8,11 +8,12 @@ import net.corda.core.crypto.testing.NullPublicKey
import net.corda.core.toFuture
import net.corda.core.transactions.SignedTransaction
import net.corda.core.transactions.WireTransaction
import net.corda.testing.DUMMY_NOTARY
import net.corda.testing.LogHelper
import net.corda.node.services.transactions.PersistentUniquenessProvider
import net.corda.node.utilities.CordaPersistence
import net.corda.node.utilities.configureDatabase
import net.corda.testing.DUMMY_NOTARY
import net.corda.testing.LogHelper
import net.corda.testing.TestDependencyInjectionBase
import net.corda.testing.node.makeTestDataSourceProperties
import org.assertj.core.api.Assertions.assertThat
import org.junit.After
@ -21,7 +22,7 @@ import org.junit.Test
import java.util.concurrent.TimeUnit
import kotlin.test.assertEquals
class DBTransactionStorageTests {
class DBTransactionStorageTests : TestDependencyInjectionBase() {
lateinit var database: CordaPersistence
lateinit var transactionStorage: DBTransactionStorage

View File

@ -8,10 +8,11 @@ import io.atomix.copycat.server.storage.Storage
import io.atomix.copycat.server.storage.StorageLevel
import net.corda.core.getOrThrow
import net.corda.core.utilities.NetworkHostAndPort
import net.corda.testing.LogHelper
import net.corda.node.services.network.NetworkMapService
import net.corda.node.utilities.CordaPersistence
import net.corda.node.utilities.configureDatabase
import net.corda.testing.LogHelper
import net.corda.testing.TestDependencyInjectionBase
import net.corda.testing.freeLocalHostAndPort
import net.corda.testing.node.makeTestDataSourceProperties
import org.jetbrains.exposed.sql.Transaction
@ -22,7 +23,7 @@ import java.util.concurrent.CompletableFuture
import kotlin.test.assertEquals
import kotlin.test.assertTrue
class DistributedImmutableMapTests {
class DistributedImmutableMapTests : TestDependencyInjectionBase() {
data class Member(val client: CopycatClient, val server: CopycatServer)
lateinit var cluster: List<Member>

View File

@ -6,6 +6,7 @@ import net.corda.node.utilities.CordaPersistence
import net.corda.node.utilities.configureDatabase
import net.corda.testing.LogHelper
import net.corda.testing.MEGA_CORP
import net.corda.testing.TestDependencyInjectionBase
import net.corda.testing.generateStateRef
import net.corda.testing.node.makeTestDataSourceProperties
import org.junit.After
@ -14,7 +15,7 @@ import org.junit.Test
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith
class PersistentUniquenessProviderTests {
class PersistentUniquenessProviderTests : TestDependencyInjectionBase() {
val identity = MEGA_CORP
val txID = SecureHash.randomSHA256()

View File

@ -34,7 +34,7 @@ import kotlin.test.assertFalse
import kotlin.test.assertNull
import kotlin.test.assertTrue
class NodeVaultServiceTest {
class NodeVaultServiceTest : TestDependencyInjectionBase() {
lateinit var services: MockServices
val vaultSvc: VaultService get() = services.vaultService
lateinit var database: CordaPersistence

View File

@ -15,8 +15,8 @@ import net.corda.core.node.services.vault.*
import net.corda.core.node.services.vault.QueryCriteria.*
import net.corda.core.utilities.seconds
import net.corda.core.transactions.SignedTransaction
import net.corda.core.utilities.OpaqueBytes
import net.corda.core.utilities.NonEmptySet
import net.corda.core.utilities.OpaqueBytes
import net.corda.core.utilities.toHexString
import net.corda.node.services.database.HibernateConfiguration
import net.corda.node.services.schema.NodeSchemaService
@ -46,7 +46,7 @@ import java.time.ZoneOffset
import java.time.temporal.ChronoUnit
import java.util.*
class VaultQueryTests {
class VaultQueryTests : TestDependencyInjectionBase() {
lateinit var services: MockServices
val vaultSvc: VaultService get() = services.vaultService

View File

@ -1,13 +1,8 @@
package net.corda.node.services.vault
import net.corda.testing.contracts.DummyDealContract
import net.corda.contracts.asset.Cash
import net.corda.contracts.asset.DUMMY_CASH_ISSUER
import net.corda.testing.contracts.fillWithSomeTestCash
import net.corda.testing.contracts.fillWithSomeTestDeals
import net.corda.testing.contracts.fillWithSomeTestLinearStates
import net.corda.core.contracts.*
import net.corda.testing.contracts.DummyLinearContract
import net.corda.core.identity.AnonymousParty
import net.corda.core.node.services.VaultService
import net.corda.core.node.services.consumedStates
@ -15,12 +10,8 @@ import net.corda.core.node.services.unconsumedStates
import net.corda.core.transactions.SignedTransaction
import net.corda.node.utilities.CordaPersistence
import net.corda.node.utilities.configureDatabase
import net.corda.testing.BOB
import net.corda.testing.DUMMY_NOTARY
import net.corda.testing.DUMMY_NOTARY_KEY
import net.corda.testing.LogHelper
import net.corda.testing.MEGA_CORP
import net.corda.testing.MEGA_CORP_KEY
import net.corda.testing.*
import net.corda.testing.contracts.*
import net.corda.testing.node.MockServices
import net.corda.testing.node.makeTestDataSourceProperties
import org.assertj.core.api.Assertions.assertThat
@ -36,7 +27,7 @@ import kotlin.test.assertNull
// TODO: Move this to the cash contract tests once mock services are further split up.
class VaultWithCashTest {
class VaultWithCashTest : TestDependencyInjectionBase() {
lateinit var services: MockServices
val vault: VaultService get() = services.vaultService
lateinit var database: CordaPersistence

View File

@ -79,7 +79,7 @@ class IRSDemoTest : IntegrationTestCategory {
fun getFloatingLegFixCount(nodeApi: HttpApi) = getTrades(nodeApi)[0].calculation.floatingLegPaymentSchedule.count { it.value.rate.ratioUnit != null }
fun getFixingDateObservable(config: FullNodeConfiguration): Observable<LocalDate?> {
val client = CordaRPCClient(config.rpcAddress!!)
val client = CordaRPCClient(config.rpcAddress!!, initialiseSerialization = false)
val proxy = client.start("user", "password").proxy
val vaultUpdates = proxy.vaultAndUpdates().second

View File

@ -14,7 +14,6 @@ import net.corda.core.getOrThrow
import net.corda.core.identity.Party
import net.corda.core.node.services.ServiceInfo
import net.corda.core.transactions.TransactionBuilder
import net.corda.testing.LogHelper
import net.corda.core.utilities.ProgressTracker
import net.corda.irs.flows.RatesFixFlow
import net.corda.node.utilities.CordaPersistence
@ -34,7 +33,7 @@ import kotlin.test.assertEquals
import kotlin.test.assertFailsWith
import kotlin.test.assertFalse
class NodeInterestRatesTest {
class NodeInterestRatesTest : TestDependencyInjectionBase() {
val TEST_DATA = NodeInterestRates.parseFile("""
LIBOR 2016-03-16 1M = 0.678
LIBOR 2016-03-16 2M = 0.685
@ -202,7 +201,7 @@ class NodeInterestRatesTest {
@Test
fun `network tearoff`() {
val mockNet = MockNetwork()
val mockNet = MockNetwork(initialiseSerialization = false)
val n1 = mockNet.createNotaryNode()
val n2 = mockNet.createNode(n1.network.myAddress, advertisedServices = ServiceInfo(NodeInterestRates.Oracle.type))
n2.registerInitiatedFlow(NodeInterestRates.FixQueryHandler::class.java)

View File

@ -4,9 +4,6 @@ import net.corda.contracts.*
import net.corda.core.contracts.*
import net.corda.core.utilities.seconds
import net.corda.core.transactions.SignedTransaction
import net.corda.testing.DUMMY_NOTARY
import net.corda.testing.DUMMY_NOTARY_KEY
import net.corda.testing.TEST_TX_TIME
import net.corda.testing.*
import net.corda.testing.node.MockServices
import org.junit.Test
@ -200,7 +197,7 @@ fun createDummyIRS(irsSelect: Int): InterestRateSwap.State {
}
}
class IRSTests {
class IRSTests : TestDependencyInjectionBase() {
val megaCorpServices = MockServices(MEGA_CORP_KEY)
val miniCorpServices = MockServices(MINI_CORP_KEY)
val notaryServices = MockServices(DUMMY_NOTARY_KEY)
@ -370,7 +367,7 @@ class IRSTests {
val ld = LocalDate.of(2016, 3, 8)
val bd = BigDecimal("0.0063518")
return ledger {
return ledger(initialiseSerialization = false) {
transaction("Agreement") {
output("irs post agreement") { singleIRS() }
command(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() }
@ -401,7 +398,7 @@ class IRSTests {
@Test
fun `ensure failure occurs when there are inbound states for an agreement command`() {
val irs = singleIRS()
transaction {
transaction(initialiseSerialization = false) {
input { irs }
output("irs post agreement") { irs }
command(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() }
@ -414,7 +411,7 @@ class IRSTests {
fun `ensure failure occurs when no events in fix schedule`() {
val irs = singleIRS()
val emptySchedule = mutableMapOf<LocalDate, FixedRatePaymentEvent>()
transaction {
transaction(initialiseSerialization = false) {
output {
irs.copy(calculation = irs.calculation.copy(fixedLegPaymentSchedule = emptySchedule))
}
@ -428,7 +425,7 @@ class IRSTests {
fun `ensure failure occurs when no events in floating schedule`() {
val irs = singleIRS()
val emptySchedule = mutableMapOf<LocalDate, FloatingRatePaymentEvent>()
transaction {
transaction(initialiseSerialization = false) {
output {
irs.copy(calculation = irs.calculation.copy(floatingLegPaymentSchedule = emptySchedule))
}
@ -441,7 +438,7 @@ class IRSTests {
@Test
fun `ensure notionals are non zero`() {
val irs = singleIRS()
transaction {
transaction(initialiseSerialization = false) {
output {
irs.copy(irs.fixedLeg.copy(notional = irs.fixedLeg.notional.copy(quantity = 0)))
}
@ -450,7 +447,7 @@ class IRSTests {
this `fails with` "All notionals must be non zero"
}
transaction {
transaction(initialiseSerialization = false) {
output {
irs.copy(irs.fixedLeg.copy(notional = irs.floatingLeg.notional.copy(quantity = 0)))
}
@ -464,7 +461,7 @@ class IRSTests {
fun `ensure positive rate on fixed leg`() {
val irs = singleIRS()
val modifiedIRS = irs.copy(fixedLeg = irs.fixedLeg.copy(fixedRate = FixedRate(PercentageRatioUnit("-0.1"))))
transaction {
transaction(initialiseSerialization = false) {
output {
modifiedIRS
}
@ -481,7 +478,7 @@ class IRSTests {
fun `ensure same currency notionals`() {
val irs = singleIRS()
val modifiedIRS = irs.copy(fixedLeg = irs.fixedLeg.copy(notional = Amount(irs.fixedLeg.notional.quantity, Currency.getInstance("JPY"))))
transaction {
transaction(initialiseSerialization = false) {
output {
modifiedIRS
}
@ -495,7 +492,7 @@ class IRSTests {
fun `ensure notional amounts are equal`() {
val irs = singleIRS()
val modifiedIRS = irs.copy(fixedLeg = irs.fixedLeg.copy(notional = Amount(irs.floatingLeg.notional.quantity + 1, irs.floatingLeg.notional.token)))
transaction {
transaction(initialiseSerialization = false) {
output {
modifiedIRS
}
@ -509,7 +506,7 @@ class IRSTests {
fun `ensure trade date and termination date checks are done pt1`() {
val irs = singleIRS()
val modifiedIRS1 = irs.copy(fixedLeg = irs.fixedLeg.copy(terminationDate = irs.fixedLeg.effectiveDate.minusDays(1)))
transaction {
transaction(initialiseSerialization = false) {
output {
modifiedIRS1
}
@ -519,7 +516,7 @@ class IRSTests {
}
val modifiedIRS2 = irs.copy(floatingLeg = irs.floatingLeg.copy(terminationDate = irs.floatingLeg.effectiveDate.minusDays(1)))
transaction {
transaction(initialiseSerialization = false) {
output {
modifiedIRS2
}
@ -534,7 +531,7 @@ class IRSTests {
val irs = singleIRS()
val modifiedIRS3 = irs.copy(floatingLeg = irs.floatingLeg.copy(terminationDate = irs.fixedLeg.terminationDate.minusDays(1)))
transaction {
transaction(initialiseSerialization = false) {
output {
modifiedIRS3
}
@ -545,7 +542,7 @@ class IRSTests {
val modifiedIRS4 = irs.copy(floatingLeg = irs.floatingLeg.copy(effectiveDate = irs.fixedLeg.effectiveDate.minusDays(1)))
transaction {
transaction(initialiseSerialization = false) {
output {
modifiedIRS4
}
@ -561,7 +558,7 @@ class IRSTests {
val ld = LocalDate.of(2016, 3, 8)
val bd = BigDecimal("0.0063518")
transaction {
transaction(initialiseSerialization = false) {
output("irs post agreement") { singleIRS() }
command(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() }
timeWindow(TEST_TX_TIME)
@ -574,7 +571,7 @@ class IRSTests {
oldIRS.calculation.applyFixing(ld, FixedRate(RatioUnit(bd))),
oldIRS.common)
transaction {
transaction(initialiseSerialization = false) {
input {
oldIRS
@ -654,7 +651,7 @@ class IRSTests {
val irs = singleIRS()
return ledger {
return ledger(initialiseSerialization = false) {
transaction("Agreement") {
output("irs post agreement1") {
irs.copy(

View File

@ -10,11 +10,11 @@ import net.corda.testing.DUMMY_BANK_A
import net.corda.testing.DUMMY_BANK_B
import net.corda.testing.DUMMY_NOTARY
import net.corda.flows.IssuerFlow
import net.corda.testing.driver.poll
import net.corda.node.services.startFlowPermission
import net.corda.node.services.transactions.SimpleNotaryService
import net.corda.nodeapi.User
import net.corda.testing.BOC
import net.corda.testing.driver.poll
import net.corda.testing.node.NodeBasedTest
import net.corda.traderdemo.flow.BuyerFlow
import net.corda.traderdemo.flow.SellerFlow
@ -40,7 +40,7 @@ class TraderDemoTest : NodeBasedTest() {
nodeA.registerInitiatedFlow(BuyerFlow::class.java)
val (nodeARpc, nodeBRpc) = listOf(nodeA, nodeB).map {
val client = CordaRPCClient(it.configuration.rpcAddress!!)
val client = CordaRPCClient(it.configuration.rpcAddress!!, initialiseSerialization = false)
client.start(demoUser[0].username, demoUser[0].password).proxy
}

View File

@ -126,11 +126,17 @@ fun getFreeLocalPorts(hostName: String, numberToAlloc: Int): List<NetworkHostAnd
*/
@JvmOverloads fun ledger(
services: ServiceHub = MockServices(),
initialiseSerialization: Boolean = true,
dsl: LedgerDSL<TestTransactionDSLInterpreter, TestLedgerDSLInterpreter>.() -> Unit
): LedgerDSL<TestTransactionDSLInterpreter, TestLedgerDSLInterpreter> {
val ledgerDsl = LedgerDSL(TestLedgerDSLInterpreter(services))
dsl(ledgerDsl)
return ledgerDsl
if (initialiseSerialization) initialiseTestSerialization()
try {
val ledgerDsl = LedgerDSL(TestLedgerDSLInterpreter(services))
dsl(ledgerDsl)
return ledgerDsl
} finally {
if (initialiseSerialization) resetTestSerialization()
}
}
/**
@ -141,8 +147,9 @@ fun getFreeLocalPorts(hostName: String, numberToAlloc: Int): List<NetworkHostAnd
@JvmOverloads fun transaction(
transactionLabel: String? = null,
transactionBuilder: TransactionBuilder = TransactionBuilder(notary = DUMMY_NOTARY),
initialiseSerialization: Boolean = true,
dsl: TransactionDSL<TransactionDSLInterpreter>.() -> EnforceVerifyOrFail
) = ledger { this.transaction(transactionLabel, transactionBuilder, dsl) }
) = ledger(initialiseSerialization = initialiseSerialization) { this.transaction(transactionLabel, transactionBuilder, dsl) }
fun testNodeConfiguration(
baseDirectory: Path,

View File

@ -5,6 +5,7 @@ import net.corda.client.mock.Generator
import net.corda.client.mock.generateOrFail
import net.corda.client.mock.int
import net.corda.client.mock.string
import net.corda.client.rpc.CordaRPCClient
import net.corda.client.rpc.internal.RPCClient
import net.corda.client.rpc.internal.RPCClientConfiguration
import net.corda.core.internal.div
@ -22,6 +23,7 @@ import net.corda.nodeapi.ArtemisTcpTransport
import net.corda.nodeapi.ConnectionDirection
import net.corda.nodeapi.RPCApi
import net.corda.nodeapi.User
import net.corda.nodeapi.serialization.KRYO_RPC_CLIENT_CONTEXT
import net.corda.testing.driver.*
import org.apache.activemq.artemis.api.core.SimpleString
import org.apache.activemq.artemis.api.core.TransportConfiguration
@ -224,6 +226,7 @@ fun <A> rpcDriver(
debugPortAllocation: PortAllocation = globalDebugPortAllocation,
systemProperties: Map<String, String> = emptyMap(),
useTestClock: Boolean = false,
initialiseSerialization: Boolean = true,
networkMapStartStrategy: NetworkMapStartStrategy = NetworkMapStartStrategy.Dedicated(startAutomatically = false),
startNodesInProcess: Boolean = false,
dsl: RPCDriverExposedDSLInterface.() -> A
@ -241,7 +244,8 @@ fun <A> rpcDriver(
)
),
coerce = { it },
dsl = dsl
dsl = dsl,
initialiseSerialization = initialiseSerialization
)
private class SingleUserSecurityManager(val rpcUser: User) : ActiveMQSecurityManager3 {
@ -510,7 +514,8 @@ class RandomRpcUser {
val hostAndPort = args[1].parseNetworkHostAndPort()
val username = args[2]
val password = args[3]
val handle = RPCClient<RPCOps>(hostAndPort, null).start(rpcClass, username, password)
CordaRPCClient.initialiseSerialization()
val handle = RPCClient<RPCOps>(hostAndPort, null, serializationContext = KRYO_RPC_CLIENT_CONTEXT).start(rpcClass, username, password)
val callGenerators = rpcClass.declaredMethods.map { method ->
Generator.sequence(method.parameters.map {
generatorStore[it.type] ?: throw Exception("No generator for ${it.type}")

View File

@ -0,0 +1,140 @@
package net.corda.testing
import net.corda.client.rpc.serialization.KryoClientSerializationScheme
import net.corda.core.serialization.*
import net.corda.core.utilities.ByteSequence
import net.corda.node.serialization.KryoServerSerializationScheme
import net.corda.nodeapi.serialization.*
fun <T> withTestSerialization(block: () -> T): T {
initialiseTestSerialization()
try {
return block()
} finally {
resetTestSerialization()
}
}
fun initialiseTestSerialization() {
// Check that everything is configured for testing with mutable delegating instances.
try {
check(SerializationDefaults.SERIALIZATION_FACTORY is TestSerializationFactory) {
"Found non-test serialization configuration: ${SerializationDefaults.SERIALIZATION_FACTORY}"
}
} catch(e: IllegalStateException) {
SerializationDefaults.SERIALIZATION_FACTORY = TestSerializationFactory()
}
try {
check(SerializationDefaults.P2P_CONTEXT is TestSerializationContext)
} catch(e: IllegalStateException) {
SerializationDefaults.P2P_CONTEXT = TestSerializationContext()
}
try {
check(SerializationDefaults.RPC_SERVER_CONTEXT is TestSerializationContext)
} catch(e: IllegalStateException) {
SerializationDefaults.RPC_SERVER_CONTEXT = TestSerializationContext()
}
try {
check(SerializationDefaults.RPC_CLIENT_CONTEXT is TestSerializationContext)
} catch(e: IllegalStateException) {
SerializationDefaults.RPC_CLIENT_CONTEXT = TestSerializationContext()
}
try {
check(SerializationDefaults.STORAGE_CONTEXT is TestSerializationContext)
} catch(e: IllegalStateException) {
SerializationDefaults.STORAGE_CONTEXT = TestSerializationContext()
}
try {
check(SerializationDefaults.CHECKPOINT_CONTEXT is TestSerializationContext)
} catch(e: IllegalStateException) {
SerializationDefaults.CHECKPOINT_CONTEXT = TestSerializationContext()
}
// Check that the previous test, if there was one, cleaned up after itself.
// IF YOU SEE THESE MESSAGES, THEN IT MEANS A TEST HAS NOT CALLED resetTestSerialization()
check((SerializationDefaults.SERIALIZATION_FACTORY as TestSerializationFactory).delegate == null, { "Expected uninitialised serialization framework but found it set from: ${SerializationDefaults.SERIALIZATION_FACTORY}" })
check((SerializationDefaults.P2P_CONTEXT as TestSerializationContext).delegate == null, { "Expected uninitialised serialization framework but found it set from: ${SerializationDefaults.P2P_CONTEXT}" })
check((SerializationDefaults.RPC_SERVER_CONTEXT as TestSerializationContext).delegate == null, { "Expected uninitialised serialization framework but found it set from: ${SerializationDefaults.RPC_SERVER_CONTEXT}" })
check((SerializationDefaults.RPC_CLIENT_CONTEXT as TestSerializationContext).delegate == null, { "Expected uninitialised serialization framework but found it set from: ${SerializationDefaults.RPC_CLIENT_CONTEXT}" })
check((SerializationDefaults.STORAGE_CONTEXT as TestSerializationContext).delegate == null, { "Expected uninitialised serialization framework but found it set from: ${SerializationDefaults.STORAGE_CONTEXT}" })
check((SerializationDefaults.CHECKPOINT_CONTEXT as TestSerializationContext).delegate == null, { "Expected uninitialised serialization framework but found it set from: ${SerializationDefaults.CHECKPOINT_CONTEXT}" })
// Now configure all the testing related delegates.
(SerializationDefaults.SERIALIZATION_FACTORY as TestSerializationFactory).delegate = SerializationFactoryImpl().apply {
registerScheme(KryoClientSerializationScheme())
registerScheme(KryoServerSerializationScheme())
}
(SerializationDefaults.P2P_CONTEXT as TestSerializationContext).delegate = KRYO_P2P_CONTEXT
(SerializationDefaults.RPC_SERVER_CONTEXT as TestSerializationContext).delegate = KRYO_RPC_SERVER_CONTEXT
(SerializationDefaults.RPC_CLIENT_CONTEXT as TestSerializationContext).delegate = KRYO_RPC_CLIENT_CONTEXT
(SerializationDefaults.STORAGE_CONTEXT as TestSerializationContext).delegate = KRYO_STORAGE_CONTEXT
(SerializationDefaults.CHECKPOINT_CONTEXT as TestSerializationContext).delegate = KRYO_CHECKPOINT_CONTEXT
}
fun resetTestSerialization() {
(SerializationDefaults.SERIALIZATION_FACTORY as TestSerializationFactory).delegate = null
(SerializationDefaults.P2P_CONTEXT as TestSerializationContext).delegate = null
(SerializationDefaults.RPC_SERVER_CONTEXT as TestSerializationContext).delegate = null
(SerializationDefaults.RPC_CLIENT_CONTEXT as TestSerializationContext).delegate = null
(SerializationDefaults.STORAGE_CONTEXT as TestSerializationContext).delegate = null
(SerializationDefaults.CHECKPOINT_CONTEXT as TestSerializationContext).delegate = null
}
class TestSerializationFactory : SerializationFactory {
var delegate: SerializationFactory? = null
set(value) {
field = value
stackTrace = Exception().stackTrace.asList()
}
private var stackTrace: List<StackTraceElement>? = null
override fun toString(): String = stackTrace?.joinToString("\n") ?: "null"
override fun <T : Any> deserialize(byteSequence: ByteSequence, clazz: Class<T>, context: SerializationContext): T {
return delegate!!.deserialize(byteSequence, clazz, context)
}
override fun <T : Any> serialize(obj: T, context: SerializationContext): SerializedBytes<T> {
return delegate!!.serialize(obj, context)
}
}
class TestSerializationContext : SerializationContext {
var delegate: SerializationContext? = null
set(value) {
field = value
stackTrace = Exception().stackTrace.asList()
}
private var stackTrace: List<StackTraceElement>? = null
override fun toString(): String = stackTrace?.joinToString("\n") ?: "null"
override val preferedSerializationVersion: ByteSequence
get() = delegate!!.preferedSerializationVersion
override val deserializationClassLoader: ClassLoader
get() = delegate!!.deserializationClassLoader
override val whitelist: ClassWhitelist
get() = delegate!!.whitelist
override val properties: Map<Any, Any>
get() = delegate!!.properties
override val objectReferencesEnabled: Boolean
get() = delegate!!.objectReferencesEnabled
override val useCase: SerializationContext.UseCase
get() = delegate!!.useCase
override fun withProperty(property: Any, value: Any): SerializationContext {
return TestSerializationContext().apply { delegate = this@TestSerializationContext.delegate!!.withProperty(property, value) }
}
override fun withoutReferences(): SerializationContext {
return TestSerializationContext().apply { delegate = this@TestSerializationContext.delegate!!.withoutReferences() }
}
override fun withClassLoader(classLoader: ClassLoader): SerializationContext {
return TestSerializationContext().apply { delegate = this@TestSerializationContext.delegate!!.withClassLoader(classLoader) }
}
override fun withWhitelisted(clazz: Class<*>): SerializationContext {
return TestSerializationContext().apply { delegate = this@TestSerializationContext.delegate!!.withWhitelisted(clazz) }
}
}

View File

@ -0,0 +1,19 @@
package net.corda.testing
import org.junit.After
import org.junit.Before
/**
* The beginnings of somewhere to inject implementations for unit tests.
*/
abstract class TestDependencyInjectionBase {
@Before
fun initialiseSerialization() {
initialiseTestSerialization()
}
@After
fun resetInitialisation() {
resetTestSerialization()
}
}

View File

@ -40,6 +40,8 @@ import net.corda.testing.BOB
import net.corda.testing.DUMMY_BANK_A
import net.corda.testing.DUMMY_NOTARY
import net.corda.testing.node.MOCK_VERSION_INFO
import net.corda.testing.initialiseTestSerialization
import net.corda.testing.resetTestSerialization
import okhttp3.OkHttpClient
import okhttp3.Request
import org.bouncycastle.asn1.x500.X500Name
@ -187,7 +189,7 @@ sealed class NodeHandle {
val nodeThread: Thread
) : NodeHandle()
fun rpcClientToNode(): CordaRPCClient = CordaRPCClient(configuration.rpcAddress!!)
fun rpcClientToNode(): CordaRPCClient = CordaRPCClient(configuration.rpcAddress!!, initialiseSerialization = false)
}
data class WebserverHandle(
@ -250,6 +252,7 @@ fun <A> driver(
debugPortAllocation: PortAllocation = PortAllocation.Incremental(5005),
systemProperties: Map<String, String> = emptyMap(),
useTestClock: Boolean = false,
initialiseSerialization: Boolean = true,
networkMapStartStrategy: NetworkMapStartStrategy = NetworkMapStartStrategy.Dedicated(startAutomatically = true),
startNodesInProcess: Boolean = false,
dsl: DriverDSLExposedInterface.() -> A
@ -278,9 +281,11 @@ fun <A> driver(
*/
fun <DI : DriverDSLExposedInterface, D : DriverDSLInternalInterface, A> genericDriver(
driverDsl: D,
initialiseSerialization: Boolean = true,
coerce: (D) -> DI,
dsl: DI.() -> A
): A {
if (initialiseSerialization) initialiseTestSerialization()
val shutdownHook = addShutdownHook(driverDsl::shutdown)
try {
driverDsl.start()
@ -291,6 +296,7 @@ fun <DI : DriverDSLExposedInterface, D : DriverDSLInternalInterface, A> genericD
} finally {
driverDsl.shutdown()
shutdownHook.cancel()
if (initialiseSerialization) resetTestSerialization()
}
}
@ -511,7 +517,7 @@ class DriverDSL(
}
private fun establishRpc(nodeAddress: NetworkHostAndPort, sslConfig: SSLConfiguration, processDeathFuture: ListenableFuture<out Throwable>): ListenableFuture<CordaRPCOps> {
val client = CordaRPCClient(nodeAddress, sslConfig)
val client = CordaRPCClient(nodeAddress, sslConfig, initialiseSerialization = false)
val connectionFuture = poll(executorService, "RPC connection") {
try {
client.start(ArtemisMessagingComponent.NODE_USER, ArtemisMessagingComponent.NODE_USER)
@ -769,7 +775,7 @@ class DriverDSL(
writeConfig(nodeConf.baseDirectory, "node.conf", config)
val clock: Clock = if (nodeConf.useTestClock) TestClock() else NodeClock()
// TODO pass the version in?
val node = Node(nodeConf, nodeConf.calculateServices(), MOCK_VERSION_INFO, clock)
val node = Node(nodeConf, nodeConf.calculateServices(), MOCK_VERSION_INFO, clock, initialiseSerialization = false)
node.start()
val nodeThread = thread(name = nodeConf.myLegalName.commonName) {
node.run()

View File

@ -5,11 +5,11 @@ import com.google.common.jimfs.Jimfs
import com.google.common.util.concurrent.Futures
import com.google.common.util.concurrent.ListenableFuture
import com.nhaarman.mockito_kotlin.whenever
import net.corda.core.*
import net.corda.core.crypto.CertificateAndKeyPair
import net.corda.core.crypto.cert
import net.corda.core.crypto.entropyToKeyPair
import net.corda.core.crypto.random63BitValue
import net.corda.core.getOrThrow
import net.corda.core.identity.PartyAndCertificate
import net.corda.core.internal.createDirectories
import net.corda.core.internal.createDirectory
@ -19,11 +19,11 @@ import net.corda.core.messaging.RPCOps
import net.corda.core.messaging.SingleMessageRecipient
import net.corda.core.node.CordaPluginRegistry
import net.corda.core.node.ServiceEntry
import net.corda.core.utilities.NetworkHostAndPort
import net.corda.core.node.WorldMapLocation
import net.corda.core.node.services.IdentityService
import net.corda.core.node.services.KeyManagementService
import net.corda.core.node.services.ServiceInfo
import net.corda.core.utilities.NetworkHostAndPort
import net.corda.core.utilities.loggerFor
import net.corda.node.internal.AbstractNode
import net.corda.node.services.config.NodeConfiguration
@ -67,7 +67,8 @@ class MockNetwork(private val networkSendManuallyPumped: Boolean = false,
private val threadPerNode: Boolean = false,
servicePeerAllocationStrategy: InMemoryMessagingNetwork.ServicePeerAllocationStrategy =
InMemoryMessagingNetwork.ServicePeerAllocationStrategy.Random(),
private val defaultFactory: Factory = MockNetwork.DefaultFactory) {
private val defaultFactory: Factory = MockNetwork.DefaultFactory,
private val initialiseSerialization: Boolean = true) {
val nextNodeId
get() = _nextNodeId
private var _nextNodeId = 0
@ -85,6 +86,7 @@ class MockNetwork(private val networkSendManuallyPumped: Boolean = false,
val nodes: List<MockNode> = _nodes
init {
if (initialiseSerialization) initialiseTestSerialization()
filesystem.getPath("/nodes").createDirectory()
}
@ -396,6 +398,7 @@ class MockNetwork(private val networkSendManuallyPumped: Boolean = false,
fun stopNodes() {
nodes.forEach { if (it.started) it.stop() }
if (initialiseSerialization) resetTestSerialization()
}
// Test method to block until all scheduled activity, active flows

View File

@ -3,12 +3,14 @@ package net.corda.testing.node
import com.google.common.util.concurrent.Futures
import com.google.common.util.concurrent.ListenableFuture
import com.google.common.util.concurrent.MoreExecutors.listeningDecorator
import net.corda.core.*
import net.corda.core.crypto.X509Utilities
import net.corda.core.crypto.appendToCommonName
import net.corda.core.crypto.commonName
import net.corda.core.flatMap
import net.corda.core.getOrThrow
import net.corda.core.internal.createDirectories
import net.corda.core.internal.div
import net.corda.core.map
import net.corda.core.node.services.ServiceInfo
import net.corda.core.node.services.ServiceType
import net.corda.core.utilities.WHITESPACE
@ -23,6 +25,7 @@ import net.corda.node.utilities.ServiceIdentityGenerator
import net.corda.nodeapi.User
import net.corda.nodeapi.config.parseAs
import net.corda.testing.DUMMY_MAP
import net.corda.testing.TestDependencyInjectionBase
import net.corda.testing.driver.addressMustNotBeBoundFuture
import net.corda.testing.getFreeLocalPorts
import org.apache.logging.log4j.Level
@ -39,7 +42,7 @@ import kotlin.concurrent.thread
* purposes. Use the driver if you need to run the nodes in separate processes otherwise this class will suffice.
*/
// TODO Some of the logic here duplicates what's in the driver
abstract class NodeBasedTest {
abstract class NodeBasedTest : TestDependencyInjectionBase() {
@Rule
@JvmField
val tempFolder = TemporaryFolder()
@ -161,7 +164,7 @@ abstract class NodeBasedTest {
val parsedConfig = config.parseAs<FullNodeConfiguration>()
val node = Node(parsedConfig, parsedConfig.calculateServices(), MOCK_VERSION_INFO.copy(platformVersion = platformVersion),
if (parsedConfig.useTestClock) TestClock() else NodeClock())
if (parsedConfig.useTestClock) TestClock() else NodeClock(), initialiseSerialization = false)
node.start()
nodes += node
thread(name = legalName.commonName) {

View File

@ -1,9 +1,13 @@
package net.corda.verifier
import com.esotericsoftware.kryo.pool.KryoPool
import com.typesafe.config.Config
import com.typesafe.config.ConfigFactory
import com.typesafe.config.ConfigParseOptions
import net.corda.core.internal.div
import net.corda.core.serialization.SerializationContext
import net.corda.core.serialization.SerializationDefaults
import net.corda.core.utilities.ByteSequence
import net.corda.core.utilities.NetworkHostAndPort
import net.corda.core.utilities.debug
import net.corda.core.utilities.loggerFor
@ -14,6 +18,10 @@ import net.corda.nodeapi.VerifierApi.VERIFICATION_REQUESTS_QUEUE_NAME
import net.corda.nodeapi.config.NodeSSLConfiguration
import net.corda.nodeapi.config.getValue
import net.corda.nodeapi.internal.addShutdownHook
import net.corda.nodeapi.serialization.AbstractKryoSerializationScheme
import net.corda.nodeapi.serialization.KRYO_P2P_CONTEXT
import net.corda.nodeapi.serialization.KryoHeaderV0_1
import net.corda.nodeapi.serialization.SerializationFactoryImpl
import org.apache.activemq.artemis.api.core.client.ActiveMQClient
import java.nio.file.Path
import java.nio.file.Paths
@ -55,6 +63,7 @@ class Verifier {
session.close()
sessionFactory.close()
}
initialiseSerialization()
val consumer = session.createConsumer(VERIFICATION_REQUESTS_QUEUE_NAME)
val replyProducer = session.createProducer()
consumer.setMessageHandler {
@ -77,5 +86,26 @@ class Verifier {
log.info("Verifier started")
Thread.sleep(Long.MAX_VALUE)
}
private fun initialiseSerialization() {
SerializationDefaults.SERIALIZATION_FACTORY = SerializationFactoryImpl().apply {
registerScheme(KryoVerifierSerializationScheme)
}
SerializationDefaults.P2P_CONTEXT = KRYO_P2P_CONTEXT
}
}
object KryoVerifierSerializationScheme : AbstractKryoSerializationScheme() {
override fun canDeserializeVersion(byteSequence: ByteSequence, target: SerializationContext.UseCase): Boolean {
return byteSequence.equals(KryoHeaderV0_1) && target == SerializationContext.UseCase.P2P
}
override fun rpcClientKryoPool(context: SerializationContext): KryoPool {
throw UnsupportedOperationException()
}
override fun rpcServerKryoPool(context: SerializationContext): KryoPool {
throw UnsupportedOperationException()
}
}
}