mirror of
https://github.com/corda/corda.git
synced 2024-12-18 20:47:57 +00:00
This commit is contained in:
parent
7752fc8c9d
commit
758a69f904
@ -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<CommandWithParties<CommandData>>()
|
||||
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<Party> = emptyList()
|
||||
override val signerKeys: List<PublicKey> = 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)
|
||||
|
@ -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<AttachmentConstraint>()
|
||||
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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<Attachment>) = 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<LedgerTransaction>
|
||||
|
||||
protected companion object {
|
||||
@JvmField
|
||||
val logger = loggerFor<Verifier>()
|
||||
}
|
||||
|
||||
/**
|
||||
* 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<TransactionState<*>> = ltx.inputs.map(StateAndRef<ContractState>::state)
|
||||
private val allStates: List<TransactionState<*>> = inputStates + ltx.references.map(StateAndRef<ContractState>::state) + ltx.outputs
|
||||
|
||||
companion object {
|
||||
val logger = contextLogger()
|
||||
private companion object {
|
||||
private val logger = loggerFor<Validator>()
|
||||
}
|
||||
|
||||
/**
|
||||
@ -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<ContractClassName, ContractAttachment> {
|
||||
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<Pair<Int, Int>>) {
|
||||
// [Set] of "from" (encumbered states).
|
||||
val encumberedSet = mutableSetOf<Int>()
|
||||
@ -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<ContractClassName, ContractAttachment>) {
|
||||
|
||||
// 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<Supplier<LedgerTransaction>, Unit> {
|
||||
class TransactionVerifier(private val transactionClassLoader: ClassLoader) : Function<Supplier<LedgerTransaction>, 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<LedgerTransaction>) {
|
||||
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)
|
||||
|
@ -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<LedgerTransaction>
|
||||
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() {}
|
||||
}
|
||||
|
@ -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<LedgerTransaction>
|
||||
get() = Supplier { ltx }
|
||||
}
|
||||
|
@ -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<Array<Any>?, List<Attachment>?> {
|
||||
private val attachments = mutableListOf<Attachment>()
|
||||
|
||||
@ -28,17 +35,30 @@ class AttachmentBuilder : Function<Array<Any>?, List<Attachment>?> {
|
||||
}
|
||||
|
||||
override fun apply(inputs: Array<Any>?): List<Attachment>? {
|
||||
@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<PublicKey>,
|
||||
size = inputs[SIZE_IDX] as Int,
|
||||
id = inputs[ID_IDX] as SecureHash,
|
||||
attachment = inputs[ATTACHMENT_IDX],
|
||||
streamer = inputs[STREAMER_IDX] as Function<in Any, out InputStream>
|
||||
))
|
||||
)
|
||||
|
||||
if (inputs.size > VERSION_IDX) {
|
||||
attachment = ContractAttachment.create(
|
||||
attachment = attachment,
|
||||
contract = inputs[CONTRACT_IDX] as String,
|
||||
additionalContracts = (inputs[ADDITIONAL_CONTRACT_IDX] as Array<String>).toSet(),
|
||||
uploader = inputs[UPLOADER_IDX] as? String,
|
||||
signerKeys = inputs[CONTRACT_SIGNER_KEYS_IDX] as List<PublicKey>,
|
||||
version = inputs[VERSION_IDX] as Int
|
||||
)
|
||||
}
|
||||
|
||||
attachments.add(attachment)
|
||||
null
|
||||
}
|
||||
}
|
||||
@ -47,7 +67,7 @@ class AttachmentBuilder : Function<Array<Any>?, List<Attachment>?> {
|
||||
/**
|
||||
* This represents an [Attachment] from within the sandbox.
|
||||
*/
|
||||
class SandboxAttachment(
|
||||
private class SandboxAttachment(
|
||||
override val signerKeys: List<PublicKey>,
|
||||
override val size: Int,
|
||||
override val id: SecureHash,
|
||||
|
@ -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 {
|
||||
|
@ -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"),
|
||||
|
@ -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)
|
||||
|
@ -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<TestCordapp>): DriverParameters {
|
||||
fun driverParameters(cordapps: List<TestCordapp>, 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())
|
||||
)
|
||||
|
@ -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()
|
||||
|
@ -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()
|
||||
|
@ -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
|
||||
)
|
||||
}
|
||||
|
@ -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)
|
||||
)
|
||||
}
|
||||
|
@ -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"),
|
||||
|
@ -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)
|
||||
|
@ -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(
|
||||
|
@ -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<TestCordapp>, 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<DeterministicVerificationException> {
|
||||
|
@ -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()
|
||||
|
@ -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<TestCordapp>, 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<DeterministicVerificationException> {
|
||||
|
@ -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<DeterministicVerificationException> {
|
||||
CordaRPCClient(hostAndPort = alice.rpcAddress)
|
||||
|
@ -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(
|
||||
|
@ -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(
|
||||
|
@ -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<Attachment>): 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<Any?> {
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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<Any>(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
|
||||
)
|
||||
|
Loading…
Reference in New Issue
Block a user