mirror of
https://github.com/corda/corda.git
synced 2024-12-20 21:43:14 +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.testing.internal.fakeAttachment
|
||||||
import net.corda.coretesting.internal.rigorousMock
|
import net.corda.coretesting.internal.rigorousMock
|
||||||
import net.corda.testing.internal.TestingNamedCacheFactory
|
import net.corda.testing.internal.TestingNamedCacheFactory
|
||||||
|
import org.assertj.core.api.Assertions.fail
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
import org.junit.Rule
|
import org.junit.Rule
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
@ -36,6 +37,7 @@ import kotlin.test.assertNotEquals
|
|||||||
@RunWith(Parameterized::class)
|
@RunWith(Parameterized::class)
|
||||||
class TransactionTests(private val digestService : DigestService) {
|
class TransactionTests(private val digestService : DigestService) {
|
||||||
private companion object {
|
private companion object {
|
||||||
|
const val ISOLATED_JAR = "isolated-4.0.jar"
|
||||||
val DUMMY_KEY_1 = generateKeyPair()
|
val DUMMY_KEY_1 = generateKeyPair()
|
||||||
val DUMMY_KEY_2 = generateKeyPair()
|
val DUMMY_KEY_2 = generateKeyPair()
|
||||||
val DUMMY_CASH_ISSUER_KEY = entropyToKeyPair(BigInteger.valueOf(10))
|
val DUMMY_CASH_ISSUER_KEY = entropyToKeyPair(BigInteger.valueOf(10))
|
||||||
@ -200,15 +202,15 @@ class TransactionTests(private val digestService : DigestService) {
|
|||||||
|
|
||||||
val outputs = listOf(outState)
|
val outputs = listOf(outState)
|
||||||
val commands = emptyList<CommandWithParties<CommandData>>()
|
val commands = emptyList<CommandWithParties<CommandData>>()
|
||||||
val attachments = listOf(object : AbstractAttachment({
|
val attachments = listOf(ContractAttachment(object : AbstractAttachment({
|
||||||
AttachmentsClassLoaderTests::class.java.getResource("isolated-4.0.jar").openStream().readBytes()
|
(AttachmentsClassLoaderTests::class.java.getResource(ISOLATED_JAR) ?: fail("Missing $ISOLATED_JAR")).openStream().readBytes()
|
||||||
}, TESTDSL_UPLOADER) {
|
}, TESTDSL_UPLOADER) {
|
||||||
@Suppress("OverridingDeprecatedMember")
|
@Suppress("OverridingDeprecatedMember")
|
||||||
override val signers: List<Party> = emptyList()
|
override val signers: List<Party> = emptyList()
|
||||||
override val signerKeys: List<PublicKey> = emptyList()
|
override val signerKeys: List<PublicKey> = emptyList()
|
||||||
override val size: Int = 1234
|
override val size: Int = 1234
|
||||||
override val id: SecureHash = SecureHash.zeroHash
|
override val id: SecureHash = SecureHash.zeroHash
|
||||||
})
|
}, DummyContract.PROGRAM_ID))
|
||||||
val id = digestService.randomHash()
|
val id = digestService.randomHash()
|
||||||
val timeWindow: TimeWindow? = null
|
val timeWindow: TimeWindow? = null
|
||||||
val privacySalt = PrivacySalt(digestService.digestLength)
|
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.
|
* 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
|
const val MAX_NUMBER_OF_KEYS_IN_SIGNATURE_CONSTRAINT = 20
|
||||||
|
private const val DJVM_SANDBOX_PREFIX = "sandbox."
|
||||||
|
|
||||||
private val log = loggerFor<AttachmentConstraint>()
|
private val log = loggerFor<AttachmentConstraint>()
|
||||||
|
|
||||||
@ -29,10 +30,14 @@ val Attachment.contractVersion: Version get() = if (this is ContractAttachment)
|
|||||||
val ContractState.requiredContractClassName: String? get() {
|
val ContractState.requiredContractClassName: String? get() {
|
||||||
val annotation = javaClass.getAnnotation(BelongsToContract::class.java)
|
val annotation = javaClass.getAnnotation(BelongsToContract::class.java)
|
||||||
if (annotation != null) {
|
if (annotation != null) {
|
||||||
return annotation.value.java.typeName
|
return annotation.value.java.typeName.removePrefix(DJVM_SANDBOX_PREFIX)
|
||||||
}
|
}
|
||||||
val enclosingClass = javaClass.enclosingClass ?: return null
|
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.StateAndRef
|
||||||
import net.corda.core.contracts.StateRef
|
import net.corda.core.contracts.StateRef
|
||||||
import net.corda.core.contracts.TransactionState
|
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.ConflictingAttachmentsRejection
|
||||||
import net.corda.core.contracts.TransactionVerificationException.ConstraintPropagationRejection
|
import net.corda.core.contracts.TransactionVerificationException.ConstraintPropagationRejection
|
||||||
import net.corda.core.contracts.TransactionVerificationException.ContractCreationError
|
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.crypto.SecureHash
|
||||||
import net.corda.core.internal.rules.StateContractValidationEnforcementRule
|
import net.corda.core.internal.rules.StateContractValidationEnforcementRule
|
||||||
import net.corda.core.transactions.LedgerTransaction
|
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.Function
|
||||||
import java.util.function.Supplier
|
import java.util.function.Supplier
|
||||||
|
|
||||||
@ -47,16 +48,54 @@ interface TransactionVerifierServiceInternal {
|
|||||||
*/
|
*/
|
||||||
fun LedgerTransaction.prepareVerify(attachments: List<Attachment>) = internalPrepareVerify(attachments)
|
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
|
* 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.
|
* 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 inputStates: List<TransactionState<*>> = ltx.inputs.map(StateAndRef<ContractState>::state)
|
||||||
private val allStates: List<TransactionState<*>> = inputStates + ltx.references.map(StateAndRef<ContractState>::state) + ltx.outputs
|
private val allStates: List<TransactionState<*>> = inputStates + ltx.references.map(StateAndRef<ContractState>::state) + ltx.outputs
|
||||||
|
|
||||||
companion object {
|
private companion object {
|
||||||
val logger = contextLogger()
|
private val logger = loggerFor<Validator>()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -66,7 +105,7 @@ abstract class Verifier(val ltx: LedgerTransaction, protected val transactionCla
|
|||||||
*
|
*
|
||||||
* @throws net.corda.core.contracts.TransactionVerificationException
|
* @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"
|
// 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.
|
// list, the contents of which need to be deserialized under the correct classloader.
|
||||||
checkNoNotaryChange()
|
checkNoNotaryChange()
|
||||||
@ -93,8 +132,7 @@ abstract class Verifier(val ltx: LedgerTransaction, protected val transactionCla
|
|||||||
// 4. Check that the [TransactionState] objects are correctly formed.
|
// 4. Check that the [TransactionState] objects are correctly formed.
|
||||||
validateStatesAgainstContract()
|
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.
|
// 5. Final step will be to run the contract code.
|
||||||
verifyContracts()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun checkTransactionWithTimeWindowIsNotarised() {
|
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.
|
* It makes sure there is one and only one.
|
||||||
* This is an important piece of the security of transactions.
|
* This is an important piece of the security of transactions.
|
||||||
*/
|
*/
|
||||||
|
@Suppress("ThrowsCount")
|
||||||
private fun getUniqueContractAttachmentsByContract(): Map<ContractClassName, ContractAttachment> {
|
private fun getUniqueContractAttachmentsByContract(): Map<ContractClassName, ContractAttachment> {
|
||||||
val contractClasses = allStates.mapTo(LinkedHashSet(), TransactionState<*>::contract)
|
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
|
// b -> c and c -> b
|
||||||
// c -> a b -> a
|
// c -> a b -> a
|
||||||
// and form a full cycle, meaning that the bi-directionality property is satisfied.
|
// and form a full cycle, meaning that the bi-directionality property is satisfied.
|
||||||
|
@Suppress("ThrowsCount")
|
||||||
private fun checkBidirectionalOutputEncumbrances(statesAndEncumbrance: List<Pair<Int, Int>>) {
|
private fun checkBidirectionalOutputEncumbrances(statesAndEncumbrance: List<Pair<Int, Int>>) {
|
||||||
// [Set] of "from" (encumbered states).
|
// [Set] of "from" (encumbered states).
|
||||||
val encumberedSet = mutableSetOf<Int>()
|
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 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).
|
* - 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>) {
|
private fun verifyConstraintsValidity(contractAttachmentsByContract: Map<ContractClassName, ContractAttachment>) {
|
||||||
|
|
||||||
// First check that the constraints are valid.
|
// 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)
|
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")
|
@Suppress("TooGenericExceptionCaught")
|
||||||
@KeepForDJVM
|
@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.
|
// This constructor is used inside the DJVM's sandbox.
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
constructor() : this(ClassLoader.getSystemClassLoader())
|
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>) {
|
override fun apply(transactionFactory: Supplier<LedgerTransaction>) {
|
||||||
var firstLtx: LedgerTransaction? = null
|
var firstLtx: LedgerTransaction? = null
|
||||||
|
|
||||||
transactionFactory.get().let { ltx ->
|
transactionFactory.get().let { ltx ->
|
||||||
firstLtx = 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)
|
generateContracts(ltx)
|
||||||
}.forEach { contract ->
|
}.forEach { contract ->
|
||||||
val ltx = firstLtx ?: transactionFactory.get()
|
val ltx = firstLtx ?: transactionFactory.get()
|
||||||
firstLtx = null
|
firstLtx = null
|
||||||
try {
|
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)
|
contract.verify(ltx)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
throw ContractRejection(ltx.id, contract, e)
|
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.crypto.SecureHash
|
||||||
import net.corda.core.flows.FlowLogic
|
import net.corda.core.flows.FlowLogic
|
||||||
import net.corda.core.identity.Party
|
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.SerializedStateAndRef
|
||||||
import net.corda.core.internal.Verifier
|
import net.corda.core.internal.Verifier
|
||||||
import net.corda.core.internal.castIfPossible
|
import net.corda.core.internal.castIfPossible
|
||||||
@ -824,7 +824,7 @@ private constructor(
|
|||||||
private class BasicVerifier(
|
private class BasicVerifier(
|
||||||
ltx: LedgerTransaction,
|
ltx: LedgerTransaction,
|
||||||
private val serializationContext: SerializationContext
|
private val serializationContext: SerializationContext
|
||||||
) : Verifier(ltx, serializationContext.deserializationClassLoader) {
|
) : AbstractVerifier(ltx, serializationContext.deserializationClassLoader) {
|
||||||
|
|
||||||
init {
|
init {
|
||||||
// This is a sanity check: We should only instantiate this
|
// 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.
|
// Fetch these commands' signing parties from the database.
|
||||||
// Corda forbids database access during contract verification,
|
// Corda forbids database access during contract verification,
|
||||||
// and so we must load the commands here eagerly instead.
|
// 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()
|
ltx.commands.eagerDeserialise()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override val transaction: Supplier<LedgerTransaction>
|
||||||
|
get() = Supplier(::createTransaction)
|
||||||
|
|
||||||
private fun createTransaction(): LedgerTransaction {
|
private fun createTransaction(): LedgerTransaction {
|
||||||
// Deserialize all relevant classes using the serializationContext.
|
// Deserialize all relevant classes using the serializationContext.
|
||||||
return SerializationFactory.defaultFactory.withCurrentContext(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!
|
* THIS CLASS IS NOT PUBLIC API, AND IS DELIBERATELY PRIVATE!
|
||||||
*/
|
*/
|
||||||
|
@Suppress("unused_parameter")
|
||||||
@CordaInternal
|
@CordaInternal
|
||||||
private class NoOpVerifier(ltx: LedgerTransaction, serializationContext: SerializationContext)
|
private class NoOpVerifier(ltx: LedgerTransaction, serializationContext: SerializationContext) : Verifier {
|
||||||
: Verifier(ltx, serializationContext.deserializationClassLoader) {
|
|
||||||
// Invoking LedgerTransaction.verify() from Contract.verify(LedgerTransaction)
|
// Invoking LedgerTransaction.verify() from Contract.verify(LedgerTransaction)
|
||||||
// will execute this function. But why would anyone do that?!
|
// 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.crypto.SecureHash
|
||||||
import net.corda.core.identity.Party
|
import net.corda.core.identity.Party
|
||||||
import net.corda.core.node.NetworkParameters
|
import net.corda.core.node.NetworkParameters
|
||||||
|
import net.corda.core.serialization.SerializationContext
|
||||||
import net.corda.core.serialization.internal.AttachmentsClassLoaderCache
|
import net.corda.core.serialization.internal.AttachmentsClassLoaderCache
|
||||||
import net.corda.core.transactions.ComponentGroup
|
import net.corda.core.transactions.ComponentGroup
|
||||||
import net.corda.core.transactions.LedgerTransaction
|
import net.corda.core.transactions.LedgerTransaction
|
||||||
import net.corda.core.transactions.WireTransaction
|
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.
|
* 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,
|
isAttachmentTrusted: (Attachment) -> Boolean,
|
||||||
attachmentsClassLoaderCache: AttachmentsClassLoaderCache,
|
attachmentsClassLoaderCache: AttachmentsClassLoaderCache,
|
||||||
digestService: DigestService = DigestService.default
|
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 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)
|
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.Attachment
|
||||||
import net.corda.core.contracts.BrokenAttachmentException
|
import net.corda.core.contracts.BrokenAttachmentException
|
||||||
|
import net.corda.core.contracts.ContractAttachment
|
||||||
import net.corda.core.crypto.SecureHash
|
import net.corda.core.crypto.SecureHash
|
||||||
import net.corda.core.identity.Party
|
import net.corda.core.identity.Party
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
@ -16,6 +17,12 @@ private const val ID_IDX = 2
|
|||||||
private const val ATTACHMENT_IDX = 3
|
private const val ATTACHMENT_IDX = 3
|
||||||
private const val STREAMER_IDX = 4
|
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>?> {
|
class AttachmentBuilder : Function<Array<Any>?, List<Attachment>?> {
|
||||||
private val attachments = mutableListOf<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>? {
|
override fun apply(inputs: Array<Any>?): List<Attachment>? {
|
||||||
|
@Suppress("unchecked_cast")
|
||||||
return if (inputs == null) {
|
return if (inputs == null) {
|
||||||
unmodifiable(attachments)
|
unmodifiable(attachments)
|
||||||
} else {
|
} else {
|
||||||
@Suppress("unchecked_cast")
|
var attachment: Attachment = SandboxAttachment(
|
||||||
attachments.add(SandboxAttachment(
|
|
||||||
signerKeys = inputs[SIGNER_KEYS_IDX] as List<PublicKey>,
|
signerKeys = inputs[SIGNER_KEYS_IDX] as List<PublicKey>,
|
||||||
size = inputs[SIZE_IDX] as Int,
|
size = inputs[SIZE_IDX] as Int,
|
||||||
id = inputs[ID_IDX] as SecureHash,
|
id = inputs[ID_IDX] as SecureHash,
|
||||||
attachment = inputs[ATTACHMENT_IDX],
|
attachment = inputs[ATTACHMENT_IDX],
|
||||||
streamer = inputs[STREAMER_IDX] as Function<in Any, out InputStream>
|
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
|
null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -47,7 +67,7 @@ class AttachmentBuilder : Function<Array<Any>?, List<Attachment>?> {
|
|||||||
/**
|
/**
|
||||||
* This represents an [Attachment] from within the sandbox.
|
* This represents an [Attachment] from within the sandbox.
|
||||||
*/
|
*/
|
||||||
class SandboxAttachment(
|
private class SandboxAttachment(
|
||||||
override val signerKeys: List<PublicKey>,
|
override val signerKeys: List<PublicKey>,
|
||||||
override val size: Int,
|
override val size: Int,
|
||||||
override val id: SecureHash,
|
override val id: SecureHash,
|
||||||
|
@ -95,9 +95,11 @@ class MutatorContract : Contract {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class ExtraSpecialise(ltx: LedgerTransaction, ctx: SerializationContext)
|
private class ExtraSpecialise(private val ltx: LedgerTransaction, private val ctx: SerializationContext) : Verifier {
|
||||||
: Verifier(ltx, ctx.deserializationClassLoader) {
|
override fun verify() {
|
||||||
override fun verifyContracts() {}
|
ltx.inputStates.forEach(::println)
|
||||||
|
println(ctx.deserializationClassLoader)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class MutateState(val owner: AbstractParty) : ContractState {
|
class MutateState(val owner: AbstractParty) : ContractState {
|
||||||
|
@ -30,12 +30,12 @@ class CashIssueAndPaymentTest {
|
|||||||
private val configOverrides = mapOf(NodeConfiguration::reloadCheckpointAfterSuspend.name to true)
|
private val configOverrides = mapOf(NodeConfiguration::reloadCheckpointAfterSuspend.name to true)
|
||||||
private val CASH_AMOUNT = 500.DOLLARS
|
private val CASH_AMOUNT = 500.DOLLARS
|
||||||
|
|
||||||
fun parametersFor(): DriverParameters {
|
fun parametersFor(runInProcess: Boolean = false): DriverParameters {
|
||||||
return DriverParameters(
|
return DriverParameters(
|
||||||
systemProperties = mapOf("co.paralleluniverse.fibers.verifyInstrumentation" to "false"),
|
systemProperties = mapOf("co.paralleluniverse.fibers.verifyInstrumentation" to "false"),
|
||||||
portAllocation = incrementalPortAllocation(),
|
portAllocation = incrementalPortAllocation(),
|
||||||
startNodesInProcess = false,
|
startNodesInProcess = runInProcess,
|
||||||
notarySpecs = listOf(NotarySpec(DUMMY_NOTARY_NAME, startInProcess = false, validating = true)),
|
notarySpecs = listOf(NotarySpec(DUMMY_NOTARY_NAME, startInProcess = runInProcess, validating = true)),
|
||||||
notaryCustomOverrides = configOverrides,
|
notaryCustomOverrides = configOverrides,
|
||||||
cordappsForAllNodes = listOf(
|
cordappsForAllNodes = listOf(
|
||||||
findCordapp("net.corda.finance.contracts"),
|
findCordapp("net.corda.finance.contracts"),
|
||||||
|
@ -23,7 +23,7 @@ class ContractCannotMutateTransactionTest {
|
|||||||
private val mutatorFlowCorDapp = cordappWithPackages("net.corda.flows.mutator").signed()
|
private val mutatorFlowCorDapp = cordappWithPackages("net.corda.flows.mutator").signed()
|
||||||
private val mutatorContractCorDapp = cordappWithPackages("net.corda.contracts.mutator").signed()
|
private val mutatorContractCorDapp = cordappWithPackages("net.corda.contracts.mutator").signed()
|
||||||
|
|
||||||
fun driverParameters(runInProcess: Boolean): DriverParameters {
|
fun driverParameters(runInProcess: Boolean = false): DriverParameters {
|
||||||
return DriverParameters(
|
return DriverParameters(
|
||||||
portAllocation = incrementalPortAllocation(),
|
portAllocation = incrementalPortAllocation(),
|
||||||
startNodesInProcess = runInProcess,
|
startNodesInProcess = runInProcess,
|
||||||
@ -35,7 +35,7 @@ class ContractCannotMutateTransactionTest {
|
|||||||
|
|
||||||
@Test(timeout = 300_000)
|
@Test(timeout = 300_000)
|
||||||
fun testContractCannotModifyTransaction() {
|
fun testContractCannotModifyTransaction() {
|
||||||
driver(driverParameters(runInProcess = false)) {
|
driver(driverParameters()) {
|
||||||
val alice = startNode(providedName = ALICE_NAME, rpcUsers = listOf(user)).getOrThrow()
|
val alice = startNode(providedName = ALICE_NAME, rpcUsers = listOf(user)).getOrThrow()
|
||||||
val txID = CordaRPCClient(hostAndPort = alice.rpcAddress)
|
val txID = CordaRPCClient(hostAndPort = alice.rpcAddress)
|
||||||
.start(user.username, user.password)
|
.start(user.username, user.password)
|
||||||
|
@ -35,11 +35,11 @@ class ContractWithCordappFixupTest {
|
|||||||
val dependentContractCorDapp = cordappWithPackages("net.corda.contracts.fixup.dependent").signed()
|
val dependentContractCorDapp = cordappWithPackages("net.corda.contracts.fixup.dependent").signed()
|
||||||
val standaloneContractCorDapp = cordappWithPackages("net.corda.contracts.fixup.standalone").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(
|
return DriverParameters(
|
||||||
portAllocation = incrementalPortAllocation(),
|
portAllocation = incrementalPortAllocation(),
|
||||||
startNodesInProcess = false,
|
startNodesInProcess = runInProcess,
|
||||||
notarySpecs = listOf(NotarySpec(DUMMY_NOTARY_NAME, validating = true)),
|
notarySpecs = listOf(NotarySpec(DUMMY_NOTARY_NAME, startInProcess = runInProcess, validating = true)),
|
||||||
cordappsForAllNodes = cordapps,
|
cordappsForAllNodes = cordapps,
|
||||||
systemProperties = mapOf("net.corda.transactionbuilder.missingclass.disabled" to true.toString())
|
systemProperties = mapOf("net.corda.transactionbuilder.missingclass.disabled" to true.toString())
|
||||||
)
|
)
|
||||||
|
@ -46,7 +46,7 @@ class ContractWithCustomSerializerTest(private val runInProcess: Boolean) {
|
|||||||
driver(DriverParameters(
|
driver(DriverParameters(
|
||||||
portAllocation = incrementalPortAllocation(),
|
portAllocation = incrementalPortAllocation(),
|
||||||
startNodesInProcess = runInProcess,
|
startNodesInProcess = runInProcess,
|
||||||
notarySpecs = listOf(NotarySpec(DUMMY_NOTARY_NAME, validating = true)),
|
notarySpecs = listOf(NotarySpec(DUMMY_NOTARY_NAME, startInProcess = runInProcess, validating = true)),
|
||||||
cordappsForAllNodes = listOf(
|
cordappsForAllNodes = listOf(
|
||||||
cordappWithPackages("net.corda.flows.serialization.custom").signed(),
|
cordappWithPackages("net.corda.flows.serialization.custom").signed(),
|
||||||
cordappWithPackages("net.corda.contracts.serialization.custom").signed()
|
cordappWithPackages("net.corda.contracts.serialization.custom").signed()
|
||||||
|
@ -31,11 +31,11 @@ class ContractWithGenericTypeTest {
|
|||||||
@JvmField
|
@JvmField
|
||||||
val user = User("u", "p", setOf(Permissions.all()))
|
val user = User("u", "p", setOf(Permissions.all()))
|
||||||
|
|
||||||
fun parameters(): DriverParameters {
|
fun parameters(runInProcess: Boolean = false): DriverParameters {
|
||||||
return DriverParameters(
|
return DriverParameters(
|
||||||
portAllocation = incrementalPortAllocation(),
|
portAllocation = incrementalPortAllocation(),
|
||||||
startNodesInProcess = false,
|
startNodesInProcess = runInProcess,
|
||||||
notarySpecs = listOf(NotarySpec(DUMMY_NOTARY_NAME, validating = true)),
|
notarySpecs = listOf(NotarySpec(DUMMY_NOTARY_NAME, startInProcess = runInProcess, validating = true)),
|
||||||
cordappsForAllNodes = listOf(
|
cordappsForAllNodes = listOf(
|
||||||
cordappWithPackages("net.corda.flows.serialization.generics").signed(),
|
cordappWithPackages("net.corda.flows.serialization.generics").signed(),
|
||||||
cordappWithPackages("net.corda.contracts.serialization.generics").signed()
|
cordappWithPackages("net.corda.contracts.serialization.generics").signed()
|
||||||
|
@ -45,7 +45,7 @@ class ContractWithMissingCustomSerializerTest(private val runInProcess: Boolean)
|
|||||||
return DriverParameters(
|
return DriverParameters(
|
||||||
portAllocation = incrementalPortAllocation(),
|
portAllocation = incrementalPortAllocation(),
|
||||||
startNodesInProcess = runInProcess,
|
startNodesInProcess = runInProcess,
|
||||||
notarySpecs = listOf(NotarySpec(DUMMY_NOTARY_NAME, validating = true)),
|
notarySpecs = listOf(NotarySpec(DUMMY_NOTARY_NAME, startInProcess = runInProcess, validating = true)),
|
||||||
cordappsForAllNodes = cordapps
|
cordappsForAllNodes = cordapps
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -43,7 +43,7 @@ class ContractWithSerializationWhitelistTest(private val runInProcess: Boolean)
|
|||||||
return DriverParameters(
|
return DriverParameters(
|
||||||
portAllocation = incrementalPortAllocation(),
|
portAllocation = incrementalPortAllocation(),
|
||||||
startNodesInProcess = runInProcess,
|
startNodesInProcess = runInProcess,
|
||||||
notarySpecs = listOf(NotarySpec(DUMMY_NOTARY_NAME, validating = true)),
|
notarySpecs = listOf(NotarySpec(DUMMY_NOTARY_NAME, startInProcess = runInProcess, validating = true)),
|
||||||
cordappsForAllNodes = listOf(contractCordapp, workflowCordapp)
|
cordappsForAllNodes = listOf(contractCordapp, workflowCordapp)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -32,11 +32,11 @@ class DeterministicCashIssueAndPaymentTest {
|
|||||||
@JvmField
|
@JvmField
|
||||||
val djvmSources = DeterministicSourcesRule()
|
val djvmSources = DeterministicSourcesRule()
|
||||||
|
|
||||||
fun parametersFor(djvmSources: DeterministicSourcesRule): DriverParameters {
|
fun parametersFor(djvmSources: DeterministicSourcesRule, runInProcess: Boolean = false): DriverParameters {
|
||||||
return DriverParameters(
|
return DriverParameters(
|
||||||
portAllocation = incrementalPortAllocation(),
|
portAllocation = incrementalPortAllocation(),
|
||||||
startNodesInProcess = false,
|
startNodesInProcess = runInProcess,
|
||||||
notarySpecs = listOf(NotarySpec(DUMMY_NOTARY_NAME, validating = true)),
|
notarySpecs = listOf(NotarySpec(DUMMY_NOTARY_NAME, startInProcess = runInProcess, validating = true)),
|
||||||
notaryCustomOverrides = configOverrides,
|
notaryCustomOverrides = configOverrides,
|
||||||
cordappsForAllNodes = listOf(
|
cordappsForAllNodes = listOf(
|
||||||
findCordapp("net.corda.finance.contracts"),
|
findCordapp("net.corda.finance.contracts"),
|
||||||
|
@ -28,7 +28,7 @@ class DeterministicContractCannotMutateTransactionTest {
|
|||||||
@JvmField
|
@JvmField
|
||||||
val djvmSources = DeterministicSourcesRule()
|
val djvmSources = DeterministicSourcesRule()
|
||||||
|
|
||||||
fun driverParameters(runInProcess: Boolean): DriverParameters {
|
fun driverParameters(runInProcess: Boolean = false): DriverParameters {
|
||||||
return DriverParameters(
|
return DriverParameters(
|
||||||
portAllocation = incrementalPortAllocation(),
|
portAllocation = incrementalPortAllocation(),
|
||||||
startNodesInProcess = runInProcess,
|
startNodesInProcess = runInProcess,
|
||||||
@ -42,7 +42,7 @@ class DeterministicContractCannotMutateTransactionTest {
|
|||||||
|
|
||||||
@Test(timeout = 300_000)
|
@Test(timeout = 300_000)
|
||||||
fun testContractCannotModifyTransaction() {
|
fun testContractCannotModifyTransaction() {
|
||||||
driver(driverParameters(runInProcess = false)) {
|
driver(driverParameters()) {
|
||||||
val alice = startNode(providedName = ALICE_NAME, rpcUsers = listOf(user)).getOrThrow()
|
val alice = startNode(providedName = ALICE_NAME, rpcUsers = listOf(user)).getOrThrow()
|
||||||
val txID = CordaRPCClient(hostAndPort = alice.rpcAddress)
|
val txID = CordaRPCClient(hostAndPort = alice.rpcAddress)
|
||||||
.start(user.username, user.password)
|
.start(user.username, user.password)
|
||||||
|
@ -32,11 +32,11 @@ class DeterministicContractCryptoTest {
|
|||||||
@JvmField
|
@JvmField
|
||||||
val djvmSources = DeterministicSourcesRule()
|
val djvmSources = DeterministicSourcesRule()
|
||||||
|
|
||||||
fun parametersFor(djvmSources: DeterministicSourcesRule): DriverParameters {
|
fun parametersFor(djvmSources: DeterministicSourcesRule, runInProcess: Boolean = false): DriverParameters {
|
||||||
return DriverParameters(
|
return DriverParameters(
|
||||||
portAllocation = incrementalPortAllocation(),
|
portAllocation = incrementalPortAllocation(),
|
||||||
startNodesInProcess = false,
|
startNodesInProcess = runInProcess,
|
||||||
notarySpecs = listOf(NotarySpec(DUMMY_NOTARY_NAME, validating = true)),
|
notarySpecs = listOf(NotarySpec(DUMMY_NOTARY_NAME, startInProcess = runInProcess, validating = true)),
|
||||||
cordappsForAllNodes = listOf(
|
cordappsForAllNodes = listOf(
|
||||||
cordappWithPackages("net.corda.flows.djvm.crypto"),
|
cordappWithPackages("net.corda.flows.djvm.crypto"),
|
||||||
CustomCordapp(
|
CustomCordapp(
|
||||||
|
@ -41,11 +41,11 @@ class DeterministicContractWithCustomSerializerTest {
|
|||||||
@JvmField
|
@JvmField
|
||||||
val contractCordapp = cordappWithPackages("net.corda.contracts.serialization.custom").signed()
|
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(
|
return DriverParameters(
|
||||||
portAllocation = incrementalPortAllocation(),
|
portAllocation = incrementalPortAllocation(),
|
||||||
startNodesInProcess = false,
|
startNodesInProcess = runInProcess,
|
||||||
notarySpecs = listOf(NotarySpec(DUMMY_NOTARY_NAME, validating = true)),
|
notarySpecs = listOf(NotarySpec(DUMMY_NOTARY_NAME, startInProcess = runInProcess, validating = true)),
|
||||||
cordappsForAllNodes = cordapps.toList(),
|
cordappsForAllNodes = cordapps.toList(),
|
||||||
djvmBootstrapSource = djvmSources.bootstrap,
|
djvmBootstrapSource = djvmSources.bootstrap,
|
||||||
djvmCordaSource = djvmSources.corda
|
djvmCordaSource = djvmSources.corda
|
||||||
@ -61,7 +61,7 @@ class DeterministicContractWithCustomSerializerTest {
|
|||||||
|
|
||||||
@Test(timeout=300_000)
|
@Test(timeout=300_000)
|
||||||
fun `test DJVM can verify using custom serializer`() {
|
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 alice = startNode(providedName = ALICE_NAME).getOrThrow()
|
||||||
val txId = assertDoesNotThrow {
|
val txId = assertDoesNotThrow {
|
||||||
alice.rpc.startFlow(::CustomSerializerFlow, Currantsy(GOOD_CURRANTS))
|
alice.rpc.startFlow(::CustomSerializerFlow, Currantsy(GOOD_CURRANTS))
|
||||||
@ -73,7 +73,7 @@ class DeterministicContractWithCustomSerializerTest {
|
|||||||
|
|
||||||
@Test(timeout=300_000)
|
@Test(timeout=300_000)
|
||||||
fun `test DJVM can fail verify using custom serializer`() {
|
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 alice = startNode(providedName = ALICE_NAME).getOrThrow()
|
||||||
val currantsy = Currantsy(BAD_CURRANTS)
|
val currantsy = Currantsy(BAD_CURRANTS)
|
||||||
val ex = assertThrows<DeterministicVerificationException> {
|
val ex = assertThrows<DeterministicVerificationException> {
|
||||||
|
@ -36,11 +36,11 @@ class DeterministicContractWithGenericTypeTest {
|
|||||||
@JvmField
|
@JvmField
|
||||||
val djvmSources = DeterministicSourcesRule()
|
val djvmSources = DeterministicSourcesRule()
|
||||||
|
|
||||||
fun parameters(): DriverParameters {
|
fun parameters(runInProcess: Boolean = false): DriverParameters {
|
||||||
return DriverParameters(
|
return DriverParameters(
|
||||||
portAllocation = incrementalPortAllocation(),
|
portAllocation = incrementalPortAllocation(),
|
||||||
startNodesInProcess = false,
|
startNodesInProcess = runInProcess,
|
||||||
notarySpecs = listOf(NotarySpec(DUMMY_NOTARY_NAME, validating = true)),
|
notarySpecs = listOf(NotarySpec(DUMMY_NOTARY_NAME, startInProcess = runInProcess, validating = true)),
|
||||||
cordappsForAllNodes = listOf(
|
cordappsForAllNodes = listOf(
|
||||||
cordappWithPackages("net.corda.flows.serialization.generics").signed(),
|
cordappWithPackages("net.corda.flows.serialization.generics").signed(),
|
||||||
cordappWithPackages("net.corda.contracts.serialization.generics").signed()
|
cordappWithPackages("net.corda.contracts.serialization.generics").signed()
|
||||||
|
@ -41,11 +41,11 @@ class DeterministicContractWithSerializationWhitelistTest {
|
|||||||
@JvmField
|
@JvmField
|
||||||
val contractCordapp = cordappWithPackages("net.corda.contracts.djvm.whitelist").signed()
|
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(
|
return DriverParameters(
|
||||||
portAllocation = incrementalPortAllocation(),
|
portAllocation = incrementalPortAllocation(),
|
||||||
startNodesInProcess = false,
|
startNodesInProcess = runInProcess,
|
||||||
notarySpecs = listOf(NotarySpec(DUMMY_NOTARY_NAME, validating = true)),
|
notarySpecs = listOf(NotarySpec(DUMMY_NOTARY_NAME, startInProcess = runInProcess, validating = true)),
|
||||||
cordappsForAllNodes = cordapps.toList(),
|
cordappsForAllNodes = cordapps.toList(),
|
||||||
djvmBootstrapSource = djvmSources.bootstrap,
|
djvmBootstrapSource = djvmSources.bootstrap,
|
||||||
djvmCordaSource = djvmSources.corda
|
djvmCordaSource = djvmSources.corda
|
||||||
@ -61,7 +61,7 @@ class DeterministicContractWithSerializationWhitelistTest {
|
|||||||
|
|
||||||
@Test(timeout=300_000)
|
@Test(timeout=300_000)
|
||||||
fun `test DJVM can verify using whitelist`() {
|
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 alice = startNode(providedName = ALICE_NAME).getOrThrow()
|
||||||
val txId = assertDoesNotThrow {
|
val txId = assertDoesNotThrow {
|
||||||
alice.rpc.startFlow(::DeterministicWhitelistFlow, WhitelistData(GOOD_VALUE))
|
alice.rpc.startFlow(::DeterministicWhitelistFlow, WhitelistData(GOOD_VALUE))
|
||||||
@ -73,7 +73,7 @@ class DeterministicContractWithSerializationWhitelistTest {
|
|||||||
|
|
||||||
@Test(timeout=300_000)
|
@Test(timeout=300_000)
|
||||||
fun `test DJVM can fail verify using whitelist`() {
|
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 alice = startNode(providedName = ALICE_NAME).getOrThrow()
|
||||||
val badData = WhitelistData(BAD_VALUE)
|
val badData = WhitelistData(BAD_VALUE)
|
||||||
val ex = assertThrows<DeterministicVerificationException> {
|
val ex = assertThrows<DeterministicVerificationException> {
|
||||||
|
@ -34,7 +34,7 @@ class DeterministicEvilContractCannotModifyStatesTest {
|
|||||||
@JvmField
|
@JvmField
|
||||||
val djvmSources = DeterministicSourcesRule()
|
val djvmSources = DeterministicSourcesRule()
|
||||||
|
|
||||||
fun driverParameters(runInProcess: Boolean): DriverParameters {
|
fun driverParameters(runInProcess: Boolean = false): DriverParameters {
|
||||||
return DriverParameters(
|
return DriverParameters(
|
||||||
portAllocation = incrementalPortAllocation(),
|
portAllocation = incrementalPortAllocation(),
|
||||||
startNodesInProcess = runInProcess,
|
startNodesInProcess = runInProcess,
|
||||||
@ -53,7 +53,7 @@ class DeterministicEvilContractCannotModifyStatesTest {
|
|||||||
@Test(timeout = 300_000)
|
@Test(timeout = 300_000)
|
||||||
fun testContractThatTriesToModifyStates() {
|
fun testContractThatTriesToModifyStates() {
|
||||||
val evilData = MutableDataObject(5000)
|
val evilData = MutableDataObject(5000)
|
||||||
driver(driverParameters(runInProcess = false)) {
|
driver(driverParameters()) {
|
||||||
val alice = startNode(providedName = ALICE_NAME, rpcUsers = listOf(user)).getOrThrow()
|
val alice = startNode(providedName = ALICE_NAME, rpcUsers = listOf(user)).getOrThrow()
|
||||||
val ex = assertFailsWith<DeterministicVerificationException> {
|
val ex = assertFailsWith<DeterministicVerificationException> {
|
||||||
CordaRPCClient(hostAndPort = alice.rpcAddress)
|
CordaRPCClient(hostAndPort = alice.rpcAddress)
|
||||||
|
@ -35,11 +35,11 @@ class NonDeterministicContractVerifyTest {
|
|||||||
@JvmField
|
@JvmField
|
||||||
val djvmSources = DeterministicSourcesRule()
|
val djvmSources = DeterministicSourcesRule()
|
||||||
|
|
||||||
fun parametersFor(djvmSources: DeterministicSourcesRule): DriverParameters {
|
fun parametersFor(djvmSources: DeterministicSourcesRule, runInProcess: Boolean = false): DriverParameters {
|
||||||
return DriverParameters(
|
return DriverParameters(
|
||||||
portAllocation = incrementalPortAllocation(),
|
portAllocation = incrementalPortAllocation(),
|
||||||
startNodesInProcess = false,
|
startNodesInProcess = runInProcess,
|
||||||
notarySpecs = listOf(NotarySpec(DUMMY_NOTARY_NAME, validating = true)),
|
notarySpecs = listOf(NotarySpec(DUMMY_NOTARY_NAME, startInProcess = runInProcess, validating = true)),
|
||||||
cordappsForAllNodes = listOf(
|
cordappsForAllNodes = listOf(
|
||||||
cordappWithPackages("net.corda.flows.djvm.broken"),
|
cordappWithPackages("net.corda.flows.djvm.broken"),
|
||||||
CustomCordapp(
|
CustomCordapp(
|
||||||
|
@ -31,11 +31,11 @@ class SandboxAttachmentsTest {
|
|||||||
@JvmField
|
@JvmField
|
||||||
val djvmSources = DeterministicSourcesRule()
|
val djvmSources = DeterministicSourcesRule()
|
||||||
|
|
||||||
fun parametersFor(djvmSources: DeterministicSourcesRule): DriverParameters {
|
fun parametersFor(djvmSources: DeterministicSourcesRule, runInProcess: Boolean = false): DriverParameters {
|
||||||
return DriverParameters(
|
return DriverParameters(
|
||||||
portAllocation = incrementalPortAllocation(),
|
portAllocation = incrementalPortAllocation(),
|
||||||
startNodesInProcess = false,
|
startNodesInProcess = runInProcess,
|
||||||
notarySpecs = listOf(NotarySpec(DUMMY_NOTARY_NAME, validating = true)),
|
notarySpecs = listOf(NotarySpec(DUMMY_NOTARY_NAME, startInProcess = runInProcess, validating = true)),
|
||||||
cordappsForAllNodes = listOf(
|
cordappsForAllNodes = listOf(
|
||||||
cordappWithPackages("net.corda.flows.djvm.attachment"),
|
cordappWithPackages("net.corda.flows.djvm.attachment"),
|
||||||
CustomCordapp(
|
CustomCordapp(
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package net.corda.node.internal.djvm
|
package net.corda.node.internal.djvm
|
||||||
|
|
||||||
import net.corda.core.contracts.Attachment
|
import net.corda.core.contracts.Attachment
|
||||||
|
import net.corda.core.contracts.ContractAttachment
|
||||||
import net.corda.core.serialization.serialize
|
import net.corda.core.serialization.serialize
|
||||||
import net.corda.djvm.rewiring.SandboxClassLoader
|
import net.corda.djvm.rewiring.SandboxClassLoader
|
||||||
import net.corda.node.djvm.AttachmentBuilder
|
import net.corda.node.djvm.AttachmentBuilder
|
||||||
@ -19,14 +20,30 @@ class AttachmentFactory(
|
|||||||
fun toSandbox(attachments: List<Attachment>): Any? {
|
fun toSandbox(attachments: List<Attachment>): Any? {
|
||||||
val builder = taskFactory.apply(AttachmentBuilder::class.java)
|
val builder = taskFactory.apply(AttachmentBuilder::class.java)
|
||||||
for (attachment in attachments) {
|
for (attachment in attachments) {
|
||||||
builder.apply(arrayOf(
|
builder.apply(generateArgsFor(attachment))
|
||||||
serializer.deserialize(attachment.signerKeys.serialize()),
|
|
||||||
sandboxBasicInput.apply(attachment.size),
|
|
||||||
serializer.deserialize(attachment.id.serialize()),
|
|
||||||
attachment,
|
|
||||||
sandboxOpenAttachment
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
return builder.apply(null)
|
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.TransactionState
|
||||||
import net.corda.core.contracts.TransactionVerificationException
|
import net.corda.core.contracts.TransactionVerificationException
|
||||||
import net.corda.core.crypto.SecureHash
|
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.Verifier
|
||||||
import net.corda.core.internal.getNamesOfClassesImplementing
|
import net.corda.core.internal.getNamesOfClassesImplementing
|
||||||
import net.corda.core.serialization.SerializationCustomSerializer
|
import net.corda.core.serialization.SerializationCustomSerializer
|
||||||
import net.corda.core.serialization.SerializationWhitelist
|
import net.corda.core.serialization.SerializationWhitelist
|
||||||
import net.corda.core.serialization.serialize
|
import net.corda.core.serialization.serialize
|
||||||
import net.corda.core.transactions.LedgerTransaction
|
import net.corda.core.transactions.LedgerTransaction
|
||||||
|
import net.corda.core.utilities.contextLogger
|
||||||
import net.corda.djvm.SandboxConfiguration
|
import net.corda.djvm.SandboxConfiguration
|
||||||
import net.corda.djvm.execution.ExecutionSummary
|
import net.corda.djvm.execution.ExecutionSummary
|
||||||
import net.corda.djvm.execution.IsolatedTask
|
import net.corda.djvm.execution.IsolatedTask
|
||||||
@ -26,10 +27,14 @@ import java.util.function.Function
|
|||||||
import kotlin.collections.LinkedHashSet
|
import kotlin.collections.LinkedHashSet
|
||||||
|
|
||||||
class DeterministicVerifier(
|
class DeterministicVerifier(
|
||||||
ltx: LedgerTransaction,
|
private val ltx: LedgerTransaction,
|
||||||
transactionClassLoader: ClassLoader,
|
private val transactionClassLoader: ClassLoader,
|
||||||
private val sandboxConfiguration: SandboxConfiguration
|
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
|
* Read the whitelisted classes without using the [java.util.ServiceLoader] mechanism
|
||||||
* because the whitelists themselves are untrusted.
|
* 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 customSerializerNames = getNamesOfClassesImplementing(transactionClassLoader, SerializationCustomSerializer::class.java)
|
||||||
val serializationWhitelistNames = getSerializationWhitelistNames(transactionClassLoader)
|
val serializationWhitelistNames = getSerializationWhitelistNames(transactionClassLoader)
|
||||||
val result = IsolatedTask(ltx.id.toString(), sandboxConfiguration).run<Any>(Function { classLoader ->
|
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...
|
// Now execute the contract verifier task within the sandbox...
|
||||||
verifier.apply(sandboxTx)
|
verifier.apply(sandboxTx)
|
||||||
@ -128,7 +133,7 @@ class DeterministicVerifier(
|
|||||||
val sandboxEx = SandboxException(
|
val sandboxEx = SandboxException(
|
||||||
Message.getMessageFromException(this),
|
Message.getMessageFromException(this),
|
||||||
result.identifier,
|
result.identifier,
|
||||||
ClassSource.fromClassName(ContractVerifier::class.java.name),
|
ClassSource.fromClassName(TransactionVerifier::class.java.name),
|
||||||
ExecutionSummary(result.costs),
|
ExecutionSummary(result.costs),
|
||||||
this
|
this
|
||||||
)
|
)
|
||||||
|
Loading…
Reference in New Issue
Block a user