mirror of
https://github.com/corda/corda.git
synced 2025-06-18 15:18:16 +00:00
Contract constraints (#1518)
* Contract constraints and attachment loading Fix compiler warnings. Fixed IdentitySyncFlowTests in confidential-identities. Fixes. Fix AttachmentClassLoaderTests. Added a TODO. Renamed cordapp service. Fix compilation error in java code. Fix RaftNotaryServiceTests Fix AttachmentLoadingTest Fix DistributedServiceTests and LargeTransactionTests. Add cordapp packages to Verifier tests. Refactor DummyContractBackdoor back out of internal package. Resolve compiler warnings. Consolidate excluding `isolated` project at top-level. Fix contract attachment serialisation for remote verifier. Fix integration tests for client:rpc. Contract constraints and attachment loading Fix compiler warnings. Fixed IdentitySyncFlowTests in confidential-identities. Fixes. Fix AttachmentClassLoaderTests. Added a TODO. Renamed cordapp service. Fix compilation error in java code. Fix example compilation. Fix RaftNotaryServiceTests Fix AttachmentLoadingTest Fix DistributedServiceTests and LargeTransactionTests. Add cordapp packages to Verifier tests. Refactor DummyContractBackdoor back out of internal package. Resolve compiler warnings. Consolidate excluding `isolated` project at top-level. Fix integration tests for client:rpc. Fixed issues with node driver and differing ZIPs. Review changes. Refactor GeneratedAttachment into node-api module. Merge branch 'clint/hash-constraint' of https://github.com/corda/corda into clint/hash-constraint Fixed compile error following rebase. wip - test to check that app code isn't loaded from attachments sent over the wire. Use Kotlin copyTo() rather than Apache's IOUtils. Fixes more fixes. Removing unconstrained output. More fixes. Fixed another test. Added missing plugin definition in net.corda.core.node.CordaPluginRegistry: net.corda.finance.contracts.isolated.IsolatedPlugin Re-added missing magic string used in unit test. Remove unused FlowSession variable. * Review fixes. * More review fixes. * Moved Cordapp implementation to an internal package. * More JVMOverloads.
This commit is contained in:
@ -0,0 +1,17 @@
|
||||
package net.corda.nodeapi
|
||||
|
||||
import net.corda.core.contracts.ContractState
|
||||
import net.corda.core.contracts.PartyAndReference
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.core.transactions.TransactionBuilder
|
||||
|
||||
/**
|
||||
* This interface deliberately mirrors the one in the finance:isolated module.
|
||||
* We will actually link [AnotherDummyContract] against this interface rather
|
||||
* than the one inside isolated.jar, which means we won't need to use reflection
|
||||
* to execute the contract's generateInitial() method.
|
||||
*/
|
||||
interface DummyContractBackdoor {
|
||||
fun generateInitial(owner: PartyAndReference, magicNumber: Int, notary: Party): TransactionBuilder
|
||||
fun inspectState(state: ContractState): Int
|
||||
}
|
@ -0,0 +1,74 @@
|
||||
package net.corda.nodeapi.internal
|
||||
|
||||
import net.corda.core.contracts.*
|
||||
import net.corda.core.identity.AbstractParty
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.core.serialization.deserialize
|
||||
import net.corda.core.serialization.serialize
|
||||
import net.corda.core.transactions.LedgerTransaction
|
||||
import net.corda.core.transactions.TransactionBuilder
|
||||
import net.corda.testing.*
|
||||
import net.corda.testing.node.MockServices
|
||||
import org.junit.After
|
||||
import org.junit.Assert.*
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
|
||||
class AttachmentsClassLoaderStaticContractTests : TestDependencyInjectionBase() {
|
||||
|
||||
class AttachmentDummyContract : Contract {
|
||||
companion object {
|
||||
private val ATTACHMENT_PROGRAM_ID = "net.corda.nodeapi.internal.AttachmentsClassLoaderStaticContractTests\$AttachmentDummyContract"
|
||||
}
|
||||
|
||||
data class State(val magicNumber: Int = 0) : ContractState {
|
||||
override val participants: List<AbstractParty>
|
||||
get() = listOf()
|
||||
}
|
||||
|
||||
interface Commands : CommandData {
|
||||
class Create : TypeOnlyCommandData(), Commands
|
||||
}
|
||||
|
||||
override fun verify(tx: LedgerTransaction) {
|
||||
// Always accepts.
|
||||
}
|
||||
|
||||
fun generateInitial(owner: PartyAndReference, magicNumber: Int, notary: Party): TransactionBuilder {
|
||||
val state = State(magicNumber)
|
||||
return TransactionBuilder(notary)
|
||||
.withItems(StateAndContract(state, ATTACHMENT_PROGRAM_ID), Command(Commands.Create(), owner.party.owningKey))
|
||||
}
|
||||
}
|
||||
|
||||
private lateinit var serviceHub: MockServices
|
||||
|
||||
@Before
|
||||
fun `create service hub`() {
|
||||
serviceHub = MockServices(cordappPackages=listOf("net.corda.nodeapi.internal"))
|
||||
}
|
||||
|
||||
@After
|
||||
fun `clear packages`() {
|
||||
unsetCordappPackages()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test serialization of WireTransaction with statically loaded contract`() {
|
||||
val tx = AttachmentDummyContract().generateInitial(MEGA_CORP.ref(0), 42, DUMMY_NOTARY)
|
||||
val wireTransaction = tx.toWireTransaction(serviceHub)
|
||||
val bytes = wireTransaction.serialize()
|
||||
val copiedWireTransaction = bytes.deserialize()
|
||||
|
||||
assertEquals(1, copiedWireTransaction.outputs.size)
|
||||
assertEquals(42, (copiedWireTransaction.outputs[0].data as AttachmentDummyContract.State).magicNumber)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `verify that contract DummyContract is in classPath`() {
|
||||
val contractClass = Class.forName("net.corda.nodeapi.internal.AttachmentsClassLoaderStaticContractTests\$AttachmentDummyContract")
|
||||
val contract = contractClass.newInstance() as Contract
|
||||
|
||||
assertNotNull(contract)
|
||||
}
|
||||
}
|
@ -1,29 +1,30 @@
|
||||
package net.corda.nodeapi
|
||||
package net.corda.nodeapi.internal
|
||||
|
||||
import com.nhaarman.mockito_kotlin.mock
|
||||
import com.nhaarman.mockito_kotlin.whenever
|
||||
import net.corda.core.contracts.*
|
||||
import net.corda.core.crypto.SecureHash
|
||||
import net.corda.core.identity.AbstractParty
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.core.internal.declaredField
|
||||
import net.corda.core.node.ServiceHub
|
||||
import net.corda.core.node.services.AttachmentStorage
|
||||
import net.corda.core.serialization.*
|
||||
import net.corda.core.transactions.LedgerTransaction
|
||||
import net.corda.core.transactions.TransactionBuilder
|
||||
import net.corda.core.utilities.ByteSequence
|
||||
import net.corda.core.utilities.OpaqueBytes
|
||||
import net.corda.nodeapi.internal.AttachmentsClassLoader
|
||||
import net.corda.node.internal.cordapp.CordappLoader
|
||||
import net.corda.node.internal.cordapp.CordappProviderImpl
|
||||
import net.corda.nodeapi.DummyContractBackdoor
|
||||
import net.corda.nodeapi.internal.serialization.SerializeAsTokenContextImpl
|
||||
import net.corda.nodeapi.internal.serialization.attachmentsClassLoaderEnabledPropertyName
|
||||
import net.corda.nodeapi.internal.serialization.withTokenContext
|
||||
import net.corda.testing.DUMMY_NOTARY
|
||||
import net.corda.testing.MEGA_CORP
|
||||
import net.corda.testing.TestDependencyInjectionBase
|
||||
import net.corda.testing.kryoSpecific
|
||||
import net.corda.testing.node.MockAttachmentStorage
|
||||
import net.corda.testing.node.MockServices
|
||||
import org.apache.commons.io.IOUtils
|
||||
import org.junit.Assert
|
||||
import org.junit.Assert.*
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import java.io.ByteArrayInputStream
|
||||
import java.io.ByteArrayOutputStream
|
||||
@ -31,83 +32,72 @@ import java.net.URL
|
||||
import java.net.URLClassLoader
|
||||
import java.util.jar.JarOutputStream
|
||||
import java.util.zip.ZipEntry
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertFailsWith
|
||||
import kotlin.test.assertNotNull
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
interface DummyContractBackdoor {
|
||||
fun generateInitial(owner: PartyAndReference, magicNumber: Int, notary: Party): TransactionBuilder
|
||||
fun inspectState(state: ContractState): Int
|
||||
}
|
||||
|
||||
class AttachmentsClassLoaderTests : TestDependencyInjectionBase() {
|
||||
companion object {
|
||||
val ISOLATED_CONTRACTS_JAR_PATH: URL = AttachmentsClassLoaderTests::class.java.getResource("isolated.jar")
|
||||
private val ISOLATED_CONTRACT_CLASS_NAME = "net.corda.finance.contracts.isolated.AnotherDummyContract"
|
||||
private val ATTACHMENT_PROGRAM_ID = "net.corda.nodeapi.AttachmentsClassLoaderTests.AttachmentDummyContract"
|
||||
private const val ISOLATED_CONTRACT_CLASS_NAME = "net.corda.finance.contracts.isolated.AnotherDummyContract"
|
||||
|
||||
private fun SerializationContext.withAttachmentStorage(attachmentStorage: AttachmentStorage): SerializationContext {
|
||||
val serviceHub = mock<ServiceHub>()
|
||||
whenever(serviceHub.attachments).thenReturn(attachmentStorage)
|
||||
return this.withServiceHub(serviceHub)
|
||||
}
|
||||
private fun SerializationContext.withServiceHub(serviceHub: ServiceHub): SerializationContext {
|
||||
return this.withTokenContext(SerializeAsTokenContextImpl(serviceHub) {}).withProperty(attachmentsClassLoaderEnabledPropertyName, true)
|
||||
}
|
||||
}
|
||||
|
||||
class AttachmentDummyContract : Contract {
|
||||
data class State(val magicNumber: Int = 0) : ContractState {
|
||||
override val participants: List<AbstractParty>
|
||||
get() = listOf()
|
||||
}
|
||||
private lateinit var serviceHub: DummyServiceHub
|
||||
|
||||
interface Commands : CommandData {
|
||||
class Create : TypeOnlyCommandData(), Commands
|
||||
}
|
||||
class DummyServiceHub : MockServices() {
|
||||
override val cordappProvider: CordappProviderImpl
|
||||
= CordappProviderImpl(CordappLoader.createDevMode(listOf(ISOLATED_CONTRACTS_JAR_PATH))).start(attachments)
|
||||
|
||||
override fun verify(tx: LedgerTransaction) {
|
||||
// Always accepts.
|
||||
}
|
||||
|
||||
fun generateInitial(owner: PartyAndReference, magicNumber: Int, notary: Party): TransactionBuilder {
|
||||
val state = State(magicNumber)
|
||||
return TransactionBuilder(notary).withItems(StateAndContract(state, ATTACHMENT_PROGRAM_ID), Command(Commands.Create(), owner.party.owningKey))
|
||||
}
|
||||
private val cordapp get() = cordappProvider.cordapps.first()
|
||||
val attachmentId get() = cordappProvider.getCordappAttachmentId(cordapp)!!
|
||||
val appContext get() = cordappProvider.getAppContext(cordapp)
|
||||
}
|
||||
|
||||
private fun importJar(storage: AttachmentStorage) = ISOLATED_CONTRACTS_JAR_PATH.openStream().use { storage.importAttachment(it) }
|
||||
|
||||
// These ClassLoaders work together to load 'AnotherDummyContract' in a disposable way, such that even though
|
||||
// the class may be on the unit test class path (due to default IDE settings, etc), it won't be loaded into the
|
||||
// regular app classloader but rather than ClassLoaderForTests. This helps keep our environment clean and
|
||||
// ensures we have precise control over where it's loaded.
|
||||
object FilteringClassLoader : ClassLoader() {
|
||||
override fun loadClass(name: String, resolve: Boolean): Class<*>? {
|
||||
return if ("AnotherDummyContract" in name) {
|
||||
null
|
||||
} else
|
||||
super.loadClass(name, resolve)
|
||||
@Throws(ClassNotFoundException::class)
|
||||
override fun loadClass(name: String, resolve: Boolean): Class<*> {
|
||||
if ("AnotherDummyContract" in name) {
|
||||
throw ClassNotFoundException(name)
|
||||
}
|
||||
return super.loadClass(name, resolve)
|
||||
}
|
||||
}
|
||||
|
||||
class ClassLoaderForTests : URLClassLoader(arrayOf(ISOLATED_CONTRACTS_JAR_PATH), FilteringClassLoader)
|
||||
|
||||
@Before
|
||||
fun `create service hub`() {
|
||||
serviceHub = DummyServiceHub()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `dynamically load AnotherDummyContract from isolated contracts jar`() {
|
||||
val child = ClassLoaderForTests()
|
||||
ClassLoaderForTests().use { child ->
|
||||
val contractClass = Class.forName(ISOLATED_CONTRACT_CLASS_NAME, true, child)
|
||||
val contract = contractClass.newInstance() as Contract
|
||||
|
||||
val contractClass = Class.forName(ISOLATED_CONTRACT_CLASS_NAME, true, child)
|
||||
val contract = contractClass.newInstance() as Contract
|
||||
|
||||
assertEquals("helloworld", contract.declaredField<Any?>("magicString").value)
|
||||
assertEquals("helloworld", contract.declaredField<Any?>("magicString").value)
|
||||
}
|
||||
}
|
||||
|
||||
private fun fakeAttachment(filepath: String, content: String): ByteArray {
|
||||
val bs = ByteArrayOutputStream()
|
||||
val js = JarOutputStream(bs)
|
||||
js.putNextEntry(ZipEntry(filepath))
|
||||
js.writer().apply { append(content); flush() }
|
||||
js.closeEntry()
|
||||
js.close()
|
||||
JarOutputStream(bs).use { js ->
|
||||
js.putNextEntry(ZipEntry(filepath))
|
||||
js.writer().apply { append(content); flush() }
|
||||
js.closeEntry()
|
||||
}
|
||||
return bs.toByteArray()
|
||||
}
|
||||
|
||||
@ -116,13 +106,12 @@ class AttachmentsClassLoaderTests : TestDependencyInjectionBase() {
|
||||
attachment.extractFile(filepath, it)
|
||||
return it.toByteArray()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test MockAttachmentStorage open as jar`() {
|
||||
val storage = MockAttachmentStorage()
|
||||
val key = importJar(storage)
|
||||
val storage = serviceHub.attachments
|
||||
val key = serviceHub.attachmentId
|
||||
val attachment = storage.openAttachment(key)!!
|
||||
|
||||
val jar = attachment.openAsJAR()
|
||||
@ -132,9 +121,9 @@ class AttachmentsClassLoaderTests : TestDependencyInjectionBase() {
|
||||
|
||||
@Test
|
||||
fun `test overlapping file exception`() {
|
||||
val storage = MockAttachmentStorage()
|
||||
val storage = serviceHub.attachments
|
||||
|
||||
val att0 = importJar(storage)
|
||||
val att0 = serviceHub.attachmentId
|
||||
val att1 = storage.importAttachment(ByteArrayInputStream(fakeAttachment("file.txt", "some data")))
|
||||
val att2 = storage.importAttachment(ByteArrayInputStream(fakeAttachment("file.txt", "some other data")))
|
||||
|
||||
@ -145,9 +134,9 @@ class AttachmentsClassLoaderTests : TestDependencyInjectionBase() {
|
||||
|
||||
@Test
|
||||
fun `basic`() {
|
||||
val storage = MockAttachmentStorage()
|
||||
val storage = serviceHub.attachments
|
||||
|
||||
val att0 = importJar(storage)
|
||||
val att0 = serviceHub.attachmentId
|
||||
val att1 = storage.importAttachment(ByteArrayInputStream(fakeAttachment("file1.txt", "some data")))
|
||||
val att2 = storage.importAttachment(ByteArrayInputStream(fakeAttachment("file2.txt", "some other data")))
|
||||
|
||||
@ -164,24 +153,23 @@ class AttachmentsClassLoaderTests : TestDependencyInjectionBase() {
|
||||
val att2 = storage.importAttachment(ByteArrayInputStream(fakeAttachment("\\folder1\\folderb\\file2.txt", "some other data")))
|
||||
|
||||
val data1a = readAttachment(storage.openAttachment(att1)!!, "/folder1/foldera/file1.txt")
|
||||
Assert.assertArrayEquals("some data".toByteArray(), data1a)
|
||||
assertArrayEquals("some data".toByteArray(), data1a)
|
||||
|
||||
val data1b = readAttachment(storage.openAttachment(att1)!!, "\\folder1\\foldera\\file1.txt")
|
||||
Assert.assertArrayEquals("some data".toByteArray(), data1b)
|
||||
assertArrayEquals("some data".toByteArray(), data1b)
|
||||
|
||||
val data2a = readAttachment(storage.openAttachment(att2)!!, "\\folder1\\folderb\\file2.txt")
|
||||
Assert.assertArrayEquals("some other data".toByteArray(), data2a)
|
||||
assertArrayEquals("some other data".toByteArray(), data2a)
|
||||
|
||||
val data2b = readAttachment(storage.openAttachment(att2)!!, "/folder1/folderb/file2.txt")
|
||||
Assert.assertArrayEquals("some other data".toByteArray(), data2b)
|
||||
|
||||
assertArrayEquals("some other data".toByteArray(), data2b)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `loading class AnotherDummyContract`() {
|
||||
val storage = MockAttachmentStorage()
|
||||
val storage = serviceHub.attachments
|
||||
|
||||
val att0 = importJar(storage)
|
||||
val att0 = serviceHub.attachmentId
|
||||
val att1 = storage.importAttachment(ByteArrayInputStream(fakeAttachment("file1.txt", "some data")))
|
||||
val att2 = storage.importAttachment(ByteArrayInputStream(fakeAttachment("file2.txt", "some other data")))
|
||||
|
||||
@ -192,18 +180,11 @@ class AttachmentsClassLoaderTests : TestDependencyInjectionBase() {
|
||||
assertEquals("helloworld", contract.declaredField<Any?>("magicString").value)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `verify that contract DummyContract is in classPath`() {
|
||||
val contractClass = Class.forName("net.corda.nodeapi.AttachmentsClassLoaderTests\$AttachmentDummyContract")
|
||||
val contract = contractClass.newInstance() as Contract
|
||||
|
||||
assertNotNull(contract)
|
||||
}
|
||||
|
||||
private fun createContract2Cash(): Contract {
|
||||
val cl = ClassLoaderForTests()
|
||||
val contractClass = Class.forName(ISOLATED_CONTRACT_CLASS_NAME, true, cl)
|
||||
return contractClass.newInstance() as Contract
|
||||
ClassLoaderForTests().use { cl ->
|
||||
val contractClass = Class.forName(ISOLATED_CONTRACT_CLASS_NAME, true, cl)
|
||||
return contractClass.newInstance() as Contract
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -212,9 +193,9 @@ class AttachmentsClassLoaderTests : TestDependencyInjectionBase() {
|
||||
|
||||
val bytes = contract.serialize()
|
||||
|
||||
val storage = MockAttachmentStorage()
|
||||
val storage = serviceHub.attachments
|
||||
|
||||
val att0 = importJar(storage)
|
||||
val att0 = serviceHub.attachmentId
|
||||
val att1 = storage.importAttachment(ByteArrayInputStream(fakeAttachment("file1.txt", "some data")))
|
||||
val att2 = storage.importAttachment(ByteArrayInputStream(fakeAttachment("file2.txt", "some other data")))
|
||||
|
||||
@ -240,9 +221,9 @@ class AttachmentsClassLoaderTests : TestDependencyInjectionBase() {
|
||||
|
||||
val bytes = data.serialize(context = context2)
|
||||
|
||||
val storage = MockAttachmentStorage()
|
||||
val storage = serviceHub.attachments
|
||||
|
||||
val att0 = importJar(storage)
|
||||
val att0 = serviceHub.attachmentId
|
||||
val att1 = storage.importAttachment(ByteArrayInputStream(fakeAttachment("file1.txt", "some data")))
|
||||
val att2 = storage.importAttachment(ByteArrayInputStream(fakeAttachment("file2.txt", "some other data")))
|
||||
|
||||
@ -291,39 +272,26 @@ class AttachmentsClassLoaderTests : TestDependencyInjectionBase() {
|
||||
assertEquals(bytesSequence, copiedBytesSequence)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test serialization of WireTransaction with statically loaded contract`() {
|
||||
val tx = AttachmentDummyContract().generateInitial(MEGA_CORP.ref(0), 42, DUMMY_NOTARY)
|
||||
val wireTransaction = tx.toWireTransaction()
|
||||
val bytes = wireTransaction.serialize()
|
||||
val copiedWireTransaction = bytes.deserialize()
|
||||
|
||||
assertEquals(1, copiedWireTransaction.outputs.size)
|
||||
assertEquals(42, (copiedWireTransaction.outputs[0].data as AttachmentDummyContract.State).magicNumber)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test serialization of WireTransaction with dynamically loaded contract`() {
|
||||
val child = ClassLoaderForTests()
|
||||
val child = serviceHub.appContext.classLoader
|
||||
val contractClass = Class.forName(ISOLATED_CONTRACT_CLASS_NAME, true, child)
|
||||
val contract = contractClass.newInstance() as DummyContractBackdoor
|
||||
val tx = contract.generateInitial(MEGA_CORP.ref(0), 42, DUMMY_NOTARY)
|
||||
val storage = MockAttachmentStorage()
|
||||
val context = SerializationFactory.defaultFactory.defaultContext.withWhitelisted(contract.javaClass)
|
||||
val context = SerializationFactory.defaultFactory.defaultContext
|
||||
.withWhitelisted(contract.javaClass)
|
||||
.withWhitelisted(Class.forName("$ISOLATED_CONTRACT_CLASS_NAME\$State", true, child))
|
||||
.withWhitelisted(Class.forName("$ISOLATED_CONTRACT_CLASS_NAME\$Commands\$Create", true, child))
|
||||
.withAttachmentStorage(storage)
|
||||
.withServiceHub(serviceHub)
|
||||
.withClassLoader(child)
|
||||
|
||||
// todo - think about better way to push attachmentStorage down to serializer
|
||||
val bytes = run {
|
||||
val attachmentRef = importJar(storage)
|
||||
tx.addAttachment(storage.openAttachment(attachmentRef)!!.id)
|
||||
val wireTransaction = tx.toWireTransaction(serializationContext = context)
|
||||
wireTransaction.serialize()
|
||||
val wireTransaction = tx.toWireTransaction(serviceHub, context)
|
||||
wireTransaction.serialize(context = context)
|
||||
}
|
||||
val copiedWireTransaction = bytes.deserialize(context = context)
|
||||
assertEquals(1, copiedWireTransaction.outputs.size)
|
||||
// Contracts need to be loaded by the same classloader as the ContractState itself
|
||||
// Contracts need to be loaded by the same classloader as the ContractState itself
|
||||
val contractClassloader = copiedWireTransaction.getOutput(0).javaClass.classLoader
|
||||
val contract2 = contractClassloader.loadClass(copiedWireTransaction.outputs.first().contract).newInstance() as DummyContractBackdoor
|
||||
assertEquals(contract2.javaClass.classLoader, copiedWireTransaction.outputs[0].data.javaClass.classLoader)
|
||||
@ -332,82 +300,82 @@ class AttachmentsClassLoaderTests : TestDependencyInjectionBase() {
|
||||
|
||||
@Test
|
||||
fun `test deserialize of WireTransaction where contract cannot be found`() {
|
||||
val child = ClassLoaderForTests()
|
||||
val contractClass = Class.forName(ISOLATED_CONTRACT_CLASS_NAME, true, child)
|
||||
val contract = contractClass.newInstance() as DummyContractBackdoor
|
||||
val tx = contract.generateInitial(MEGA_CORP.ref(0), 42, DUMMY_NOTARY)
|
||||
val storage = MockAttachmentStorage()
|
||||
val context = SerializationFactory.defaultFactory.defaultContext.withWhitelisted(contract.javaClass)
|
||||
.withWhitelisted(Class.forName("net.corda.finance.contracts.isolated.AnotherDummyContract\$State", true, child))
|
||||
.withWhitelisted(Class.forName("net.corda.finance.contracts.isolated.AnotherDummyContract\$Commands\$Create", true, child))
|
||||
.withAttachmentStorage(storage)
|
||||
kryoSpecific<AttachmentsClassLoaderTests>("Kryo verifies/loads attachments on deserialization, whereas AMQP currently does not") {
|
||||
ClassLoaderForTests().use { child ->
|
||||
val contractClass = Class.forName(ISOLATED_CONTRACT_CLASS_NAME, true, child)
|
||||
val contract = contractClass.newInstance() as DummyContractBackdoor
|
||||
val tx = contract.generateInitial(MEGA_CORP.ref(0), 42, DUMMY_NOTARY)
|
||||
|
||||
// todo - think about better way to push attachmentStorage down to serializer
|
||||
val attachmentRef = importJar(storage)
|
||||
val bytes = run {
|
||||
val attachmentRef = serviceHub.attachmentId
|
||||
val bytes = run {
|
||||
val outboundContext = SerializationFactory.defaultFactory.defaultContext
|
||||
.withServiceHub(serviceHub)
|
||||
.withClassLoader(child)
|
||||
val wireTransaction = tx.toWireTransaction(serviceHub, outboundContext)
|
||||
wireTransaction.serialize(context = outboundContext)
|
||||
}
|
||||
// use empty attachmentStorage
|
||||
|
||||
tx.addAttachment(storage.openAttachment(attachmentRef)!!.id)
|
||||
val e = assertFailsWith(MissingAttachmentsException::class) {
|
||||
val mockAttStorage = MockAttachmentStorage()
|
||||
val inboundContext = SerializationFactory.defaultFactory.defaultContext
|
||||
.withAttachmentStorage(mockAttStorage)
|
||||
.withAttachmentsClassLoader(listOf(attachmentRef))
|
||||
bytes.deserialize(context = inboundContext)
|
||||
|
||||
val wireTransaction = tx.toWireTransaction(serializationContext = context)
|
||||
wireTransaction.serialize()
|
||||
}
|
||||
// use empty attachmentStorage
|
||||
|
||||
val e = assertFailsWith(MissingAttachmentsException::class) {
|
||||
val mockAttStorage = MockAttachmentStorage()
|
||||
bytes.deserialize(context = SerializationFactory.defaultFactory.defaultContext.withAttachmentStorage(mockAttStorage))
|
||||
|
||||
if(mockAttStorage.openAttachment(attachmentRef) == null) {
|
||||
throw MissingAttachmentsException(listOf(attachmentRef))
|
||||
if (mockAttStorage.openAttachment(attachmentRef) == null) {
|
||||
throw MissingAttachmentsException(listOf(attachmentRef))
|
||||
}
|
||||
}
|
||||
assertEquals(attachmentRef, e.ids.single())
|
||||
}
|
||||
}
|
||||
assertEquals(attachmentRef, e.ids.single())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test loading a class from attachment during deserialization`() {
|
||||
val child = ClassLoaderForTests()
|
||||
val contractClass = Class.forName(ISOLATED_CONTRACT_CLASS_NAME, true, child)
|
||||
val contract = contractClass.newInstance() as DummyContractBackdoor
|
||||
val storage = MockAttachmentStorage()
|
||||
val attachmentRef = importJar(storage)
|
||||
val outboundContext = SerializationFactory.defaultFactory.defaultContext.withClassLoader(child)
|
||||
// We currently ignore annotations in attachments, so manually whitelist.
|
||||
val inboundContext = SerializationFactory
|
||||
.defaultFactory
|
||||
.defaultContext
|
||||
.withWhitelisted(contract.javaClass)
|
||||
.withAttachmentStorage(storage)
|
||||
.withAttachmentsClassLoader(listOf(attachmentRef))
|
||||
|
||||
// Serialize with custom context to avoid populating the default context with the specially loaded class
|
||||
val serialized = contract.serialize(context = outboundContext)
|
||||
// Then deserialize with the attachment class loader associated with the attachment
|
||||
serialized.deserialize(context = inboundContext)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test loading a class with attachment missing during deserialization`() {
|
||||
val child = ClassLoaderForTests()
|
||||
val contractClass = Class.forName(ISOLATED_CONTRACT_CLASS_NAME, true, child)
|
||||
val contract = contractClass.newInstance() as DummyContractBackdoor
|
||||
val storage = MockAttachmentStorage()
|
||||
val attachmentRef = SecureHash.randomSHA256()
|
||||
val outboundContext = SerializationFactory.defaultFactory.defaultContext.withClassLoader(child)
|
||||
// Serialize with custom context to avoid populating the default context with the specially loaded class.
|
||||
val serialized = contract.serialize(context = outboundContext)
|
||||
|
||||
// Then deserialize with the attachment class loader associated with the attachment.
|
||||
val e = assertFailsWith(MissingAttachmentsException::class) {
|
||||
ClassLoaderForTests().use { child ->
|
||||
val contractClass = Class.forName(ISOLATED_CONTRACT_CLASS_NAME, true, child)
|
||||
val contract = contractClass.newInstance() as DummyContractBackdoor
|
||||
val outboundContext = SerializationFactory.defaultFactory.defaultContext.withClassLoader(child)
|
||||
val attachmentRef = serviceHub.attachmentId
|
||||
// We currently ignore annotations in attachments, so manually whitelist.
|
||||
val inboundContext = SerializationFactory
|
||||
.defaultFactory
|
||||
.defaultContext
|
||||
.withWhitelisted(contract.javaClass)
|
||||
.withAttachmentStorage(storage)
|
||||
.withServiceHub(serviceHub)
|
||||
.withAttachmentsClassLoader(listOf(attachmentRef))
|
||||
|
||||
// Serialize with custom context to avoid populating the default context with the specially loaded class
|
||||
val serialized = contract.serialize(context = outboundContext)
|
||||
// Then deserialize with the attachment class loader associated with the attachment
|
||||
serialized.deserialize(context = inboundContext)
|
||||
}
|
||||
assertEquals(attachmentRef, e.ids.single())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test loading a class with attachment missing during deserialization`() {
|
||||
ClassLoaderForTests().use { child ->
|
||||
val contractClass = Class.forName(ISOLATED_CONTRACT_CLASS_NAME, true, child)
|
||||
val contract = contractClass.newInstance() as DummyContractBackdoor
|
||||
val attachmentRef = SecureHash.randomSHA256()
|
||||
val outboundContext = SerializationFactory.defaultFactory.defaultContext.withClassLoader(child)
|
||||
// Serialize with custom context to avoid populating the default context with the specially loaded class
|
||||
val serialized = contract.serialize(context = outboundContext)
|
||||
|
||||
// Then deserialize with the attachment class loader associated with the attachment
|
||||
val e = assertFailsWith(MissingAttachmentsException::class) {
|
||||
// We currently ignore annotations in attachments, so manually whitelist.
|
||||
val inboundContext = SerializationFactory
|
||||
.defaultFactory
|
||||
.defaultContext
|
||||
.withWhitelisted(contract.javaClass)
|
||||
.withServiceHub(serviceHub)
|
||||
.withAttachmentsClassLoader(listOf(attachmentRef))
|
||||
serialized.deserialize(context = inboundContext)
|
||||
}
|
||||
assertEquals(attachmentRef, e.ids.single())
|
||||
}
|
||||
}
|
||||
}
|
@ -10,8 +10,8 @@ import net.corda.core.serialization.SerializationContext
|
||||
import net.corda.core.serialization.SerializationFactory
|
||||
import net.corda.core.serialization.SerializedBytes
|
||||
import net.corda.core.utilities.ByteSequence
|
||||
import net.corda.nodeapi.AttachmentsClassLoaderTests
|
||||
import net.corda.nodeapi.internal.AttachmentsClassLoader
|
||||
import net.corda.nodeapi.internal.AttachmentsClassLoaderTests
|
||||
import net.corda.testing.node.MockAttachmentStorage
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
@ -82,11 +82,11 @@ class DefaultSerializableSerializer : Serializer<DefaultSerializable>() {
|
||||
class CordaClassResolverTests {
|
||||
val factory: SerializationFactory = object : SerializationFactory() {
|
||||
override fun <T : Any> deserialize(byteSequence: ByteSequence, clazz: Class<T>, context: SerializationContext): T {
|
||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||
TODO("not implemented")
|
||||
}
|
||||
|
||||
override fun <T : Any> serialize(obj: T, context: SerializationContext): SerializedBytes<T> {
|
||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||
TODO("not implemented")
|
||||
}
|
||||
|
||||
}
|
||||
|
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user