From c955093ce85bfa8d7f5146c00aba4b7fb7c16244 Mon Sep 17 00:00:00 2001 From: Chris Rankin Date: Fri, 28 Jan 2022 17:05:59 +0000 Subject: [PATCH 1/5] Validate LedgerTransaction deserialised from AttachmentsClassLoader. (#7049) --- .../transactions/TransactionTests.kt | 8 +- .../corda/core/internal/ConstraintsUtils.kt | 9 +- .../TransactionVerifierServiceInternal.kt | 82 +++++++++++++++---- .../core/transactions/LedgerTransaction.kt | 31 +++---- .../internal/internalAccessTestHelpers.kt | 14 +++- .../net/corda/node/djvm/AttachmentBuilder.kt | 28 ++++++- .../contracts/mutator/MutatorContract.kt | 8 +- .../net/corda/node/CashIssueAndPaymentTest.kt | 6 +- .../ContractCannotMutateTransactionTest.kt | 4 +- .../node/ContractWithCordappFixupTest.kt | 6 +- .../node/ContractWithCustomSerializerTest.kt | 2 +- .../corda/node/ContractWithGenericTypeTest.kt | 6 +- ...ContractWithMissingCustomSerializerTest.kt | 2 +- .../ContractWithSerializationWhitelistTest.kt | 2 +- .../DeterministicCashIssueAndPaymentTest.kt | 6 +- ...sticContractCannotMutateTransactionTest.kt | 4 +- .../DeterministicContractCryptoTest.kt | 6 +- ...inisticContractWithCustomSerializerTest.kt | 10 +-- ...eterministicContractWithGenericTypeTest.kt | 6 +- ...cContractWithSerializationWhitelistTest.kt | 10 +-- ...isticEvilContractCannotModifyStatesTest.kt | 4 +- .../NonDeterministicContractVerifyTest.kt | 6 +- .../node/services/SandboxAttachmentsTest.kt | 6 +- .../node/internal/djvm/AttachmentFactory.kt | 31 +++++-- .../internal/djvm/DeterministicVerifier.kt | 19 +++-- 25 files changed, 212 insertions(+), 104 deletions(-) diff --git a/core-tests/src/test/kotlin/net/corda/coretests/transactions/TransactionTests.kt b/core-tests/src/test/kotlin/net/corda/coretests/transactions/TransactionTests.kt index 47d4171c1d..62254d6b4e 100644 --- a/core-tests/src/test/kotlin/net/corda/coretests/transactions/TransactionTests.kt +++ b/core-tests/src/test/kotlin/net/corda/coretests/transactions/TransactionTests.kt @@ -21,6 +21,7 @@ import net.corda.testing.internal.createWireTransaction import net.corda.testing.internal.fakeAttachment import net.corda.coretesting.internal.rigorousMock import net.corda.testing.internal.TestingNamedCacheFactory +import org.assertj.core.api.Assertions.fail import org.junit.Before import org.junit.Rule import org.junit.Test @@ -36,6 +37,7 @@ import kotlin.test.assertNotEquals @RunWith(Parameterized::class) class TransactionTests(private val digestService : DigestService) { private companion object { + const val ISOLATED_JAR = "isolated-4.0.jar" val DUMMY_KEY_1 = generateKeyPair() val DUMMY_KEY_2 = generateKeyPair() val DUMMY_CASH_ISSUER_KEY = entropyToKeyPair(BigInteger.valueOf(10)) @@ -200,15 +202,15 @@ class TransactionTests(private val digestService : DigestService) { val outputs = listOf(outState) val commands = emptyList>() - val attachments = listOf(object : AbstractAttachment({ - AttachmentsClassLoaderTests::class.java.getResource("isolated-4.0.jar").openStream().readBytes() + val attachments = listOf(ContractAttachment(object : AbstractAttachment({ + (AttachmentsClassLoaderTests::class.java.getResource(ISOLATED_JAR) ?: fail("Missing $ISOLATED_JAR")).openStream().readBytes() }, TESTDSL_UPLOADER) { @Suppress("OverridingDeprecatedMember") override val signers: List = emptyList() override val signerKeys: List = emptyList() override val size: Int = 1234 override val id: SecureHash = SecureHash.zeroHash - }) + }, DummyContract.PROGRAM_ID)) val id = digestService.randomHash() val timeWindow: TimeWindow? = null val privacySalt = PrivacySalt(digestService.digestLength) diff --git a/core/src/main/kotlin/net/corda/core/internal/ConstraintsUtils.kt b/core/src/main/kotlin/net/corda/core/internal/ConstraintsUtils.kt index c05ae94680..2cdb80fad1 100644 --- a/core/src/main/kotlin/net/corda/core/internal/ConstraintsUtils.kt +++ b/core/src/main/kotlin/net/corda/core/internal/ConstraintsUtils.kt @@ -16,6 +16,7 @@ typealias Version = Int * Attention: this value affects consensus, so it requires a minimum platform version bump in order to be changed. */ const val MAX_NUMBER_OF_KEYS_IN_SIGNATURE_CONSTRAINT = 20 +private const val DJVM_SANDBOX_PREFIX = "sandbox." private val log = loggerFor() @@ -29,10 +30,14 @@ val Attachment.contractVersion: Version get() = if (this is ContractAttachment) val ContractState.requiredContractClassName: String? get() { val annotation = javaClass.getAnnotation(BelongsToContract::class.java) if (annotation != null) { - return annotation.value.java.typeName + return annotation.value.java.typeName.removePrefix(DJVM_SANDBOX_PREFIX) } val enclosingClass = javaClass.enclosingClass ?: return null - return if (Contract::class.java.isAssignableFrom(enclosingClass)) enclosingClass.typeName else null + return if (Contract::class.java.isAssignableFrom(enclosingClass)) { + enclosingClass.typeName.removePrefix(DJVM_SANDBOX_PREFIX) + } else { + null + } } /** diff --git a/core/src/main/kotlin/net/corda/core/internal/TransactionVerifierServiceInternal.kt b/core/src/main/kotlin/net/corda/core/internal/TransactionVerifierServiceInternal.kt index 2a8c13036e..58d647af6f 100644 --- a/core/src/main/kotlin/net/corda/core/internal/TransactionVerifierServiceInternal.kt +++ b/core/src/main/kotlin/net/corda/core/internal/TransactionVerifierServiceInternal.kt @@ -13,6 +13,7 @@ import net.corda.core.contracts.SignatureAttachmentConstraint import net.corda.core.contracts.StateAndRef import net.corda.core.contracts.StateRef import net.corda.core.contracts.TransactionState +import net.corda.core.contracts.TransactionVerificationException import net.corda.core.contracts.TransactionVerificationException.ConflictingAttachmentsRejection import net.corda.core.contracts.TransactionVerificationException.ConstraintPropagationRejection import net.corda.core.contracts.TransactionVerificationException.ContractCreationError @@ -33,7 +34,7 @@ import net.corda.core.crypto.CompositeKey import net.corda.core.crypto.SecureHash import net.corda.core.internal.rules.StateContractValidationEnforcementRule import net.corda.core.transactions.LedgerTransaction -import net.corda.core.utilities.contextLogger +import net.corda.core.utilities.loggerFor import java.util.function.Function import java.util.function.Supplier @@ -47,16 +48,54 @@ interface TransactionVerifierServiceInternal { */ fun LedgerTransaction.prepareVerify(attachments: List) = internalPrepareVerify(attachments) +interface Verifier { + + /** + * Placeholder function for the verification logic. + */ + fun verify() +} + +// This class allows us unit-test transaction verification more easily. +abstract class AbstractVerifier( + protected val ltx: LedgerTransaction, + protected val transactionClassLoader: ClassLoader +) : Verifier { + protected abstract val transaction: Supplier + + protected companion object { + @JvmField + val logger = loggerFor() + } + + /** + * Check that the transaction is internally consistent, and then check that it is + * contract-valid by running verify() for each input and output state contract. + * If any contract fails to verify, the whole transaction is considered to be invalid. + * + * Note: Reference states are not verified. + */ + final override fun verify() { + try { + TransactionVerifier(transactionClassLoader).apply(transaction) + } catch (e: TransactionVerificationException) { + logger.error("Error validating transaction ${ltx.id}.", e.cause) + throw e + } + } +} + /** * Because we create a separate [LedgerTransaction] onto which we need to perform verification, it becomes important we don't verify the * wrong object instance. This class helps avoid that. */ -abstract class Verifier(val ltx: LedgerTransaction, protected val transactionClassLoader: ClassLoader) { +@KeepForDJVM +private class Validator(private val ltx: LedgerTransaction, private val transactionClassLoader: ClassLoader) { private val inputStates: List> = ltx.inputs.map(StateAndRef::state) private val allStates: List> = inputStates + ltx.references.map(StateAndRef::state) + ltx.outputs - companion object { - val logger = contextLogger() + private companion object { + private val logger = loggerFor() } /** @@ -66,7 +105,7 @@ abstract class Verifier(val ltx: LedgerTransaction, protected val transactionCla * * @throws net.corda.core.contracts.TransactionVerificationException */ - fun verify() { + fun validate() { // checkNoNotaryChange and checkEncumbrancesValid are called here, and not in the c'tor, as they need access to the "outputs" // list, the contents of which need to be deserialized under the correct classloader. checkNoNotaryChange() @@ -93,8 +132,7 @@ abstract class Verifier(val ltx: LedgerTransaction, protected val transactionCla // 4. Check that the [TransactionState] objects are correctly formed. validateStatesAgainstContract() - // 5. Final step is to run the contract code. After the first 4 steps we are now sure that we are running the correct code. - verifyContracts() + // 5. Final step will be to run the contract code. } private fun checkTransactionWithTimeWindowIsNotarised() { @@ -106,6 +144,7 @@ abstract class Verifier(val ltx: LedgerTransaction, protected val transactionCla * It makes sure there is one and only one. * This is an important piece of the security of transactions. */ + @Suppress("ThrowsCount") private fun getUniqueContractAttachmentsByContract(): Map { val contractClasses = allStates.mapTo(LinkedHashSet(), TransactionState<*>::contract) @@ -210,6 +249,7 @@ abstract class Verifier(val ltx: LedgerTransaction, protected val transactionCla // b -> c and c -> b // c -> a b -> a // and form a full cycle, meaning that the bi-directionality property is satisfied. + @Suppress("ThrowsCount") private fun checkBidirectionalOutputEncumbrances(statesAndEncumbrance: List>) { // [Set] of "from" (encumbered states). val encumberedSet = mutableSetOf() @@ -306,6 +346,7 @@ abstract class Verifier(val ltx: LedgerTransaction, protected val transactionCla * - Constraints should be one of the valid supported ones. * - Constraints should propagate correctly if not marked otherwise (in that case it is the responsibility of the contract to ensure that the output states are created properly). */ + @Suppress("NestedBlockDepth") private fun verifyConstraintsValidity(contractAttachmentsByContract: Map) { // First check that the constraints are valid. @@ -392,19 +433,15 @@ abstract class Verifier(val ltx: LedgerTransaction, protected val transactionCla throw ContractConstraintRejection(ltx.id, contract) } } - - /** - * Placeholder function for the contract verification logic. - */ - abstract fun verifyContracts() } /** - * Verify all of the contracts on the given [LedgerTransaction]. + * Verify the given [LedgerTransaction]. This includes validating + * its contents, as well as executing all of its smart contracts. */ @Suppress("TooGenericExceptionCaught") @KeepForDJVM -class ContractVerifier(private val transactionClassLoader: ClassLoader) : Function, Unit> { +class TransactionVerifier(private val transactionClassLoader: ClassLoader) : Function, Unit> { // This constructor is used inside the DJVM's sandbox. @Suppress("unused") constructor() : this(ClassLoader.getSystemClassLoader()) @@ -440,16 +477,33 @@ class ContractVerifier(private val transactionClassLoader: ClassLoader) : Functi } } + private fun validateTransaction(ltx: LedgerTransaction) { + Validator(ltx, transactionClassLoader).validate() + } + override fun apply(transactionFactory: Supplier) { var firstLtx: LedgerTransaction? = null transactionFactory.get().let { ltx -> firstLtx = ltx + + /** + * Check that this transaction is correctly formed. + * We only need to run these checks once. + */ + validateTransaction(ltx) + + /** + * Generate the list of unique contracts + * within this transaction. + */ generateContracts(ltx) }.forEach { contract -> val ltx = firstLtx ?: transactionFactory.get() firstLtx = null try { + // Final step is to run the contract code. Having validated the + // transaction, we are now sure that we are running the correct code. contract.verify(ltx) } catch (e: Exception) { throw ContractRejection(ltx.id, contract, e) diff --git a/core/src/main/kotlin/net/corda/core/transactions/LedgerTransaction.kt b/core/src/main/kotlin/net/corda/core/transactions/LedgerTransaction.kt index c7b00f60bd..25dfa7f293 100644 --- a/core/src/main/kotlin/net/corda/core/transactions/LedgerTransaction.kt +++ b/core/src/main/kotlin/net/corda/core/transactions/LedgerTransaction.kt @@ -18,7 +18,7 @@ import net.corda.core.crypto.DigestService import net.corda.core.crypto.SecureHash import net.corda.core.flows.FlowLogic import net.corda.core.identity.Party -import net.corda.core.internal.ContractVerifier +import net.corda.core.internal.AbstractVerifier import net.corda.core.internal.SerializedStateAndRef import net.corda.core.internal.Verifier import net.corda.core.internal.castIfPossible @@ -824,7 +824,7 @@ private constructor( private class BasicVerifier( ltx: LedgerTransaction, private val serializationContext: SerializationContext -) : Verifier(ltx, serializationContext.deserializationClassLoader) { +) : AbstractVerifier(ltx, serializationContext.deserializationClassLoader) { init { // This is a sanity check: We should only instantiate this @@ -836,9 +836,15 @@ private class BasicVerifier( // Fetch these commands' signing parties from the database. // Corda forbids database access during contract verification, // and so we must load the commands here eagerly instead. + // THIS ALSO DESERIALISES THE COMMANDS USING THE WRONG CONTEXT + // BECAUSE THAT CONTEXT WAS CHOSEN WHEN THE LAZY MAP WAS CREATED, + // AND CHANGING THE DEFAULT CONTEXT HERE DOES NOT AFFECT IT. ltx.commands.eagerDeserialise() } + override val transaction: Supplier + get() = Supplier(::createTransaction) + private fun createTransaction(): LedgerTransaction { // Deserialize all relevant classes using the serializationContext. return SerializationFactory.defaultFactory.withCurrentContext(serializationContext) { @@ -870,21 +876,6 @@ private class BasicVerifier( } } } - - /** - * Check the transaction is contract-valid by running verify() for each input and output state contract. - * If any contract fails to verify, the whole transaction is considered to be invalid. - * - * Note: Reference states are not verified. - */ - override fun verifyContracts() { - try { - ContractVerifier(transactionClassLoader).apply(Supplier(::createTransaction)) - } catch (e: TransactionVerificationException) { - logger.error("Error validating transaction ${ltx.id}.", e.cause) - throw e - } - } } /** @@ -892,10 +883,10 @@ private class BasicVerifier( * * THIS CLASS IS NOT PUBLIC API, AND IS DELIBERATELY PRIVATE! */ +@Suppress("unused_parameter") @CordaInternal -private class NoOpVerifier(ltx: LedgerTransaction, serializationContext: SerializationContext) - : Verifier(ltx, serializationContext.deserializationClassLoader) { +private class NoOpVerifier(ltx: LedgerTransaction, serializationContext: SerializationContext) : Verifier { // Invoking LedgerTransaction.verify() from Contract.verify(LedgerTransaction) // will execute this function. But why would anyone do that?! - override fun verifyContracts() {} + override fun verify() {} } diff --git a/core/src/test/kotlin/net/corda/core/internal/internalAccessTestHelpers.kt b/core/src/test/kotlin/net/corda/core/internal/internalAccessTestHelpers.kt index 1a6f880f1d..16a6e6bef8 100644 --- a/core/src/test/kotlin/net/corda/core/internal/internalAccessTestHelpers.kt +++ b/core/src/test/kotlin/net/corda/core/internal/internalAccessTestHelpers.kt @@ -5,10 +5,12 @@ import net.corda.core.crypto.DigestService import net.corda.core.crypto.SecureHash import net.corda.core.identity.Party import net.corda.core.node.NetworkParameters +import net.corda.core.serialization.SerializationContext import net.corda.core.serialization.internal.AttachmentsClassLoaderCache import net.corda.core.transactions.ComponentGroup import net.corda.core.transactions.LedgerTransaction import net.corda.core.transactions.WireTransaction +import java.util.function.Supplier /** * A set of functions in core:test that allows testing of core internal classes in the core-tests project. @@ -38,7 +40,17 @@ fun createLedgerTransaction( isAttachmentTrusted: (Attachment) -> Boolean, attachmentsClassLoaderCache: AttachmentsClassLoaderCache, digestService: DigestService = DigestService.default -): LedgerTransaction = LedgerTransaction.create(inputs, outputs, commands, attachments, id, notary, timeWindow, privacySalt, networkParameters, references, componentGroups, serializedInputs, serializedReferences, isAttachmentTrusted, attachmentsClassLoaderCache, digestService) +): LedgerTransaction = LedgerTransaction.create( + inputs, outputs, commands, attachments, id, notary, timeWindow, privacySalt, networkParameters, references, componentGroups, serializedInputs, serializedReferences, isAttachmentTrusted, attachmentsClassLoaderCache, digestService +).specialise(::PassthroughVerifier) fun createContractCreationError(txId: SecureHash, contractClass: String, cause: Throwable) = TransactionVerificationException.ContractCreationError(txId, contractClass, cause) fun createContractRejection(txId: SecureHash, contract: Contract, cause: Throwable) = TransactionVerificationException.ContractRejection(txId, contract, cause) + +/** + * Verify the [LedgerTransaction] we already have. + */ +private class PassthroughVerifier(ltx: LedgerTransaction, context: SerializationContext) : AbstractVerifier(ltx, context.deserializationClassLoader) { + override val transaction: Supplier + get() = Supplier { ltx } +} diff --git a/node/djvm/src/main/kotlin/net/corda/node/djvm/AttachmentBuilder.kt b/node/djvm/src/main/kotlin/net/corda/node/djvm/AttachmentBuilder.kt index f3a205ba38..561b5bcd76 100644 --- a/node/djvm/src/main/kotlin/net/corda/node/djvm/AttachmentBuilder.kt +++ b/node/djvm/src/main/kotlin/net/corda/node/djvm/AttachmentBuilder.kt @@ -3,6 +3,7 @@ package net.corda.node.djvm import net.corda.core.contracts.Attachment import net.corda.core.contracts.BrokenAttachmentException +import net.corda.core.contracts.ContractAttachment import net.corda.core.crypto.SecureHash import net.corda.core.identity.Party import java.io.InputStream @@ -16,6 +17,12 @@ private const val ID_IDX = 2 private const val ATTACHMENT_IDX = 3 private const val STREAMER_IDX = 4 +private const val CONTRACT_IDX = 5 +private const val ADDITIONAL_CONTRACT_IDX = 6 +private const val UPLOADER_IDX = 7 +private const val CONTRACT_SIGNER_KEYS_IDX = 8 +private const val VERSION_IDX = 9 + class AttachmentBuilder : Function?, List?> { private val attachments = mutableListOf() @@ -28,17 +35,30 @@ class AttachmentBuilder : Function?, List?> { } override fun apply(inputs: Array?): List? { + @Suppress("unchecked_cast") return if (inputs == null) { unmodifiable(attachments) } else { - @Suppress("unchecked_cast") - attachments.add(SandboxAttachment( + var attachment: Attachment = SandboxAttachment( signerKeys = inputs[SIGNER_KEYS_IDX] as List, size = inputs[SIZE_IDX] as Int, id = inputs[ID_IDX] as SecureHash, attachment = inputs[ATTACHMENT_IDX], streamer = inputs[STREAMER_IDX] as Function - )) + ) + + if (inputs.size > VERSION_IDX) { + attachment = ContractAttachment.create( + attachment = attachment, + contract = inputs[CONTRACT_IDX] as String, + additionalContracts = (inputs[ADDITIONAL_CONTRACT_IDX] as Array).toSet(), + uploader = inputs[UPLOADER_IDX] as? String, + signerKeys = inputs[CONTRACT_SIGNER_KEYS_IDX] as List, + version = inputs[VERSION_IDX] as Int + ) + } + + attachments.add(attachment) null } } @@ -47,7 +67,7 @@ class AttachmentBuilder : Function?, List?> { /** * This represents an [Attachment] from within the sandbox. */ -class SandboxAttachment( +private class SandboxAttachment( override val signerKeys: List, override val size: Int, override val id: SecureHash, diff --git a/node/src/integration-test/kotlin/net/corda/contracts/mutator/MutatorContract.kt b/node/src/integration-test/kotlin/net/corda/contracts/mutator/MutatorContract.kt index cffcf18b3d..239525c576 100644 --- a/node/src/integration-test/kotlin/net/corda/contracts/mutator/MutatorContract.kt +++ b/node/src/integration-test/kotlin/net/corda/contracts/mutator/MutatorContract.kt @@ -95,9 +95,11 @@ class MutatorContract : Contract { } } - private class ExtraSpecialise(ltx: LedgerTransaction, ctx: SerializationContext) - : Verifier(ltx, ctx.deserializationClassLoader) { - override fun verifyContracts() {} + private class ExtraSpecialise(private val ltx: LedgerTransaction, private val ctx: SerializationContext) : Verifier { + override fun verify() { + ltx.inputStates.forEach(::println) + println(ctx.deserializationClassLoader) + } } class MutateState(val owner: AbstractParty) : ContractState { diff --git a/node/src/integration-test/kotlin/net/corda/node/CashIssueAndPaymentTest.kt b/node/src/integration-test/kotlin/net/corda/node/CashIssueAndPaymentTest.kt index 399e1d9c2c..2da38e1509 100644 --- a/node/src/integration-test/kotlin/net/corda/node/CashIssueAndPaymentTest.kt +++ b/node/src/integration-test/kotlin/net/corda/node/CashIssueAndPaymentTest.kt @@ -30,12 +30,12 @@ class CashIssueAndPaymentTest { private val configOverrides = mapOf(NodeConfiguration::reloadCheckpointAfterSuspend.name to true) private val CASH_AMOUNT = 500.DOLLARS - fun parametersFor(): DriverParameters { + fun parametersFor(runInProcess: Boolean = false): DriverParameters { return DriverParameters( systemProperties = mapOf("co.paralleluniverse.fibers.verifyInstrumentation" to "false"), portAllocation = incrementalPortAllocation(), - startNodesInProcess = false, - notarySpecs = listOf(NotarySpec(DUMMY_NOTARY_NAME, startInProcess = false, validating = true)), + startNodesInProcess = runInProcess, + notarySpecs = listOf(NotarySpec(DUMMY_NOTARY_NAME, startInProcess = runInProcess, validating = true)), notaryCustomOverrides = configOverrides, cordappsForAllNodes = listOf( findCordapp("net.corda.finance.contracts"), diff --git a/node/src/integration-test/kotlin/net/corda/node/ContractCannotMutateTransactionTest.kt b/node/src/integration-test/kotlin/net/corda/node/ContractCannotMutateTransactionTest.kt index eecfe203ec..62a92dc14f 100644 --- a/node/src/integration-test/kotlin/net/corda/node/ContractCannotMutateTransactionTest.kt +++ b/node/src/integration-test/kotlin/net/corda/node/ContractCannotMutateTransactionTest.kt @@ -23,7 +23,7 @@ class ContractCannotMutateTransactionTest { private val mutatorFlowCorDapp = cordappWithPackages("net.corda.flows.mutator").signed() private val mutatorContractCorDapp = cordappWithPackages("net.corda.contracts.mutator").signed() - fun driverParameters(runInProcess: Boolean): DriverParameters { + fun driverParameters(runInProcess: Boolean = false): DriverParameters { return DriverParameters( portAllocation = incrementalPortAllocation(), startNodesInProcess = runInProcess, @@ -35,7 +35,7 @@ class ContractCannotMutateTransactionTest { @Test(timeout = 300_000) fun testContractCannotModifyTransaction() { - driver(driverParameters(runInProcess = false)) { + driver(driverParameters()) { val alice = startNode(providedName = ALICE_NAME, rpcUsers = listOf(user)).getOrThrow() val txID = CordaRPCClient(hostAndPort = alice.rpcAddress) .start(user.username, user.password) diff --git a/node/src/integration-test/kotlin/net/corda/node/ContractWithCordappFixupTest.kt b/node/src/integration-test/kotlin/net/corda/node/ContractWithCordappFixupTest.kt index 50e5b1b1bd..77f267aed7 100644 --- a/node/src/integration-test/kotlin/net/corda/node/ContractWithCordappFixupTest.kt +++ b/node/src/integration-test/kotlin/net/corda/node/ContractWithCordappFixupTest.kt @@ -35,11 +35,11 @@ class ContractWithCordappFixupTest { val dependentContractCorDapp = cordappWithPackages("net.corda.contracts.fixup.dependent").signed() val standaloneContractCorDapp = cordappWithPackages("net.corda.contracts.fixup.standalone").signed() - fun driverParameters(cordapps: List): DriverParameters { + fun driverParameters(cordapps: List, runInProcess: Boolean = false): DriverParameters { return DriverParameters( portAllocation = incrementalPortAllocation(), - startNodesInProcess = false, - notarySpecs = listOf(NotarySpec(DUMMY_NOTARY_NAME, validating = true)), + startNodesInProcess = runInProcess, + notarySpecs = listOf(NotarySpec(DUMMY_NOTARY_NAME, startInProcess = runInProcess, validating = true)), cordappsForAllNodes = cordapps, systemProperties = mapOf("net.corda.transactionbuilder.missingclass.disabled" to true.toString()) ) diff --git a/node/src/integration-test/kotlin/net/corda/node/ContractWithCustomSerializerTest.kt b/node/src/integration-test/kotlin/net/corda/node/ContractWithCustomSerializerTest.kt index ffb2d297b1..442214e13e 100644 --- a/node/src/integration-test/kotlin/net/corda/node/ContractWithCustomSerializerTest.kt +++ b/node/src/integration-test/kotlin/net/corda/node/ContractWithCustomSerializerTest.kt @@ -46,7 +46,7 @@ class ContractWithCustomSerializerTest(private val runInProcess: Boolean) { driver(DriverParameters( portAllocation = incrementalPortAllocation(), startNodesInProcess = runInProcess, - notarySpecs = listOf(NotarySpec(DUMMY_NOTARY_NAME, validating = true)), + notarySpecs = listOf(NotarySpec(DUMMY_NOTARY_NAME, startInProcess = runInProcess, validating = true)), cordappsForAllNodes = listOf( cordappWithPackages("net.corda.flows.serialization.custom").signed(), cordappWithPackages("net.corda.contracts.serialization.custom").signed() diff --git a/node/src/integration-test/kotlin/net/corda/node/ContractWithGenericTypeTest.kt b/node/src/integration-test/kotlin/net/corda/node/ContractWithGenericTypeTest.kt index d23c137dda..4dfb1f17e6 100644 --- a/node/src/integration-test/kotlin/net/corda/node/ContractWithGenericTypeTest.kt +++ b/node/src/integration-test/kotlin/net/corda/node/ContractWithGenericTypeTest.kt @@ -31,11 +31,11 @@ class ContractWithGenericTypeTest { @JvmField val user = User("u", "p", setOf(Permissions.all())) - fun parameters(): DriverParameters { + fun parameters(runInProcess: Boolean = false): DriverParameters { return DriverParameters( portAllocation = incrementalPortAllocation(), - startNodesInProcess = false, - notarySpecs = listOf(NotarySpec(DUMMY_NOTARY_NAME, validating = true)), + startNodesInProcess = runInProcess, + notarySpecs = listOf(NotarySpec(DUMMY_NOTARY_NAME, startInProcess = runInProcess, validating = true)), cordappsForAllNodes = listOf( cordappWithPackages("net.corda.flows.serialization.generics").signed(), cordappWithPackages("net.corda.contracts.serialization.generics").signed() diff --git a/node/src/integration-test/kotlin/net/corda/node/ContractWithMissingCustomSerializerTest.kt b/node/src/integration-test/kotlin/net/corda/node/ContractWithMissingCustomSerializerTest.kt index 2110ff3cfe..78ee896844 100644 --- a/node/src/integration-test/kotlin/net/corda/node/ContractWithMissingCustomSerializerTest.kt +++ b/node/src/integration-test/kotlin/net/corda/node/ContractWithMissingCustomSerializerTest.kt @@ -45,7 +45,7 @@ class ContractWithMissingCustomSerializerTest(private val runInProcess: Boolean) return DriverParameters( portAllocation = incrementalPortAllocation(), startNodesInProcess = runInProcess, - notarySpecs = listOf(NotarySpec(DUMMY_NOTARY_NAME, validating = true)), + notarySpecs = listOf(NotarySpec(DUMMY_NOTARY_NAME, startInProcess = runInProcess, validating = true)), cordappsForAllNodes = cordapps ) } diff --git a/node/src/integration-test/kotlin/net/corda/node/ContractWithSerializationWhitelistTest.kt b/node/src/integration-test/kotlin/net/corda/node/ContractWithSerializationWhitelistTest.kt index 2a9ae80195..9c6d809d77 100644 --- a/node/src/integration-test/kotlin/net/corda/node/ContractWithSerializationWhitelistTest.kt +++ b/node/src/integration-test/kotlin/net/corda/node/ContractWithSerializationWhitelistTest.kt @@ -43,7 +43,7 @@ class ContractWithSerializationWhitelistTest(private val runInProcess: Boolean) return DriverParameters( portAllocation = incrementalPortAllocation(), startNodesInProcess = runInProcess, - notarySpecs = listOf(NotarySpec(DUMMY_NOTARY_NAME, validating = true)), + notarySpecs = listOf(NotarySpec(DUMMY_NOTARY_NAME, startInProcess = runInProcess, validating = true)), cordappsForAllNodes = listOf(contractCordapp, workflowCordapp) ) } diff --git a/node/src/integration-test/kotlin/net/corda/node/services/DeterministicCashIssueAndPaymentTest.kt b/node/src/integration-test/kotlin/net/corda/node/services/DeterministicCashIssueAndPaymentTest.kt index 4997cac5e3..9b3bdf77ef 100644 --- a/node/src/integration-test/kotlin/net/corda/node/services/DeterministicCashIssueAndPaymentTest.kt +++ b/node/src/integration-test/kotlin/net/corda/node/services/DeterministicCashIssueAndPaymentTest.kt @@ -32,11 +32,11 @@ class DeterministicCashIssueAndPaymentTest { @JvmField val djvmSources = DeterministicSourcesRule() - fun parametersFor(djvmSources: DeterministicSourcesRule): DriverParameters { + fun parametersFor(djvmSources: DeterministicSourcesRule, runInProcess: Boolean = false): DriverParameters { return DriverParameters( portAllocation = incrementalPortAllocation(), - startNodesInProcess = false, - notarySpecs = listOf(NotarySpec(DUMMY_NOTARY_NAME, validating = true)), + startNodesInProcess = runInProcess, + notarySpecs = listOf(NotarySpec(DUMMY_NOTARY_NAME, startInProcess = runInProcess, validating = true)), notaryCustomOverrides = configOverrides, cordappsForAllNodes = listOf( findCordapp("net.corda.finance.contracts"), diff --git a/node/src/integration-test/kotlin/net/corda/node/services/DeterministicContractCannotMutateTransactionTest.kt b/node/src/integration-test/kotlin/net/corda/node/services/DeterministicContractCannotMutateTransactionTest.kt index 68b8c3531c..41c80ea7d9 100644 --- a/node/src/integration-test/kotlin/net/corda/node/services/DeterministicContractCannotMutateTransactionTest.kt +++ b/node/src/integration-test/kotlin/net/corda/node/services/DeterministicContractCannotMutateTransactionTest.kt @@ -28,7 +28,7 @@ class DeterministicContractCannotMutateTransactionTest { @JvmField val djvmSources = DeterministicSourcesRule() - fun driverParameters(runInProcess: Boolean): DriverParameters { + fun driverParameters(runInProcess: Boolean = false): DriverParameters { return DriverParameters( portAllocation = incrementalPortAllocation(), startNodesInProcess = runInProcess, @@ -42,7 +42,7 @@ class DeterministicContractCannotMutateTransactionTest { @Test(timeout = 300_000) fun testContractCannotModifyTransaction() { - driver(driverParameters(runInProcess = false)) { + driver(driverParameters()) { val alice = startNode(providedName = ALICE_NAME, rpcUsers = listOf(user)).getOrThrow() val txID = CordaRPCClient(hostAndPort = alice.rpcAddress) .start(user.username, user.password) diff --git a/node/src/integration-test/kotlin/net/corda/node/services/DeterministicContractCryptoTest.kt b/node/src/integration-test/kotlin/net/corda/node/services/DeterministicContractCryptoTest.kt index 275693f4d7..5d28ae41b8 100644 --- a/node/src/integration-test/kotlin/net/corda/node/services/DeterministicContractCryptoTest.kt +++ b/node/src/integration-test/kotlin/net/corda/node/services/DeterministicContractCryptoTest.kt @@ -32,11 +32,11 @@ class DeterministicContractCryptoTest { @JvmField val djvmSources = DeterministicSourcesRule() - fun parametersFor(djvmSources: DeterministicSourcesRule): DriverParameters { + fun parametersFor(djvmSources: DeterministicSourcesRule, runInProcess: Boolean = false): DriverParameters { return DriverParameters( portAllocation = incrementalPortAllocation(), - startNodesInProcess = false, - notarySpecs = listOf(NotarySpec(DUMMY_NOTARY_NAME, validating = true)), + startNodesInProcess = runInProcess, + notarySpecs = listOf(NotarySpec(DUMMY_NOTARY_NAME, startInProcess = runInProcess, validating = true)), cordappsForAllNodes = listOf( cordappWithPackages("net.corda.flows.djvm.crypto"), CustomCordapp( diff --git a/node/src/integration-test/kotlin/net/corda/node/services/DeterministicContractWithCustomSerializerTest.kt b/node/src/integration-test/kotlin/net/corda/node/services/DeterministicContractWithCustomSerializerTest.kt index 3630fbcf3c..447cd2d6a6 100644 --- a/node/src/integration-test/kotlin/net/corda/node/services/DeterministicContractWithCustomSerializerTest.kt +++ b/node/src/integration-test/kotlin/net/corda/node/services/DeterministicContractWithCustomSerializerTest.kt @@ -41,11 +41,11 @@ class DeterministicContractWithCustomSerializerTest { @JvmField val contractCordapp = cordappWithPackages("net.corda.contracts.serialization.custom").signed() - fun parametersFor(djvmSources: DeterministicSourcesRule, vararg cordapps: TestCordapp): DriverParameters { + fun parametersFor(djvmSources: DeterministicSourcesRule, cordapps: List, runInProcess: Boolean = false): DriverParameters { return DriverParameters( portAllocation = incrementalPortAllocation(), - startNodesInProcess = false, - notarySpecs = listOf(NotarySpec(DUMMY_NOTARY_NAME, validating = true)), + startNodesInProcess = runInProcess, + notarySpecs = listOf(NotarySpec(DUMMY_NOTARY_NAME, startInProcess = runInProcess, validating = true)), cordappsForAllNodes = cordapps.toList(), djvmBootstrapSource = djvmSources.bootstrap, djvmCordaSource = djvmSources.corda @@ -61,7 +61,7 @@ class DeterministicContractWithCustomSerializerTest { @Test(timeout=300_000) fun `test DJVM can verify using custom serializer`() { - driver(parametersFor(djvmSources, flowCordapp, contractCordapp)) { + driver(parametersFor(djvmSources, listOf(flowCordapp, contractCordapp))) { val alice = startNode(providedName = ALICE_NAME).getOrThrow() val txId = assertDoesNotThrow { alice.rpc.startFlow(::CustomSerializerFlow, Currantsy(GOOD_CURRANTS)) @@ -73,7 +73,7 @@ class DeterministicContractWithCustomSerializerTest { @Test(timeout=300_000) fun `test DJVM can fail verify using custom serializer`() { - driver(parametersFor(djvmSources, flowCordapp, contractCordapp)) { + driver(parametersFor(djvmSources, listOf(flowCordapp, contractCordapp))) { val alice = startNode(providedName = ALICE_NAME).getOrThrow() val currantsy = Currantsy(BAD_CURRANTS) val ex = assertThrows { diff --git a/node/src/integration-test/kotlin/net/corda/node/services/DeterministicContractWithGenericTypeTest.kt b/node/src/integration-test/kotlin/net/corda/node/services/DeterministicContractWithGenericTypeTest.kt index c3c440eaf6..d2cae60136 100644 --- a/node/src/integration-test/kotlin/net/corda/node/services/DeterministicContractWithGenericTypeTest.kt +++ b/node/src/integration-test/kotlin/net/corda/node/services/DeterministicContractWithGenericTypeTest.kt @@ -36,11 +36,11 @@ class DeterministicContractWithGenericTypeTest { @JvmField val djvmSources = DeterministicSourcesRule() - fun parameters(): DriverParameters { + fun parameters(runInProcess: Boolean = false): DriverParameters { return DriverParameters( portAllocation = incrementalPortAllocation(), - startNodesInProcess = false, - notarySpecs = listOf(NotarySpec(DUMMY_NOTARY_NAME, validating = true)), + startNodesInProcess = runInProcess, + notarySpecs = listOf(NotarySpec(DUMMY_NOTARY_NAME, startInProcess = runInProcess, validating = true)), cordappsForAllNodes = listOf( cordappWithPackages("net.corda.flows.serialization.generics").signed(), cordappWithPackages("net.corda.contracts.serialization.generics").signed() diff --git a/node/src/integration-test/kotlin/net/corda/node/services/DeterministicContractWithSerializationWhitelistTest.kt b/node/src/integration-test/kotlin/net/corda/node/services/DeterministicContractWithSerializationWhitelistTest.kt index 97ecbf014a..9b0e057453 100644 --- a/node/src/integration-test/kotlin/net/corda/node/services/DeterministicContractWithSerializationWhitelistTest.kt +++ b/node/src/integration-test/kotlin/net/corda/node/services/DeterministicContractWithSerializationWhitelistTest.kt @@ -41,11 +41,11 @@ class DeterministicContractWithSerializationWhitelistTest { @JvmField val contractCordapp = cordappWithPackages("net.corda.contracts.djvm.whitelist").signed() - fun parametersFor(djvmSources: DeterministicSourcesRule, vararg cordapps: TestCordapp): DriverParameters { + fun parametersFor(djvmSources: DeterministicSourcesRule, cordapps: List, runInProcess: Boolean = false): DriverParameters { return DriverParameters( portAllocation = incrementalPortAllocation(), - startNodesInProcess = false, - notarySpecs = listOf(NotarySpec(DUMMY_NOTARY_NAME, validating = true)), + startNodesInProcess = runInProcess, + notarySpecs = listOf(NotarySpec(DUMMY_NOTARY_NAME, startInProcess = runInProcess, validating = true)), cordappsForAllNodes = cordapps.toList(), djvmBootstrapSource = djvmSources.bootstrap, djvmCordaSource = djvmSources.corda @@ -61,7 +61,7 @@ class DeterministicContractWithSerializationWhitelistTest { @Test(timeout=300_000) fun `test DJVM can verify using whitelist`() { - driver(parametersFor(djvmSources, flowCordapp, contractCordapp)) { + driver(parametersFor(djvmSources, listOf(flowCordapp, contractCordapp))) { val alice = startNode(providedName = ALICE_NAME).getOrThrow() val txId = assertDoesNotThrow { alice.rpc.startFlow(::DeterministicWhitelistFlow, WhitelistData(GOOD_VALUE)) @@ -73,7 +73,7 @@ class DeterministicContractWithSerializationWhitelistTest { @Test(timeout=300_000) fun `test DJVM can fail verify using whitelist`() { - driver(parametersFor(djvmSources, flowCordapp, contractCordapp)) { + driver(parametersFor(djvmSources, listOf(flowCordapp, contractCordapp))) { val alice = startNode(providedName = ALICE_NAME).getOrThrow() val badData = WhitelistData(BAD_VALUE) val ex = assertThrows { diff --git a/node/src/integration-test/kotlin/net/corda/node/services/DeterministicEvilContractCannotModifyStatesTest.kt b/node/src/integration-test/kotlin/net/corda/node/services/DeterministicEvilContractCannotModifyStatesTest.kt index f2d455dce4..5188f0b4fd 100644 --- a/node/src/integration-test/kotlin/net/corda/node/services/DeterministicEvilContractCannotModifyStatesTest.kt +++ b/node/src/integration-test/kotlin/net/corda/node/services/DeterministicEvilContractCannotModifyStatesTest.kt @@ -34,7 +34,7 @@ class DeterministicEvilContractCannotModifyStatesTest { @JvmField val djvmSources = DeterministicSourcesRule() - fun driverParameters(runInProcess: Boolean): DriverParameters { + fun driverParameters(runInProcess: Boolean = false): DriverParameters { return DriverParameters( portAllocation = incrementalPortAllocation(), startNodesInProcess = runInProcess, @@ -53,7 +53,7 @@ class DeterministicEvilContractCannotModifyStatesTest { @Test(timeout = 300_000) fun testContractThatTriesToModifyStates() { val evilData = MutableDataObject(5000) - driver(driverParameters(runInProcess = false)) { + driver(driverParameters()) { val alice = startNode(providedName = ALICE_NAME, rpcUsers = listOf(user)).getOrThrow() val ex = assertFailsWith { CordaRPCClient(hostAndPort = alice.rpcAddress) diff --git a/node/src/integration-test/kotlin/net/corda/node/services/NonDeterministicContractVerifyTest.kt b/node/src/integration-test/kotlin/net/corda/node/services/NonDeterministicContractVerifyTest.kt index 264502e448..a200d5db41 100644 --- a/node/src/integration-test/kotlin/net/corda/node/services/NonDeterministicContractVerifyTest.kt +++ b/node/src/integration-test/kotlin/net/corda/node/services/NonDeterministicContractVerifyTest.kt @@ -35,11 +35,11 @@ class NonDeterministicContractVerifyTest { @JvmField val djvmSources = DeterministicSourcesRule() - fun parametersFor(djvmSources: DeterministicSourcesRule): DriverParameters { + fun parametersFor(djvmSources: DeterministicSourcesRule, runInProcess: Boolean = false): DriverParameters { return DriverParameters( portAllocation = incrementalPortAllocation(), - startNodesInProcess = false, - notarySpecs = listOf(NotarySpec(DUMMY_NOTARY_NAME, validating = true)), + startNodesInProcess = runInProcess, + notarySpecs = listOf(NotarySpec(DUMMY_NOTARY_NAME, startInProcess = runInProcess, validating = true)), cordappsForAllNodes = listOf( cordappWithPackages("net.corda.flows.djvm.broken"), CustomCordapp( diff --git a/node/src/integration-test/kotlin/net/corda/node/services/SandboxAttachmentsTest.kt b/node/src/integration-test/kotlin/net/corda/node/services/SandboxAttachmentsTest.kt index c825357581..e868566f58 100644 --- a/node/src/integration-test/kotlin/net/corda/node/services/SandboxAttachmentsTest.kt +++ b/node/src/integration-test/kotlin/net/corda/node/services/SandboxAttachmentsTest.kt @@ -31,11 +31,11 @@ class SandboxAttachmentsTest { @JvmField val djvmSources = DeterministicSourcesRule() - fun parametersFor(djvmSources: DeterministicSourcesRule): DriverParameters { + fun parametersFor(djvmSources: DeterministicSourcesRule, runInProcess: Boolean = false): DriverParameters { return DriverParameters( portAllocation = incrementalPortAllocation(), - startNodesInProcess = false, - notarySpecs = listOf(NotarySpec(DUMMY_NOTARY_NAME, validating = true)), + startNodesInProcess = runInProcess, + notarySpecs = listOf(NotarySpec(DUMMY_NOTARY_NAME, startInProcess = runInProcess, validating = true)), cordappsForAllNodes = listOf( cordappWithPackages("net.corda.flows.djvm.attachment"), CustomCordapp( diff --git a/node/src/main/kotlin/net/corda/node/internal/djvm/AttachmentFactory.kt b/node/src/main/kotlin/net/corda/node/internal/djvm/AttachmentFactory.kt index 9a616aa813..d272a7428e 100644 --- a/node/src/main/kotlin/net/corda/node/internal/djvm/AttachmentFactory.kt +++ b/node/src/main/kotlin/net/corda/node/internal/djvm/AttachmentFactory.kt @@ -1,6 +1,7 @@ package net.corda.node.internal.djvm import net.corda.core.contracts.Attachment +import net.corda.core.contracts.ContractAttachment import net.corda.core.serialization.serialize import net.corda.djvm.rewiring.SandboxClassLoader import net.corda.node.djvm.AttachmentBuilder @@ -19,14 +20,30 @@ class AttachmentFactory( fun toSandbox(attachments: List): Any? { val builder = taskFactory.apply(AttachmentBuilder::class.java) for (attachment in attachments) { - builder.apply(arrayOf( - serializer.deserialize(attachment.signerKeys.serialize()), - sandboxBasicInput.apply(attachment.size), - serializer.deserialize(attachment.id.serialize()), - attachment, - sandboxOpenAttachment - )) + builder.apply(generateArgsFor(attachment)) } return builder.apply(null) } + + private fun generateArgsFor(attachment: Attachment): Array { + val signerKeys = serializer.deserialize(attachment.signerKeys.serialize()) + val id = serializer.deserialize(attachment.id.serialize()) + val size = sandboxBasicInput.apply(attachment.size) + return if (attachment is ContractAttachment) { + val underlyingAttachment = attachment.attachment + arrayOf( + serializer.deserialize(underlyingAttachment.signerKeys.serialize()), + size, id, + underlyingAttachment, + sandboxOpenAttachment, + sandboxBasicInput.apply(attachment.contract), + sandboxBasicInput.apply(attachment.additionalContracts.toTypedArray()), + sandboxBasicInput.apply(attachment.uploader), + signerKeys, + sandboxBasicInput.apply(attachment.version) + ) + } else { + arrayOf(signerKeys, size, id, attachment, sandboxOpenAttachment) + } + } } diff --git a/node/src/main/kotlin/net/corda/node/internal/djvm/DeterministicVerifier.kt b/node/src/main/kotlin/net/corda/node/internal/djvm/DeterministicVerifier.kt index f1880fbf54..3263868aa8 100644 --- a/node/src/main/kotlin/net/corda/node/internal/djvm/DeterministicVerifier.kt +++ b/node/src/main/kotlin/net/corda/node/internal/djvm/DeterministicVerifier.kt @@ -7,13 +7,14 @@ import net.corda.core.contracts.ComponentGroupEnum.SIGNERS_GROUP import net.corda.core.contracts.TransactionState import net.corda.core.contracts.TransactionVerificationException import net.corda.core.crypto.SecureHash -import net.corda.core.internal.ContractVerifier +import net.corda.core.internal.TransactionVerifier import net.corda.core.internal.Verifier import net.corda.core.internal.getNamesOfClassesImplementing import net.corda.core.serialization.SerializationCustomSerializer import net.corda.core.serialization.SerializationWhitelist import net.corda.core.serialization.serialize import net.corda.core.transactions.LedgerTransaction +import net.corda.core.utilities.contextLogger import net.corda.djvm.SandboxConfiguration import net.corda.djvm.execution.ExecutionSummary import net.corda.djvm.execution.IsolatedTask @@ -26,10 +27,14 @@ import java.util.function.Function import kotlin.collections.LinkedHashSet class DeterministicVerifier( - ltx: LedgerTransaction, - transactionClassLoader: ClassLoader, + private val ltx: LedgerTransaction, + private val transactionClassLoader: ClassLoader, private val sandboxConfiguration: SandboxConfiguration -) : Verifier(ltx, transactionClassLoader) { +) : Verifier { + private companion object { + private val logger = contextLogger() + } + /** * Read the whitelisted classes without using the [java.util.ServiceLoader] mechanism * because the whitelists themselves are untrusted. @@ -47,7 +52,7 @@ class DeterministicVerifier( } } - override fun verifyContracts() { + override fun verify() { val customSerializerNames = getNamesOfClassesImplementing(transactionClassLoader, SerializationCustomSerializer::class.java) val serializationWhitelistNames = getSerializationWhitelistNames(transactionClassLoader) val result = IsolatedTask(ltx.id.toString(), sandboxConfiguration).run(Function { classLoader -> @@ -113,7 +118,7 @@ class DeterministicVerifier( )) } - val verifier = taskFactory.apply(ContractVerifier::class.java) + val verifier = taskFactory.apply(TransactionVerifier::class.java) // Now execute the contract verifier task within the sandbox... verifier.apply(sandboxTx) @@ -128,7 +133,7 @@ class DeterministicVerifier( val sandboxEx = SandboxException( Message.getMessageFromException(this), result.identifier, - ClassSource.fromClassName(ContractVerifier::class.java.name), + ClassSource.fromClassName(TransactionVerifier::class.java.name), ExecutionSummary(result.costs), this ) From 5c052e9273de4da6fc2e19b558ca8299fb7b2ad7 Mon Sep 17 00:00:00 2001 From: Adel El-Beik <48713346+adelel1@users.noreply.github.com> Date: Mon, 7 Feb 2022 18:05:59 +0000 Subject: [PATCH 2/5] ENT-6586: Validate when signing that output states can be deserialized. (#7055) * ENT-6586: Log warning instead of debug if state cannot be deserialized. --- .../kotlin/net/corda/node/services/vault/NodeVaultService.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/node/src/main/kotlin/net/corda/node/services/vault/NodeVaultService.kt b/node/src/main/kotlin/net/corda/node/services/vault/NodeVaultService.kt index e7846b2821..6db962cdce 100644 --- a/node/src/main/kotlin/net/corda/node/services/vault/NodeVaultService.kt +++ b/node/src/main/kotlin/net/corda/node/services/vault/NodeVaultService.kt @@ -271,7 +271,7 @@ class NodeVaultService( // This will cause a failure as we can't deserialize such states in the context of the `appClassloader`. // For now we ignore these states. // In the future we will use the AttachmentsClassloader to correctly deserialize and asses the relevancy. - log.debug { "Could not deserialize state $idx from transaction $txId. Cause: $e" } + log.warn("Could not deserialize state $idx from transaction $txId. Cause: $e") null } }.toMap() From fa739fc46757ff925e9f19faf66587ce32f1279c Mon Sep 17 00:00:00 2001 From: Dan Newton Date: Tue, 8 Feb 2022 10:06:44 +0000 Subject: [PATCH 3/5] ENT-6588 Restrict database operations platform flag (#7053) * ENT-6588 Restrict database operations platform flag Put the restricting of database operations in `RestrictedConnection` and `RestrictedEntityManager` behind a platform version flag. `RESTRICTED_DATABASE_OPERATIONS = 7` was added to signify this. If the version is less than 7, then the database operations will not be restricted. A warning is logged to indicate that they are using potentially dangerous methods. If the version is 7 or greater, then the database operations are restricted and throw an error if called. --- .../core/internal/PlatformVersionSwitches.kt | 1 + .../RestrictedConnectionFlowTest.kt | 52 ++- .../RestrictedEntityManagerFlowTest.kt | 58 +++- .../persistence/RestrictedConnection.kt | 41 +-- .../RestrictedDatabaseOperations.kt | 31 ++ .../persistence/RestrictedEntityManager.kt | 32 +- .../persistence/RestrictedConnectionTest.kt | 311 +++++++++++++++--- .../RestrictedEntityManagerTest.kt | 153 ++++++++- .../net/corda/node/internal/AbstractNode.kt | 4 +- 9 files changed, 559 insertions(+), 124 deletions(-) create mode 100644 node-api/src/main/kotlin/net/corda/nodeapi/internal/persistence/RestrictedDatabaseOperations.kt diff --git a/core/src/main/kotlin/net/corda/core/internal/PlatformVersionSwitches.kt b/core/src/main/kotlin/net/corda/core/internal/PlatformVersionSwitches.kt index 4370f998b9..c6d93f272f 100644 --- a/core/src/main/kotlin/net/corda/core/internal/PlatformVersionSwitches.kt +++ b/core/src/main/kotlin/net/corda/core/internal/PlatformVersionSwitches.kt @@ -16,5 +16,6 @@ object PlatformVersionSwitches { const val LIMIT_KEYS_IN_SIGNATURE_CONSTRAINTS = 5 const val BATCH_DOWNLOAD_COUNTERPARTY_BACKCHAIN = 6 const val ENABLE_P2P_COMPRESSION = 7 + const val RESTRICTED_DATABASE_OPERATIONS = 7 const val CERTIFICATE_ROTATION = 9 } \ No newline at end of file diff --git a/node-api-tests/src/test/kotlin/net/corda/nodeapitests/internal/persistence/RestrictedConnectionFlowTest.kt b/node-api-tests/src/test/kotlin/net/corda/nodeapitests/internal/persistence/RestrictedConnectionFlowTest.kt index f948c75f37..e00e65ec44 100644 --- a/node-api-tests/src/test/kotlin/net/corda/nodeapitests/internal/persistence/RestrictedConnectionFlowTest.kt +++ b/node-api-tests/src/test/kotlin/net/corda/nodeapitests/internal/persistence/RestrictedConnectionFlowTest.kt @@ -4,14 +4,15 @@ import co.paralleluniverse.fibers.Suspendable import net.corda.core.flows.FlowLogic import net.corda.core.flows.InitiatingFlow import net.corda.core.identity.CordaX500Name +import net.corda.core.internal.PLATFORM_VERSION import net.corda.core.utilities.getOrThrow import net.corda.nodeapi.internal.persistence.RestrictedConnection import net.corda.testing.node.MockNetwork import net.corda.testing.node.MockNetworkParameters import net.corda.testing.node.StartedMockNode +import net.corda.testing.node.internal.enclosedCordapp import org.assertj.core.api.Assertions import org.junit.After -import org.junit.Before import org.junit.Test import kotlin.test.assertTrue @@ -38,40 +39,63 @@ class RestrictedConnectionFlowTest { } @InitiatingFlow - class TestCloseMethodIsBlocked : FlowLogic() { + class TestClearWarningsMethodIsBlocked : FlowLogic() { @Suspendable override fun call() { val connection = serviceHub.jdbcSession() - connection.close() + connection.clearWarnings() } } - @Before - fun init() { - mockNetwork = MockNetwork(MockNetworkParameters()) - aliceNode = mockNetwork.createPartyNode(CordaX500Name("Alice", "London", "GB")) - } - @After fun done() { mockNetwork.stopNodes() } @Test(timeout=300_000) - fun testIfItIsRestrictedConnection() { + fun `restricted connection is returned from ServiceHub#jdbcSession`() { + mockNetwork = MockNetwork(MockNetworkParameters(listOf(enclosedCordapp().copy(targetPlatformVersion = PLATFORM_VERSION)))) + aliceNode = mockNetwork.createPartyNode(CordaX500Name("Alice", "London", "GB")) assertTrue { aliceNode.startFlow(TestIfItIsRestrictedConnection()).get() } mockNetwork.runNetwork() } @Test(timeout=300_000) - fun testMethodsAreBlocked() { + fun `restricted methods are blocked when the target platform is the current corda version`() { + mockNetwork = MockNetwork(MockNetworkParameters(listOf(enclosedCordapp().copy(targetPlatformVersion = PLATFORM_VERSION)))) + aliceNode = mockNetwork.createPartyNode(CordaX500Name("Alice", "London", "GB")) Assertions.assertThatExceptionOfType(UnsupportedOperationException::class.java) .isThrownBy { aliceNode.startFlow(TestAutoCommitMethodIsBlocked()).getOrThrow() } - .withMessageContaining("This method cannot be called via ServiceHub.jdbcSession.") + .withMessageContaining("ServiceHub.jdbcSession.setAutoCommit is restricted and cannot be called") Assertions.assertThatExceptionOfType(UnsupportedOperationException::class.java) - .isThrownBy { aliceNode.startFlow(TestCloseMethodIsBlocked()).getOrThrow() } - .withMessageContaining("This method cannot be called via ServiceHub.jdbcSession.") + .isThrownBy { aliceNode.startFlow(TestClearWarningsMethodIsBlocked()).getOrThrow() } + .withMessageContaining("ServiceHub.jdbcSession.clearWarnings is restricted and cannot be called") + + mockNetwork.runNetwork() + } + + @Test(timeout=300_000) + fun `restricted methods are blocked when the target platform is 7`() { + mockNetwork = MockNetwork(MockNetworkParameters(listOf(enclosedCordapp().copy(targetPlatformVersion = 7)))) + aliceNode = mockNetwork.createPartyNode(CordaX500Name("Alice", "London", "GB")) + Assertions.assertThatExceptionOfType(UnsupportedOperationException::class.java) + .isThrownBy { aliceNode.startFlow(TestAutoCommitMethodIsBlocked()).getOrThrow() } + .withMessageContaining("ServiceHub.jdbcSession.setAutoCommit is restricted and cannot be called") + + Assertions.assertThatExceptionOfType(UnsupportedOperationException::class.java) + .isThrownBy { aliceNode.startFlow(TestClearWarningsMethodIsBlocked()).getOrThrow() } + .withMessageContaining("ServiceHub.jdbcSession.clearWarnings is restricted and cannot be called") + + mockNetwork.runNetwork() + } + + @Test(timeout=300_000) + fun `restricted methods are not blocked when the target platform is 6`() { + mockNetwork = MockNetwork(MockNetworkParameters(listOf(enclosedCordapp().copy(targetPlatformVersion = 6)))) + aliceNode = mockNetwork.createPartyNode(CordaX500Name("Alice", "London", "GB")) + aliceNode.startFlow(TestAutoCommitMethodIsBlocked()).getOrThrow() + aliceNode.startFlow(TestClearWarningsMethodIsBlocked()).getOrThrow() mockNetwork.runNetwork() } diff --git a/node-api-tests/src/test/kotlin/net/corda/nodeapitests/internal/persistence/RestrictedEntityManagerFlowTest.kt b/node-api-tests/src/test/kotlin/net/corda/nodeapitests/internal/persistence/RestrictedEntityManagerFlowTest.kt index 7da2ff26ff..995440973d 100644 --- a/node-api-tests/src/test/kotlin/net/corda/nodeapitests/internal/persistence/RestrictedEntityManagerFlowTest.kt +++ b/node-api-tests/src/test/kotlin/net/corda/nodeapitests/internal/persistence/RestrictedEntityManagerFlowTest.kt @@ -4,14 +4,15 @@ import co.paralleluniverse.fibers.Suspendable import net.corda.core.flows.FlowLogic import net.corda.core.flows.InitiatingFlow import net.corda.core.identity.CordaX500Name +import net.corda.core.internal.PLATFORM_VERSION import net.corda.core.utilities.getOrThrow import net.corda.nodeapi.internal.persistence.RestrictedEntityManager import net.corda.testing.node.MockNetwork import net.corda.testing.node.MockNetworkParameters import net.corda.testing.node.StartedMockNode +import net.corda.testing.node.internal.enclosedCordapp import org.assertj.core.api.Assertions import org.junit.After -import org.junit.Before import org.junit.Test import kotlin.test.assertTrue @@ -25,7 +26,7 @@ class RestrictedEntityManagerFlowTest { @Suspendable override fun call() : Boolean { var result = false - serviceHub.withEntityManager() { + serviceHub.withEntityManager { result = this is RestrictedEntityManager } return result @@ -33,11 +34,11 @@ class RestrictedEntityManagerFlowTest { } @InitiatingFlow - class TestCloseMethodIsBlocked : FlowLogic() { + class TestGetMetamodelMethodIsBlocked : FlowLogic() { @Suspendable override fun call() { - serviceHub.withEntityManager() { - this.close() + serviceHub.withEntityManager { + this.metamodel } } } @@ -46,38 +47,61 @@ class RestrictedEntityManagerFlowTest { class TestJoinTransactionMethodIsBlocked : FlowLogic() { @Suspendable override fun call() { - serviceHub.withEntityManager() { + serviceHub.withEntityManager { this.joinTransaction() } } } - @Before - fun init() { - mockNetwork = MockNetwork(MockNetworkParameters()) - aliceNode = mockNetwork.createPartyNode(CordaX500Name("Alice", "London", "GB")) - } - @After fun done() { mockNetwork.stopNodes() } @Test(timeout=300_000) - fun testIfItIsRestrictedConnection() { + fun `restricted connection is returned from ServiceHub#withEntityManager`() { + mockNetwork = MockNetwork(MockNetworkParameters(listOf(enclosedCordapp().copy(targetPlatformVersion = PLATFORM_VERSION)))) + aliceNode = mockNetwork.createPartyNode(CordaX500Name("Alice", "London", "GB")) assertTrue { aliceNode.startFlow(TestIfItIsRestrictedEntityManager()).get() } mockNetwork.runNetwork() } @Test(timeout=300_000) - fun testMethodsAreBlocked() { + fun `restricted methods are blocked when the target platform is the current corda version`() { + mockNetwork = MockNetwork(MockNetworkParameters(listOf(enclosedCordapp().copy(targetPlatformVersion = PLATFORM_VERSION)))) + aliceNode = mockNetwork.createPartyNode(CordaX500Name("Alice", "London", "GB")) Assertions.assertThatExceptionOfType(UnsupportedOperationException::class.java) - .isThrownBy { aliceNode.startFlow(TestCloseMethodIsBlocked()).getOrThrow() } - .withMessageContaining("This method cannot be called via ServiceHub.withEntityManager.") + .isThrownBy { aliceNode.startFlow(TestGetMetamodelMethodIsBlocked()).getOrThrow() } + .withMessageContaining("ServiceHub.withEntityManager.getMetamodel is restricted and cannot be called") Assertions.assertThatExceptionOfType(UnsupportedOperationException::class.java) .isThrownBy { aliceNode.startFlow(TestJoinTransactionMethodIsBlocked()).getOrThrow() } - .withMessageContaining("This method cannot be called via ServiceHub.withEntityManager.") + .withMessageContaining("ServiceHub.withEntityManager.joinTransaction is restricted and cannot be called") + + mockNetwork.runNetwork() + } + + @Test(timeout=300_000) + fun `restricted methods are blocked when the target platform is 7`() { + mockNetwork = MockNetwork(MockNetworkParameters(listOf(enclosedCordapp().copy(targetPlatformVersion = 7)))) + aliceNode = mockNetwork.createPartyNode(CordaX500Name("Alice", "London", "GB")) + Assertions.assertThatExceptionOfType(UnsupportedOperationException::class.java) + .isThrownBy { aliceNode.startFlow(TestGetMetamodelMethodIsBlocked()).getOrThrow() } + .withMessageContaining("ServiceHub.withEntityManager.getMetamodel is restricted and cannot be called") + + Assertions.assertThatExceptionOfType(UnsupportedOperationException::class.java) + .isThrownBy { aliceNode.startFlow(TestJoinTransactionMethodIsBlocked()).getOrThrow() } + .withMessageContaining("ServiceHub.withEntityManager.joinTransaction is restricted and cannot be called") + + mockNetwork.runNetwork() + } + + @Test(timeout=300_000) + fun `restricted methods are not blocked when the target platform is 6`() { + mockNetwork = MockNetwork(MockNetworkParameters(listOf(enclosedCordapp().copy(targetPlatformVersion = 6)))) + aliceNode = mockNetwork.createPartyNode(CordaX500Name("Alice", "London", "GB")) + aliceNode.startFlow(TestGetMetamodelMethodIsBlocked()).getOrThrow() + aliceNode.startFlow(TestJoinTransactionMethodIsBlocked()).getOrThrow() mockNetwork.runNetwork() } diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/persistence/RestrictedConnection.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/persistence/RestrictedConnection.kt index 997cdc3ebd..a2a471c364 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/persistence/RestrictedConnection.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/persistence/RestrictedConnection.kt @@ -1,5 +1,6 @@ package net.corda.nodeapi.internal.persistence +import net.corda.core.node.ServiceHub import java.sql.Connection import java.sql.Savepoint import java.util.concurrent.Executor @@ -8,73 +9,73 @@ import java.util.concurrent.Executor * A delegate of [Connection] which disallows some operations. */ @Suppress("TooManyFunctions") -class RestrictedConnection(private val delegate : Connection) : Connection by delegate { +class RestrictedConnection(private val delegate: Connection, private val serviceHub: ServiceHub) : Connection by delegate { override fun abort(executor: Executor?) { - throw UnsupportedOperationException("This method cannot be called via ServiceHub.jdbcSession.") + restrictDatabaseOperationFromJdbcSession("abort", serviceHub) { delegate.abort(executor) } } override fun clearWarnings() { - throw UnsupportedOperationException("This method cannot be called via ServiceHub.jdbcSession.") + restrictDatabaseOperationFromJdbcSession("clearWarnings", serviceHub) { delegate.clearWarnings() } } override fun close() { - throw UnsupportedOperationException("This method cannot be called via ServiceHub.jdbcSession.") + restrictDatabaseOperationFromJdbcSession("close", serviceHub) { delegate.close() } } override fun commit() { - throw UnsupportedOperationException("This method cannot be called via ServiceHub.jdbcSession.") + restrictDatabaseOperationFromJdbcSession("commit", serviceHub) { delegate.commit() } } override fun setSavepoint(): Savepoint? { - throw UnsupportedOperationException("This method cannot be called via ServiceHub.jdbcSession.") + return restrictDatabaseOperationFromJdbcSession("setSavepoint", serviceHub) { delegate.setSavepoint() } } - override fun setSavepoint(name : String?): Savepoint? { - throw UnsupportedOperationException("This method cannot be called via ServiceHub.jdbcSession.") + override fun setSavepoint(name: String?): Savepoint? { + return restrictDatabaseOperationFromJdbcSession("setSavepoint", serviceHub) { delegate.setSavepoint(name) } } override fun releaseSavepoint(savepoint: Savepoint?) { - throw UnsupportedOperationException("This method cannot be called via ServiceHub.jdbcSession.") + restrictDatabaseOperationFromJdbcSession("releaseSavepoint", serviceHub) { delegate.releaseSavepoint(savepoint) } } override fun rollback() { - throw UnsupportedOperationException("This method cannot be called via ServiceHub.jdbcSession.") + restrictDatabaseOperationFromJdbcSession("rollback", serviceHub) { delegate.rollback() } } override fun rollback(savepoint: Savepoint?) { - throw UnsupportedOperationException("This method cannot be called via ServiceHub.jdbcSession.") + restrictDatabaseOperationFromJdbcSession("rollback", serviceHub) { delegate.rollback(savepoint) } } - override fun setCatalog(catalog : String?) { - throw UnsupportedOperationException("This method cannot be called via ServiceHub.jdbcSession.") + override fun setCatalog(catalog: String?) { + restrictDatabaseOperationFromJdbcSession("setCatalog", serviceHub) { delegate.catalog = catalog } } override fun setTransactionIsolation(level: Int) { - throw UnsupportedOperationException("This method cannot be called via ServiceHub.jdbcSession.") + restrictDatabaseOperationFromJdbcSession("setTransactionIsolation", serviceHub) { delegate.transactionIsolation = level } } override fun setTypeMap(map: MutableMap>?) { - throw UnsupportedOperationException("This method cannot be called via ServiceHub.jdbcSession.") + restrictDatabaseOperationFromJdbcSession("setTypeMap", serviceHub) { delegate.typeMap = map } } override fun setHoldability(holdability: Int) { - throw UnsupportedOperationException("This method cannot be called via ServiceHub.jdbcSession.") + restrictDatabaseOperationFromJdbcSession("setHoldability", serviceHub) { delegate.holdability = holdability } } override fun setSchema(schema: String?) { - throw UnsupportedOperationException("This method cannot be called via ServiceHub.jdbcSession.") + restrictDatabaseOperationFromJdbcSession("setSchema", serviceHub) { delegate.schema = schema } } override fun setNetworkTimeout(executor: Executor?, milliseconds: Int) { - throw UnsupportedOperationException("This method cannot be called via ServiceHub.jdbcSession.") + restrictDatabaseOperationFromJdbcSession("setNetworkTimeout", serviceHub) { delegate.setNetworkTimeout(executor, milliseconds) } } override fun setAutoCommit(autoCommit: Boolean) { - throw UnsupportedOperationException("This method cannot be called via ServiceHub.jdbcSession.") + restrictDatabaseOperationFromJdbcSession("setAutoCommit", serviceHub) { delegate.autoCommit = autoCommit } } override fun setReadOnly(readOnly: Boolean) { - throw UnsupportedOperationException("This method cannot be called via ServiceHub.jdbcSession.") + restrictDatabaseOperationFromJdbcSession("setReadOnly", serviceHub) { delegate.isReadOnly = readOnly } } } \ No newline at end of file diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/persistence/RestrictedDatabaseOperations.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/persistence/RestrictedDatabaseOperations.kt new file mode 100644 index 0000000000..bc5cbc055f --- /dev/null +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/persistence/RestrictedDatabaseOperations.kt @@ -0,0 +1,31 @@ +package net.corda.nodeapi.internal.persistence + +import net.corda.core.internal.PlatformVersionSwitches.RESTRICTED_DATABASE_OPERATIONS +import net.corda.core.internal.warnOnce +import net.corda.core.node.ServiceHub +import org.slf4j.LoggerFactory + +private val log = LoggerFactory.getLogger("RestrictedDatabaseOperations") + +internal inline fun restrictDatabaseOperationFromJdbcSession(method: String, serviceHub: ServiceHub, operation: () -> T): T { + return restrictDatabaseOperation("ServiceHub.jdbcSession.$method", serviceHub, operation) +} + +internal inline fun restrictDatabaseOperationFromEntityManager(method: String, serviceHub: ServiceHub, operation: () -> T): T { + return restrictDatabaseOperation("ServiceHub.withEntityManager.$method", serviceHub, operation) +} + +internal inline fun restrictDatabaseOperation(method: String, serviceHub: ServiceHub, operation: () -> T): T { + return if (serviceHub.getAppContext().cordapp.targetPlatformVersion >= RESTRICTED_DATABASE_OPERATIONS) { + throw UnsupportedOperationException("$method is restricted and cannot be called") + } else { + log.warnOnce( + "$method should not be called, as manipulating database transactions and connections breaks the Corda flow state machine in " + + "ways that only become evident in failure scenarios. Purely for API backwards compatibility reasons, the prior " + + "behaviour is continued for target platform versions less than $RESTRICTED_DATABASE_OPERATIONS. You should evolve " + + "the CorDapp away from using these problematic APIs as soon as possible. For target platform version of " + + "$RESTRICTED_DATABASE_OPERATIONS or above, an exception will be thrown instead." + ) + operation() + } +} \ No newline at end of file diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/persistence/RestrictedEntityManager.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/persistence/RestrictedEntityManager.kt index 1ea4f2c4fd..d6d2672c0d 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/persistence/RestrictedEntityManager.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/persistence/RestrictedEntityManager.kt @@ -1,5 +1,6 @@ package net.corda.nodeapi.internal.persistence +import net.corda.core.node.ServiceHub import javax.persistence.EntityManager import javax.persistence.EntityTransaction import javax.persistence.LockModeType @@ -8,56 +9,59 @@ import javax.persistence.metamodel.Metamodel /** * A delegate of [EntityManager] which disallows some operations. */ -class RestrictedEntityManager(private val delegate: EntityManager) : EntityManager by delegate { +class RestrictedEntityManager(private val delegate: EntityManager, private val serviceHub: ServiceHub) : EntityManager by delegate { override fun getTransaction(): EntityTransaction { - return RestrictedEntityTransaction(delegate.transaction) + return RestrictedEntityTransaction(delegate.transaction, serviceHub) } override fun close() { - throw UnsupportedOperationException("This method cannot be called via ServiceHub.withEntityManager.") + restrictDatabaseOperationFromEntityManager("close", serviceHub) { delegate.close() } } override fun unwrap(cls: Class?): T { - throw UnsupportedOperationException("This method cannot be called via ServiceHub.withEntityManager.") + return restrictDatabaseOperationFromEntityManager("unwrap", serviceHub) { delegate.unwrap(cls) } } override fun getDelegate(): Any { - throw UnsupportedOperationException("This method cannot be called via ServiceHub.withEntityManager.") + return restrictDatabaseOperationFromEntityManager("getDelegate", serviceHub) { delegate.delegate } } override fun getMetamodel(): Metamodel? { - throw UnsupportedOperationException("This method cannot be called via ServiceHub.withEntityManager.") + return restrictDatabaseOperationFromEntityManager("getMetamodel", serviceHub) { delegate.metamodel } } override fun joinTransaction() { - throw UnsupportedOperationException("This method cannot be called via ServiceHub.withEntityManager.") + restrictDatabaseOperationFromEntityManager("joinTransaction", serviceHub) { delegate.joinTransaction() } } override fun lock(entity: Any?, lockMode: LockModeType?) { - throw UnsupportedOperationException("This method cannot be called via ServiceHub.withEntityManager.") + restrictDatabaseOperationFromEntityManager("lock", serviceHub) { delegate.lock(entity, lockMode) } } override fun lock(entity: Any?, lockMode: LockModeType?, properties: MutableMap?) { - throw UnsupportedOperationException("This method cannot be called via ServiceHub.withEntityManager.") + restrictDatabaseOperationFromEntityManager("lock", serviceHub) { delegate.lock(entity, lockMode, properties) } } override fun setProperty(propertyName: String?, value: Any?) { - throw UnsupportedOperationException("This method cannot be called via ServiceHub.withEntityManager.") + restrictDatabaseOperationFromEntityManager("lock", serviceHub) { delegate.setProperty(propertyName, value) } } } -class RestrictedEntityTransaction(private val delegate: EntityTransaction) : EntityTransaction by delegate { +class RestrictedEntityTransaction( + private val delegate: EntityTransaction, + private val serviceHub: ServiceHub +) : EntityTransaction by delegate { override fun rollback() { - throw UnsupportedOperationException("This method cannot be called via ServiceHub.withEntityManager.") + restrictDatabaseOperationFromEntityManager("EntityTransaction.rollback", serviceHub) { delegate.rollback() } } override fun commit() { - throw UnsupportedOperationException("This method cannot be called via ServiceHub.withEntityManager.") + restrictDatabaseOperationFromEntityManager("EntityTransaction.commit", serviceHub) { delegate.commit() } } override fun begin() { - throw UnsupportedOperationException("This method cannot be called via ServiceHub.withEntityManager.") + restrictDatabaseOperationFromEntityManager("EntityTransaction.begin", serviceHub) { delegate.begin() } } } \ No newline at end of file diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/persistence/RestrictedConnectionTest.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/persistence/RestrictedConnectionTest.kt index 3708360bfc..39f2d7af73 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/persistence/RestrictedConnectionTest.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/persistence/RestrictedConnectionTest.kt @@ -1,104 +1,337 @@ package net.corda.nodeapi.internal.persistence import com.nhaarman.mockito_kotlin.mock +import com.nhaarman.mockito_kotlin.whenever +import net.corda.core.cordapp.Cordapp +import net.corda.core.cordapp.CordappContext +import net.corda.core.internal.PLATFORM_VERSION +import net.corda.core.node.ServiceHub import org.junit.Test import java.sql.Connection import java.sql.Savepoint class RestrictedConnectionTest { - private val connection : Connection = mock() - private val savePoint : Savepoint = mock() - private val restrictedConnection : RestrictedConnection = RestrictedConnection(connection) + private val connection: Connection = mock() + private val savePoint: Savepoint = mock() + private val cordapp = mock() + private val cordappContext = CordappContext.create(cordapp, null, javaClass.classLoader, mock()) + private val serviceHub = mock().apply { + whenever(getAppContext()).thenReturn(cordappContext) + } + private val restrictedConnection: RestrictedConnection = RestrictedConnection(connection, serviceHub) companion object { - private const val TEST_STRING : String = "test" - private const val TEST_INT : Int = 1 + private const val TEST_STRING: String = "test" + private const val TEST_INT: Int = 1 } - @Test(expected = UnsupportedOperationException::class, timeout=300_000) - fun testAbort() { + @Test(expected = UnsupportedOperationException::class, timeout = 300_000) + fun `abort with target platform version of current corda version throws unsupported exception`() { + whenever(cordapp.targetPlatformVersion).thenReturn(PLATFORM_VERSION) restrictedConnection.abort { println("I'm just an executor for this test...") } } - @Test(expected = UnsupportedOperationException::class, timeout=300_000) - fun testClearWarnings() { + @Test(expected = UnsupportedOperationException::class, timeout = 300_000) + fun `clearWarnings with target platform version of current corda version throws unsupported exception`() { + whenever(cordapp.targetPlatformVersion).thenReturn(PLATFORM_VERSION) restrictedConnection.clearWarnings() } - @Test(expected = UnsupportedOperationException::class, timeout=300_000) - fun testClose() { + @Test(expected = UnsupportedOperationException::class, timeout = 300_000) + fun `close with target platform version of current corda version throws unsupported exception`() { + whenever(cordapp.targetPlatformVersion).thenReturn(PLATFORM_VERSION) restrictedConnection.close() } - @Test(expected = UnsupportedOperationException::class, timeout=300_000) - fun testCommit() { + @Test(expected = UnsupportedOperationException::class, timeout = 300_000) + fun `commit with target platform version of current corda version throws unsupported exception`() { + whenever(cordapp.targetPlatformVersion).thenReturn(PLATFORM_VERSION) restrictedConnection.commit() } - @Test(expected = UnsupportedOperationException::class, timeout=300_000) - fun testSetSavepoint() { + @Test(expected = UnsupportedOperationException::class, timeout = 300_000) + fun `setSavepoint with target platform version of current corda version throws unsupported exception`() { + whenever(cordapp.targetPlatformVersion).thenReturn(PLATFORM_VERSION) restrictedConnection.setSavepoint() } - @Test(expected = UnsupportedOperationException::class, timeout=300_000) - fun testSetSavepointWithName() { + @Test(expected = UnsupportedOperationException::class, timeout = 300_000) + fun `setSavepoint with name with target platform version of current corda version throws unsupported exception`() { + whenever(cordapp.targetPlatformVersion).thenReturn(PLATFORM_VERSION) restrictedConnection.setSavepoint(TEST_STRING) } - @Test(expected = UnsupportedOperationException::class, timeout=300_000) - fun testReleaseSavepoint() { + @Test(expected = UnsupportedOperationException::class, timeout = 300_000) + fun `releaseSavepoint with target platform version of current corda version throws unsupported exception`() { + whenever(cordapp.targetPlatformVersion).thenReturn(PLATFORM_VERSION) restrictedConnection.releaseSavepoint(savePoint) } - @Test(expected = UnsupportedOperationException::class, timeout=300_000) - fun testRollback() { + @Test(expected = UnsupportedOperationException::class, timeout = 300_000) + fun `rollback with target platform version of current corda version throws unsupported exception`() { + whenever(cordapp.targetPlatformVersion).thenReturn(PLATFORM_VERSION) restrictedConnection.rollback() } - @Test(expected = UnsupportedOperationException::class, timeout=300_000) - fun testRollbackWithSavepoint() { + @Test(expected = UnsupportedOperationException::class, timeout = 300_000) + fun `rollbackWithSavepoint with target platform version of current corda version throws unsupported exception`() { + whenever(cordapp.targetPlatformVersion).thenReturn(PLATFORM_VERSION) restrictedConnection.rollback(savePoint) } - @Test(expected = UnsupportedOperationException::class, timeout=300_000) - fun testSetCatalog() { + @Test(expected = UnsupportedOperationException::class, timeout = 300_000) + fun `setCatalog with target platform version of current corda version throws unsupported exception`() { + whenever(cordapp.targetPlatformVersion).thenReturn(PLATFORM_VERSION) restrictedConnection.catalog = TEST_STRING } - @Test(expected = UnsupportedOperationException::class, timeout=300_000) - fun testSetTransactionIsolation() { + @Test(expected = UnsupportedOperationException::class, timeout = 300_000) + fun `setTransactionIsolation with target platform version of current corda version throws unsupported exception`() { + whenever(cordapp.targetPlatformVersion).thenReturn(PLATFORM_VERSION) restrictedConnection.transactionIsolation = TEST_INT } - @Test(expected = UnsupportedOperationException::class, timeout=300_000) - fun testSetTypeMap() { + @Test(expected = UnsupportedOperationException::class, timeout = 300_000) + fun `setTypeMap with target platform version of current corda version throws unsupported exception`() { + whenever(cordapp.targetPlatformVersion).thenReturn(PLATFORM_VERSION) val map: MutableMap> = mutableMapOf() restrictedConnection.typeMap = map } - @Test(expected = UnsupportedOperationException::class, timeout=300_000) - fun testSetHoldability() { + @Test(expected = UnsupportedOperationException::class, timeout = 300_000) + fun `setHoldability with target platform version of current corda version throws unsupported exception`() { + whenever(cordapp.targetPlatformVersion).thenReturn(PLATFORM_VERSION) restrictedConnection.holdability = TEST_INT } - @Test(expected = UnsupportedOperationException::class, timeout=300_000) - fun testSetSchema() { + @Test(expected = UnsupportedOperationException::class, timeout = 300_000) + fun `setSchema with target platform version of current corda version throws unsupported exception`() { + whenever(cordapp.targetPlatformVersion).thenReturn(PLATFORM_VERSION) restrictedConnection.schema = TEST_STRING } - @Test(expected = UnsupportedOperationException::class, timeout=300_000) - fun testSetNetworkTimeout() { + @Test(expected = UnsupportedOperationException::class, timeout = 300_000) + fun `setNetworkTimeout with target platform version of current corda version throws unsupported exception`() { + whenever(cordapp.targetPlatformVersion).thenReturn(PLATFORM_VERSION) restrictedConnection.setNetworkTimeout({ println("I'm just an executor for this test...") }, TEST_INT) } - @Test(expected = UnsupportedOperationException::class, timeout=300_000) - fun testSetAutoCommit() { + @Test(expected = UnsupportedOperationException::class, timeout = 300_000) + fun `setAutoCommit with target platform version of current corda version throws unsupported exception`() { + whenever(cordapp.targetPlatformVersion).thenReturn(PLATFORM_VERSION) restrictedConnection.autoCommit = true } - @Test(expected = UnsupportedOperationException::class, timeout=300_000) - fun testSetReadOnly() { + @Test(expected = UnsupportedOperationException::class, timeout = 300_000) + fun `setReadOnly with target platform version of current corda version throws unsupported exception`() { + whenever(cordapp.targetPlatformVersion).thenReturn(PLATFORM_VERSION) + restrictedConnection.isReadOnly = true + } + + @Test(expected = UnsupportedOperationException::class, timeout = 300_000) + fun `abort with target platform version of 7 throws unsupported exception`() { + whenever(cordapp.targetPlatformVersion).thenReturn(7) + restrictedConnection.abort { println("I'm just an executor for this test...") } + } + + @Test(expected = UnsupportedOperationException::class, timeout = 300_000) + fun `clearWarnings with target platform version of 7 throws unsupported exception`() { + whenever(cordapp.targetPlatformVersion).thenReturn(7) + restrictedConnection.clearWarnings() + } + + @Test(expected = UnsupportedOperationException::class, timeout = 300_000) + fun `close with target platform version of 7 throws unsupported exception`() { + whenever(cordapp.targetPlatformVersion).thenReturn(7) + restrictedConnection.close() + } + + @Test(expected = UnsupportedOperationException::class, timeout = 300_000) + fun `commit with target platform version of 7 throws unsupported exception`() { + whenever(cordapp.targetPlatformVersion).thenReturn(7) + restrictedConnection.commit() + } + + @Test(expected = UnsupportedOperationException::class, timeout = 300_000) + fun `setSavepoint with target platform version of 7 throws unsupported exception`() { + whenever(cordapp.targetPlatformVersion).thenReturn(7) + restrictedConnection.setSavepoint() + } + + @Test(expected = UnsupportedOperationException::class, timeout = 300_000) + fun `setSavepoint with name with target platform version of 7 throws unsupported exception`() { + whenever(cordapp.targetPlatformVersion).thenReturn(7) + restrictedConnection.setSavepoint(TEST_STRING) + } + + @Test(expected = UnsupportedOperationException::class, timeout = 300_000) + fun `releaseSavepoint with target platform version of 7 throws unsupported exception`() { + whenever(cordapp.targetPlatformVersion).thenReturn(7) + restrictedConnection.releaseSavepoint(savePoint) + } + + @Test(expected = UnsupportedOperationException::class, timeout = 300_000) + fun `rollback with target platform version of 7 throws unsupported exception`() { + whenever(cordapp.targetPlatformVersion).thenReturn(7) + restrictedConnection.rollback() + } + + @Test(expected = UnsupportedOperationException::class, timeout = 300_000) + fun `rollbackWithSavepoint with target platform version of 7 throws unsupported exception`() { + whenever(cordapp.targetPlatformVersion).thenReturn(7) + restrictedConnection.rollback(savePoint) + } + + @Test(expected = UnsupportedOperationException::class, timeout = 300_000) + fun `setCatalog with target platform version of 7 throws unsupported exception`() { + whenever(cordapp.targetPlatformVersion).thenReturn(7) + restrictedConnection.catalog = TEST_STRING + } + + @Test(expected = UnsupportedOperationException::class, timeout = 300_000) + fun `setTransactionIsolation with target platform version of 7 throws unsupported exception`() { + whenever(cordapp.targetPlatformVersion).thenReturn(7) + restrictedConnection.transactionIsolation = TEST_INT + } + + @Test(expected = UnsupportedOperationException::class, timeout = 300_000) + fun `setTypeMap with target platform version of 7 throws unsupported exception`() { + whenever(cordapp.targetPlatformVersion).thenReturn(7) + val map: MutableMap> = mutableMapOf() + restrictedConnection.typeMap = map + } + + @Test(expected = UnsupportedOperationException::class, timeout = 300_000) + fun `setHoldability with target platform version of 7 throws unsupported exception`() { + whenever(cordapp.targetPlatformVersion).thenReturn(7) + restrictedConnection.holdability = TEST_INT + } + + @Test(expected = UnsupportedOperationException::class, timeout = 300_000) + fun `setSchema with target platform version of current 7 unsupported exception`() { + whenever(cordapp.targetPlatformVersion).thenReturn(7) + restrictedConnection.schema = TEST_STRING + } + + @Test(expected = UnsupportedOperationException::class, timeout = 300_000) + fun `setNetworkTimeout with target platform version of 7 throws unsupported exception`() { + whenever(cordapp.targetPlatformVersion).thenReturn(7) + restrictedConnection.setNetworkTimeout({ println("I'm just an executor for this test...") }, TEST_INT) + } + + @Test(expected = UnsupportedOperationException::class, timeout = 300_000) + fun `setAutoCommit with target platform version of 7 throws unsupported exception`() { + whenever(cordapp.targetPlatformVersion).thenReturn(7) + restrictedConnection.autoCommit = true + } + + @Test(expected = UnsupportedOperationException::class, timeout = 300_000) + fun `setReadOnly with target platform version of 7 throws unsupported exception`() { + whenever(cordapp.targetPlatformVersion).thenReturn(7) + restrictedConnection.isReadOnly = true + } + + @Test(timeout = 300_000) + fun `abort with target platform version of 6 executes successfully`() { + whenever(cordapp.targetPlatformVersion).thenReturn(6) + restrictedConnection.abort { println("I'm just an executor for this test...") } + } + + @Test(timeout = 300_000) + fun `clearWarnings with target platform version of 6 executes successfully`() { + whenever(cordapp.targetPlatformVersion).thenReturn(6) + restrictedConnection.clearWarnings() + } + + @Test(timeout = 300_000) + fun `close with target platform version of 6 executes successfully`() { + whenever(cordapp.targetPlatformVersion).thenReturn(6) + restrictedConnection.close() + } + + @Test(timeout = 300_000) + fun `commit with target platform version of 6 executes successfully`() { + whenever(cordapp.targetPlatformVersion).thenReturn(6) + restrictedConnection.commit() + } + + @Test(timeout = 300_000) + fun `setSavepoint with target platform version of 6 executes successfully`() { + whenever(cordapp.targetPlatformVersion).thenReturn(6) + restrictedConnection.setSavepoint() + } + + @Test(timeout = 300_000) + fun `setSavepoint with name with target platform version of 6 executes successfully`() { + whenever(cordapp.targetPlatformVersion).thenReturn(6) + restrictedConnection.setSavepoint(TEST_STRING) + } + + @Test(timeout = 300_000) + fun `releaseSavepoint with target platform version of 6 executes successfully`() { + whenever(cordapp.targetPlatformVersion).thenReturn(6) + restrictedConnection.releaseSavepoint(savePoint) + } + + @Test(timeout = 300_000) + fun `rollback with target platform version of 6 executes successfully`() { + whenever(cordapp.targetPlatformVersion).thenReturn(6) + restrictedConnection.rollback() + } + + @Test(timeout = 300_000) + fun `rollbackWithSavepoint with target platform version of 6 executes successfully`() { + whenever(cordapp.targetPlatformVersion).thenReturn(6) + restrictedConnection.rollback(savePoint) + } + + @Test(timeout = 300_000) + fun `setCatalog with target platform version of 6 executes successfully`() { + whenever(cordapp.targetPlatformVersion).thenReturn(6) + restrictedConnection.catalog = TEST_STRING + } + + @Test(timeout = 300_000) + fun `setTransactionIsolation with target platform version of 6 executes successfully`() { + whenever(cordapp.targetPlatformVersion).thenReturn(6) + restrictedConnection.transactionIsolation = TEST_INT + } + + @Test(timeout = 300_000) + fun `setTypeMap with target platform version of 6 executes successfully`() { + whenever(cordapp.targetPlatformVersion).thenReturn(6) + val map: MutableMap> = mutableMapOf() + restrictedConnection.typeMap = map + } + + @Test(timeout = 300_000) + fun `setHoldability with target platform version of 6 executes successfully`() { + whenever(cordapp.targetPlatformVersion).thenReturn(6) + restrictedConnection.holdability = TEST_INT + } + + @Test(timeout = 300_000) + fun `setSchema with target platform version of current 6 unsupported exception`() { + whenever(cordapp.targetPlatformVersion).thenReturn(6) + restrictedConnection.schema = TEST_STRING + } + + @Test(timeout = 300_000) + fun `setNetworkTimeout with target platform version of 6 executes successfully`() { + whenever(cordapp.targetPlatformVersion).thenReturn(6) + restrictedConnection.setNetworkTimeout({ println("I'm just an executor for this test...") }, TEST_INT) + } + + @Test(timeout = 300_000) + fun `setAutoCommit with target platform version of 6 executes successfully`() { + whenever(cordapp.targetPlatformVersion).thenReturn(6) + restrictedConnection.autoCommit = true + } + + @Test(timeout = 300_000) + fun `setReadOnly with target platform version of 6 executes successfully`() { + whenever(cordapp.targetPlatformVersion).thenReturn(6) restrictedConnection.isReadOnly = true } } \ No newline at end of file diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/persistence/RestrictedEntityManagerTest.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/persistence/RestrictedEntityManagerTest.kt index 6f53ade01c..92994a7fab 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/persistence/RestrictedEntityManagerTest.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/persistence/RestrictedEntityManagerTest.kt @@ -3,6 +3,10 @@ package net.corda.nodeapi.internal.persistence import com.nhaarman.mockito_kotlin.doReturn import com.nhaarman.mockito_kotlin.mock import com.nhaarman.mockito_kotlin.whenever +import net.corda.core.cordapp.Cordapp +import net.corda.core.cordapp.CordappContext +import net.corda.core.internal.PLATFORM_VERSION +import net.corda.core.node.ServiceHub import org.junit.Test import javax.persistence.EntityManager import javax.persistence.EntityTransaction @@ -12,47 +16,160 @@ import kotlin.test.assertTrue class RestrictedEntityManagerTest { private val entitymanager = mock() private val transaction = mock() - private val restrictedEntityManager = RestrictedEntityManager(entitymanager) + private val cordapp = mock() + private val cordappContext = CordappContext.create(cordapp, null, javaClass.classLoader, mock()) + private val serviceHub = mock().apply { + whenever(getAppContext()).thenReturn(cordappContext) + } + private val restrictedEntityManager = RestrictedEntityManager(entitymanager, serviceHub) - @Test(expected = UnsupportedOperationException::class, timeout=300_000) - fun testClose() { + @Test(expected = UnsupportedOperationException::class, timeout = 300_000) + fun `close with target platform version of current corda version throws unsupported exception`() { + whenever(cordapp.targetPlatformVersion).thenReturn(PLATFORM_VERSION) restrictedEntityManager.close() } @Test(timeout = 300_000) - fun testClear() { + fun `clear with target platform version of current corda version throws unsupported exception`() { + whenever(cordapp.targetPlatformVersion).thenReturn(PLATFORM_VERSION) restrictedEntityManager.clear() } - @Test(expected = UnsupportedOperationException::class, timeout=300_000) - fun testGetMetaModel() { - restrictedEntityManager.getMetamodel() + @Test(expected = UnsupportedOperationException::class, timeout = 300_000) + fun `getMetaModel with target platform version of current corda version throws unsupported exception`() { + whenever(cordapp.targetPlatformVersion).thenReturn(PLATFORM_VERSION) + restrictedEntityManager.metamodel } @Test(timeout = 300_000) - fun testGetTransaction() { + fun `getTransaction with target platform version of current corda version executes successfully`() { + whenever(cordapp.targetPlatformVersion).thenReturn(PLATFORM_VERSION) whenever(entitymanager.transaction).doReturn(transaction) assertTrue(restrictedEntityManager.transaction is RestrictedEntityTransaction) } - @Test(expected = UnsupportedOperationException::class, timeout=300_000) - fun testJoinTransaction() { + @Test(expected = UnsupportedOperationException::class, timeout = 300_000) + fun `joinTransaction with target platform version of current corda version throws unsupported exception`() { + whenever(cordapp.targetPlatformVersion).thenReturn(PLATFORM_VERSION) restrictedEntityManager.joinTransaction() } - @Test(expected = UnsupportedOperationException::class, timeout=300_000) - fun testLockWithTwoParameters() { + @Test(expected = UnsupportedOperationException::class, timeout = 300_000) + fun `lock with two parameters with target platform version of current corda version throws unsupported exception`() { + whenever(cordapp.targetPlatformVersion).thenReturn(PLATFORM_VERSION) restrictedEntityManager.lock(Object(), LockModeType.OPTIMISTIC) } - @Test(expected = UnsupportedOperationException::class, timeout=300_000) - fun testLockWithThreeParameters() { - val map: MutableMap = mutableMapOf() - restrictedEntityManager.lock(Object(), LockModeType.OPTIMISTIC,map) + @Test(expected = UnsupportedOperationException::class, timeout = 300_000) + fun `lock with three parameters with target platform version of current corda version throws unsupported exception`() { + whenever(cordapp.targetPlatformVersion).thenReturn(PLATFORM_VERSION) + val map: MutableMap = mutableMapOf() + restrictedEntityManager.lock(Object(), LockModeType.OPTIMISTIC, map) } - @Test(expected = UnsupportedOperationException::class, timeout=300_000) - fun testSetProperty() { + @Test(expected = UnsupportedOperationException::class, timeout = 300_000) + fun `setProperty with target platform version of current corda version throws unsupported exception`() { + whenever(cordapp.targetPlatformVersion).thenReturn(PLATFORM_VERSION) + restrictedEntityManager.setProperty("number", 12) + } + + @Test(expected = UnsupportedOperationException::class, timeout = 300_000) + fun `close with target platform version of 7 throws unsupported exception`() { + whenever(cordapp.targetPlatformVersion).thenReturn(7) + restrictedEntityManager.close() + } + + @Test(timeout = 300_000) + fun `clear with target platform version of 7 executes successfully`() { + whenever(cordapp.targetPlatformVersion).thenReturn(7) + restrictedEntityManager.clear() + } + + @Test(expected = UnsupportedOperationException::class, timeout = 300_000) + fun `getMetaModel with target platform version of 7 throws unsupported exception`() { + whenever(cordapp.targetPlatformVersion).thenReturn(7) + restrictedEntityManager.metamodel + } + + @Test(timeout = 300_000) + fun `getTransaction with target platform version of 7 throws unsupported exception`() { + whenever(cordapp.targetPlatformVersion).thenReturn(7) + whenever(entitymanager.transaction).doReturn(transaction) + assertTrue(restrictedEntityManager.transaction is RestrictedEntityTransaction) + } + + @Test(expected = UnsupportedOperationException::class, timeout = 300_000) + fun `joinTransaction with target platform version of 7 throws unsupported exception`() { + whenever(cordapp.targetPlatformVersion).thenReturn(7) + restrictedEntityManager.joinTransaction() + } + + @Test(expected = UnsupportedOperationException::class, timeout = 300_000) + fun `lock with two parameters with target platform version of 7 throws unsupported exception`() { + whenever(cordapp.targetPlatformVersion).thenReturn(7) + restrictedEntityManager.lock(Object(), LockModeType.OPTIMISTIC) + } + + @Test(expected = UnsupportedOperationException::class, timeout = 300_000) + fun `lock with three parameters with target platform version of 7 throws unsupported exception`() { + whenever(cordapp.targetPlatformVersion).thenReturn(7) + val map: MutableMap = mutableMapOf() + restrictedEntityManager.lock(Object(), LockModeType.OPTIMISTIC, map) + } + + @Test(expected = UnsupportedOperationException::class, timeout = 300_000) + fun `setProperty with target platform version of 7 throws unsupported exception`() { + whenever(cordapp.targetPlatformVersion).thenReturn(7) + restrictedEntityManager.setProperty("number", 12) + } + + @Test(timeout = 300_000) + fun `close with target platform version of 6 executes successfully`() { + whenever(cordapp.targetPlatformVersion).thenReturn(6) + restrictedEntityManager.close() + } + + @Test(timeout = 300_000) + fun `clear with target platform version of 6 executes successfully`() { + whenever(cordapp.targetPlatformVersion).thenReturn(6) + restrictedEntityManager.clear() + } + + @Test(timeout = 300_000) + fun `getMetaModel with target platform version of 6 executes successfully`() { + whenever(cordapp.targetPlatformVersion).thenReturn(6) + restrictedEntityManager.metamodel + } + + @Test(timeout = 300_000) + fun `getTransaction with target platform version of 6 executes successfully`() { + whenever(cordapp.targetPlatformVersion).thenReturn(6) + whenever(entitymanager.transaction).doReturn(transaction) + assertTrue(restrictedEntityManager.transaction is RestrictedEntityTransaction) + } + + @Test(timeout = 300_000) + fun `joinTransaction with target platform version of 6 executes successfully`() { + whenever(cordapp.targetPlatformVersion).thenReturn(6) + restrictedEntityManager.joinTransaction() + } + + @Test(timeout = 300_000) + fun `lock with two parameters with target platform version of 6 executes successfully`() { + whenever(cordapp.targetPlatformVersion).thenReturn(6) + restrictedEntityManager.lock(Object(), LockModeType.OPTIMISTIC) + } + + @Test(timeout = 300_000) + fun `lock with three parameters with target platform version of 6 executes successfully`() { + whenever(cordapp.targetPlatformVersion).thenReturn(6) + val map: MutableMap = mutableMapOf() + restrictedEntityManager.lock(Object(), LockModeType.OPTIMISTIC, map) + } + + @Test(timeout = 300_000) + fun `setProperty with target platform version of 6 executes successfully`() { + whenever(cordapp.targetPlatformVersion).thenReturn(6) restrictedEntityManager.setProperty("number", 12) } } \ No newline at end of file diff --git a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt index 88662aec01..10bc96a666 100644 --- a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt +++ b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt @@ -1162,7 +1162,7 @@ abstract class AbstractNode(val configuration: NodeConfiguration, /** * Exposes the database connection as a [RestrictedConnection] to the users. */ - override fun jdbcSession(): Connection = RestrictedConnection(database.createSession()) + override fun jdbcSession(): Connection = RestrictedConnection(database.createSession(), services) @Suppress("TooGenericExceptionCaught") override fun withEntityManager(block: EntityManager.() -> T): T { @@ -1172,7 +1172,7 @@ abstract class AbstractNode(val configuration: NodeConfiguration, withSavePoint { savepoint -> // Restrict what entity manager they can use inside the block try { - block(RestrictedEntityManager(manager)).also { + block(RestrictedEntityManager(manager, services)).also { if (!manager.transaction.rollbackOnly) { manager.flush() } else { From 9c30d5a2cc632c847dff60631db130c9bf05cfe8 Mon Sep 17 00:00:00 2001 From: Rick Parker Date: Tue, 8 Feb 2022 10:25:07 +0000 Subject: [PATCH 4/5] ENT-6498 Fix API docs dependencies (#7054) --- build.gradle | 2 +- docs/build.gradle | 15 +++++++++++---- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/build.gradle b/build.gradle index bc96ca3e27..4bdd2d8ed7 100644 --- a/build.gradle +++ b/build.gradle @@ -100,7 +100,7 @@ buildscript { ext.hibernate_version = '5.4.3.Final' ext.h2_version = '1.4.199' // Update docs if renamed or removed. ext.rxjava_version = '1.3.8' - ext.dokka_version = '0.9.17' + ext.dokka_version = '0.10.1' ext.eddsa_version = '0.3.0' ext.dependency_checker_version = '5.2.0' ext.commons_collections_version = '4.3' diff --git a/docs/build.gradle b/docs/build.gradle index aa50560300..94fd4e6043 100644 --- a/docs/build.gradle +++ b/docs/build.gradle @@ -5,6 +5,10 @@ apply plugin: 'net.corda.plugins.publish-utils' apply plugin: 'maven-publish' apply plugin: 'com.jfrog.artifactory' +dependencies { + compile rootProject +} + def internalPackagePrefixes(sourceDirs) { def prefixes = [] // Kotlin allows packages to deviate from the directory structure, but let's assume they don't: @@ -36,10 +40,13 @@ task dokkaJavadoc(type: org.jetbrains.dokka.gradle.DokkaTask) { } [dokka, dokkaJavadoc].collect { - it.configure { + it.configuration { moduleName = 'corda' - processConfigurations = ['compile'] - sourceDirs = dokkaSourceDirs + dokkaSourceDirs.collect { sourceDir -> + sourceRoot { + path = sourceDir.path + } + } includes = ['packages.md'] jdkVersion = 8 externalDocumentationLink { @@ -52,7 +59,7 @@ task dokkaJavadoc(type: org.jetbrains.dokka.gradle.DokkaTask) { url = new URL("https://www.bouncycastle.org/docs/docs1.5on/") } internalPackagePrefixes.collect { packagePrefix -> - packageOptions { + perPackageOption { prefix = packagePrefix suppress = true } From 69d87df56260496056547eb8baafa177fbda67f1 Mon Sep 17 00:00:00 2001 From: Ronan Browne Date: Tue, 8 Feb 2022 16:19:12 +0000 Subject: [PATCH 5/5] ENT-6495: bump java version (#7058) * NOTICK: bump java version to later java8 * NOTICK: bump to java 8 --- docker/src/docker/Dockerfile | 4 ++-- docker/src/docker/Dockerfile-debug | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docker/src/docker/Dockerfile b/docker/src/docker/Dockerfile index d3d287a750..41be49ad20 100644 --- a/docker/src/docker/Dockerfile +++ b/docker/src/docker/Dockerfile @@ -1,4 +1,4 @@ -FROM azul/zulu-openjdk:8u192 +FROM azul/zulu-openjdk:8u312 ## Add packages, clean cache, create dirs, create corda user and change ownership RUN apt-get update && \ @@ -63,4 +63,4 @@ COPY --chown=corda:corda starting-node.conf /opt/corda/starting-node.conf USER "corda" EXPOSE ${MY_P2P_PORT} ${MY_RPC_PORT} ${MY_RPC_ADMIN_PORT} WORKDIR /opt/corda -CMD ["run-corda"] \ No newline at end of file +CMD ["run-corda"] diff --git a/docker/src/docker/Dockerfile-debug b/docker/src/docker/Dockerfile-debug index 5b0c7bbb1f..b961b4570d 100644 --- a/docker/src/docker/Dockerfile-debug +++ b/docker/src/docker/Dockerfile-debug @@ -1,4 +1,4 @@ -FROM azul/zulu-openjdk:8u192 +FROM azul/zulu-openjdk:8u312 ## Add packages, clean cache, create dirs, create corda user and change ownership RUN apt-get update && \