Test examining attachments within DJVM sandbox.

This commit is contained in:
Chris Rankin 2019-10-15 11:22:40 +01:00
parent ceeb170f6c
commit 210f10f6be
4 changed files with 148 additions and 2 deletions

View File

@ -0,0 +1,38 @@
package net.corda.contracts.djvm.attachment
import net.corda.core.contracts.CommandData
import net.corda.core.contracts.Contract
import net.corda.core.contracts.ContractState
import net.corda.core.identity.AbstractParty
import net.corda.core.transactions.LedgerTransaction
import java.io.ByteArrayOutputStream
class SandboxAttachmentContract : Contract {
override fun verify(tx: LedgerTransaction) {
val attachments = tx.attachments
require(attachments.isNotEmpty()) { "Attachments are missing for TX=${tx.id}" }
require(attachments.size == 1) { "Did not expect to find ${attachments.size} attachments for TX${tx.id}" }
val attachment = attachments[0]
require(attachment.size > 0) { "Attachment ${attachment.id} has no contents for TX=${tx.id}" }
val keyCount = attachment.signerKeys.size
require(keyCount == 1) { "Attachment ${attachment.id} has $keyCount signing keys for TX=${tx.id}" }
tx.commandsOfType(ExtractFile::class.java).forEach { extract ->
val fileName = extract.value.fileName
val contents = ByteArrayOutputStream().use {
attachment.extractFile(fileName, it)
it
}.toByteArray()
require(contents.isNotEmpty()) { "File $fileName has no contents for TX=${tx.id}" }
}
}
@Suppress("CanBeParameter", "MemberVisibilityCanBePrivate")
class State(val issuer: AbstractParty) : ContractState {
override val participants: List<AbstractParty> = listOf(issuer)
}
class ExtractFile(val fileName: String) : CommandData
}

View File

@ -22,8 +22,8 @@ class DeterministicCryptoContract : Contract {
}
}
if (cryptoData.isEmpty() || validators.isEmpty() || !isValid) {
throw IllegalStateException("Failed to validate signatures in command data")
require(cryptoData.isNotEmpty() && validators.isNotEmpty() && isValid) {
"Failed to validate signatures in command data"
}
}

View File

@ -0,0 +1,27 @@
package net.corda.flows.djvm.attachment
import co.paralleluniverse.fibers.Suspendable
import net.corda.contracts.djvm.attachment.SandboxAttachmentContract
import net.corda.core.contracts.Command
import net.corda.core.contracts.CommandData
import net.corda.core.crypto.SecureHash
import net.corda.core.flows.FlowLogic
import net.corda.core.flows.InitiatingFlow
import net.corda.core.flows.StartableByRPC
import net.corda.core.transactions.TransactionBuilder
@InitiatingFlow
@StartableByRPC
class SandboxAttachmentFlow(private val command: CommandData) : FlowLogic<SecureHash>() {
@Suspendable
override fun call(): SecureHash {
val notary = serviceHub.networkMapCache.notaryIdentities[0]
val stx = serviceHub.signInitialTransaction(
TransactionBuilder(notary)
.addOutputState(SandboxAttachmentContract.State(ourIdentity))
.addCommand(Command(command, ourIdentity.owningKey))
)
stx.verify(serviceHub, checkSufficientSignatures = false)
return stx.id
}
}

View File

@ -0,0 +1,81 @@
package net.corda.node.services
import net.corda.contracts.djvm.attachment.SandboxAttachmentContract
import net.corda.contracts.djvm.attachment.SandboxAttachmentContract.*
import net.corda.core.messaging.startFlow
import net.corda.core.utilities.getOrThrow
import net.corda.core.utilities.loggerFor
import net.corda.djvm.code.asResourcePath
import net.corda.flows.djvm.attachment.SandboxAttachmentFlow
import net.corda.node.DeterministicSourcesRule
import net.corda.node.internal.djvm.DeterministicVerificationException
import net.corda.testing.core.ALICE_NAME
import net.corda.testing.core.DUMMY_NOTARY_NAME
import net.corda.testing.driver.DriverParameters
import net.corda.testing.driver.driver
import net.corda.testing.driver.internal.incrementalPortAllocation
import net.corda.testing.node.NotarySpec
import net.corda.testing.node.internal.CustomCordapp
import net.corda.testing.node.internal.cordappWithPackages
import org.assertj.core.api.Assertions.assertThat
import org.junit.ClassRule
import org.junit.Test
import org.junit.jupiter.api.assertDoesNotThrow
import org.junit.jupiter.api.assertThrows
class SandboxAttachmentsTest {
companion object {
val logger = loggerFor<SandboxAttachmentsTest>()
@ClassRule
@JvmField
val djvmSources = DeterministicSourcesRule()
fun parametersFor(djvmSources: DeterministicSourcesRule): DriverParameters {
return DriverParameters(
portAllocation = incrementalPortAllocation(),
startNodesInProcess = false,
notarySpecs = listOf(NotarySpec(DUMMY_NOTARY_NAME, validating = true)),
cordappsForAllNodes = listOf(
cordappWithPackages("net.corda.flows.djvm.attachment"),
CustomCordapp(
packages = setOf("net.corda.contracts.djvm.attachment"),
name = "sandbox-attachment-contract",
signingInfo = CustomCordapp.SigningInfo()
)
),
djvmBootstrapSource = djvmSources.bootstrap,
djvmCordaSource = djvmSources.corda
)
}
}
@Test
fun `test attachment accessible within sandbox`() {
val extractFile = ExtractFile(SandboxAttachmentContract::class.java.name.asResourcePath + ".class")
driver(parametersFor(djvmSources)) {
val alice = startNode(providedName = ALICE_NAME).getOrThrow()
val txId = assertDoesNotThrow {
alice.rpc.startFlow(::SandboxAttachmentFlow, extractFile)
.returnValue.getOrThrow()
}
logger.info("TX-ID: {}", txId)
}
}
@Test
fun `test attachment file not found within sandbox`() {
val extractFile = ExtractFile("does/not/Exist.class")
driver(parametersFor(djvmSources)) {
val alice = startNode(providedName = ALICE_NAME).getOrThrow()
val ex = assertThrows<DeterministicVerificationException> {
alice.rpc.startFlow(::SandboxAttachmentFlow, extractFile)
.returnValue.getOrThrow()
}
assertThat(ex)
.hasMessageStartingWith("sandbox.net.corda.core.contracts.TransactionVerificationException\$ContractRejection -> ")
.hasMessageContaining(" Contract verification failed: does/not/Exist.class, " )
.hasMessageContaining(" contract: sandbox.net.corda.contracts.djvm.attachment.SandboxAttachmentContract, ")
}
}
}