mirror of
https://github.com/corda/corda.git
synced 2025-06-01 15:10:54 +00:00
CORDA-2128: Moved constraints and attachments stuff out of the public API that shouldn't be there (#4460)
This commit is contained in:
parent
60d215aaa8
commit
00672f97fa
@ -1,19 +1,14 @@
|
|||||||
package net.corda.core.contracts
|
package net.corda.core.contracts
|
||||||
|
|
||||||
import net.corda.core.CordaInternal
|
|
||||||
import net.corda.core.DoNotImplement
|
import net.corda.core.DoNotImplement
|
||||||
import net.corda.core.KeepForDJVM
|
import net.corda.core.KeepForDJVM
|
||||||
import net.corda.core.contracts.AlwaysAcceptAttachmentConstraint.isSatisfiedBy
|
import net.corda.core.contracts.AlwaysAcceptAttachmentConstraint.isSatisfiedBy
|
||||||
import net.corda.core.crypto.SecureHash
|
import net.corda.core.crypto.SecureHash
|
||||||
import net.corda.core.crypto.isFulfilledBy
|
import net.corda.core.crypto.isFulfilledBy
|
||||||
import net.corda.core.crypto.keys
|
|
||||||
import net.corda.core.internal.AttachmentWithContext
|
import net.corda.core.internal.AttachmentWithContext
|
||||||
import net.corda.core.internal.isUploaderTrusted
|
import net.corda.core.internal.isUploaderTrusted
|
||||||
import net.corda.core.serialization.internal.AttachmentsClassLoader
|
|
||||||
import net.corda.core.serialization.CordaSerializable
|
import net.corda.core.serialization.CordaSerializable
|
||||||
import net.corda.core.utilities.contextLogger
|
import net.corda.core.transactions.TransactionBuilder
|
||||||
import net.corda.core.utilities.warnOnce
|
|
||||||
import org.slf4j.LoggerFactory
|
|
||||||
import java.lang.annotation.Inherited
|
import java.lang.annotation.Inherited
|
||||||
import java.security.PublicKey
|
import java.security.PublicKey
|
||||||
|
|
||||||
@ -34,72 +29,6 @@ annotation class NoConstraintPropagation
|
|||||||
interface AttachmentConstraint {
|
interface AttachmentConstraint {
|
||||||
/** Returns whether the given contract attachment can be used with the [ContractState] associated with this constraint object. */
|
/** Returns whether the given contract attachment can be used with the [ContractState] associated with this constraint object. */
|
||||||
fun isSatisfiedBy(attachment: Attachment): Boolean
|
fun isSatisfiedBy(attachment: Attachment): Boolean
|
||||||
|
|
||||||
private companion object {
|
|
||||||
private val log = contextLogger()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This method will be used in conjunction with [NoConstraintPropagation]. It is run during transaction verification when the contract is not annotated with [NoConstraintPropagation].
|
|
||||||
* When constraints propagation is enabled, constraints set on output states need to follow certain rules with regards to constraints of input states.
|
|
||||||
*
|
|
||||||
* Rules:
|
|
||||||
* * It is allowed for output states to inherit the exact same constraint as the input states.
|
|
||||||
* * The [AlwaysAcceptAttachmentConstraint] is not allowed to transition to a different constraint, as that could be used to hide malicious behaviour.
|
|
||||||
* * Anything (except the [AlwaysAcceptAttachmentConstraint]) can be transitioned to a [HashAttachmentConstraint].
|
|
||||||
* * You can transition from the [WhitelistedByZoneAttachmentConstraint] to the [SignatureAttachmentConstraint] only if all signers of the JAR are required to sign in the future.
|
|
||||||
* * You can transition from a [HashAttachmentConstraint] to a [SignatureAttachmentConstraint] when the following conditions are met:
|
|
||||||
* * 1. Jar contents (per entry, by hashcode) of both original (unsigned) and signed contract jars are identical
|
|
||||||
* * Note: this step is enforced in the [AttachmentsClassLoader] no overlap rule checking.
|
|
||||||
* * 2. Java package namespace of signed contract jar is registered in the CZ network map with same public keys (as used to sign contract jar)
|
|
||||||
*
|
|
||||||
* TODO - SignatureConstraint third party signers.
|
|
||||||
*/
|
|
||||||
@CordaInternal
|
|
||||||
fun canBeTransitionedFrom(input: AttachmentConstraint, attachment: AttachmentWithContext): Boolean {
|
|
||||||
val output = this
|
|
||||||
return when {
|
|
||||||
// These branches should not happen, as this has been already checked.
|
|
||||||
input is AutomaticPlaceholderConstraint || output is AutomaticPlaceholderConstraint -> throw IllegalArgumentException("Illegal constraint: AutomaticPlaceholderConstraint.")
|
|
||||||
input is AutomaticHashConstraint || output is AutomaticHashConstraint -> throw IllegalArgumentException("Illegal constraint: AutomaticHashConstraint.")
|
|
||||||
|
|
||||||
// Transition to the same constraint.
|
|
||||||
input == output -> true
|
|
||||||
|
|
||||||
// You can't transition from the AlwaysAcceptAttachmentConstraint to anything else, as it could hide something illegal.
|
|
||||||
input is AlwaysAcceptAttachmentConstraint && output !is AlwaysAcceptAttachmentConstraint -> false
|
|
||||||
|
|
||||||
// Nothing can be migrated from the HashConstraint except a HashConstraint with the same Hash. (This check is redundant, but added for clarity)
|
|
||||||
input is HashAttachmentConstraint && output is HashAttachmentConstraint -> input == output
|
|
||||||
|
|
||||||
// Anything (except the AlwaysAcceptAttachmentConstraint) can be transformed to a HashAttachmentConstraint.
|
|
||||||
input !is HashAttachmentConstraint && output is HashAttachmentConstraint -> true
|
|
||||||
|
|
||||||
// The SignatureAttachmentConstraint allows migration from a Signature constraint with the same key.
|
|
||||||
// TODO - we don't support currently third party signers. When we do, the output key will have to be stronger then the input key.
|
|
||||||
input is SignatureAttachmentConstraint && output is SignatureAttachmentConstraint -> input.key == output.key
|
|
||||||
|
|
||||||
// You can transition from the WhitelistConstraint to the SignatureConstraint only if all signers of the JAR are required to sign in the future.
|
|
||||||
input is WhitelistedByZoneAttachmentConstraint && output is SignatureAttachmentConstraint ->
|
|
||||||
attachment.signerKeys.isNotEmpty() && output.key.keys.containsAll(attachment.signerKeys)
|
|
||||||
|
|
||||||
// Transition from Hash to Signature constraint requires
|
|
||||||
// signer(s) of signature-constrained output state is same as signer(s) of registered package namespace
|
|
||||||
input is HashAttachmentConstraint && output is SignatureAttachmentConstraint -> {
|
|
||||||
val packageOwnerPK = attachment.networkParameters.getPackageOwnerOf(attachment.contractAttachment.allContracts)
|
|
||||||
if (packageOwnerPK == null) {
|
|
||||||
log.warn("Missing registered java package owner for ${attachment.contractAttachment.contract} in network parameters: ${attachment.networkParameters} (input constraint = $input, output constraint = $output)")
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
else if (!packageOwnerPK.isFulfilledBy(output.key) ) {
|
|
||||||
log.warn("Java package owner keys do not match signature constrained output state keys")
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
else -> false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** An [AttachmentConstraint] where [isSatisfiedBy] always returns true. */
|
/** An [AttachmentConstraint] where [isSatisfiedBy] always returns true. */
|
||||||
@ -138,7 +67,11 @@ object WhitelistedByZoneAttachmentConstraint : AttachmentConstraint {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@KeepForDJVM
|
@KeepForDJVM
|
||||||
@Deprecated("The name is no longer valid as multiple constraints were added.", replaceWith = ReplaceWith("AutomaticPlaceholderConstraint"), level = DeprecationLevel.WARNING)
|
@Deprecated(
|
||||||
|
"The name is no longer valid as multiple constraints were added.",
|
||||||
|
replaceWith = ReplaceWith("AutomaticPlaceholderConstraint"),
|
||||||
|
level = DeprecationLevel.WARNING
|
||||||
|
)
|
||||||
object AutomaticHashConstraint : AttachmentConstraint {
|
object AutomaticHashConstraint : AttachmentConstraint {
|
||||||
override fun isSatisfiedBy(attachment: Attachment): Boolean {
|
override fun isSatisfiedBy(attachment: Attachment): Boolean {
|
||||||
throw UnsupportedOperationException("Contracts cannot be satisfied by an AutomaticHashConstraint placeholder.")
|
throw UnsupportedOperationException("Contracts cannot be satisfied by an AutomaticHashConstraint placeholder.")
|
||||||
@ -146,8 +79,8 @@ object AutomaticHashConstraint : AttachmentConstraint {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This [AttachmentConstraint] is a convenience class that acts as a placeholder and will be automatically resolved by the platform when set on an output state.
|
* This [AttachmentConstraint] is a convenience class that acts as a placeholder and will be automatically resolved by the platform when set
|
||||||
* It is the default constraint of all output states.
|
* on an output state. It is the default constraint of all output states.
|
||||||
*
|
*
|
||||||
* The resolution occurs in [TransactionBuilder.toWireTransaction] and is based on the input states and the attachments.
|
* The resolution occurs in [TransactionBuilder.toWireTransaction] and is based on the input states and the attachments.
|
||||||
* If the [Contract] was not annotated with [NoConstraintPropagation], then the platform will ensure the correct constraint propagation.
|
* If the [Contract] was not annotated with [NoConstraintPropagation], then the platform will ensure the correct constraint propagation.
|
||||||
@ -159,48 +92,13 @@ object AutomaticPlaceholderConstraint : AttachmentConstraint {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private val logger = LoggerFactory.getLogger(AttachmentConstraint::class.java)
|
|
||||||
private val validConstraints = setOf(
|
|
||||||
AlwaysAcceptAttachmentConstraint::class,
|
|
||||||
HashAttachmentConstraint::class,
|
|
||||||
WhitelistedByZoneAttachmentConstraint::class,
|
|
||||||
SignatureAttachmentConstraint::class)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Fails if the constraint is not of a known type.
|
|
||||||
* Only the Corda core is allowed to implement the [AttachmentConstraint] interface.
|
|
||||||
*/
|
|
||||||
internal fun checkConstraintValidity(state: TransactionState<*>) {
|
|
||||||
require(state.constraint::class in validConstraints) { "Found state ${state.contract} with an illegal constraint: ${state.constraint}" }
|
|
||||||
if (state.constraint is AlwaysAcceptAttachmentConstraint) {
|
|
||||||
logger.warnOnce("Found state ${state.contract} that is constrained by the insecure: AlwaysAcceptAttachmentConstraint.")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check for the [NoConstraintPropagation] annotation on the contractClassName.
|
|
||||||
* If it's present it means that the automatic secure core behaviour is not applied, and it's up to the contract developer to enforce a secure propagation logic.
|
|
||||||
*/
|
|
||||||
internal fun ContractClassName.contractHasAutomaticConstraintPropagation(classLoader: ClassLoader? = null) =
|
|
||||||
(classLoader ?: NoConstraintPropagation::class.java.classLoader)
|
|
||||||
.loadClass(this).getAnnotation(NoConstraintPropagation::class.java) == null
|
|
||||||
|
|
||||||
fun ContractClassName.warnContractWithoutConstraintPropagation(classLoader: ClassLoader? = null) {
|
|
||||||
if (!this.contractHasAutomaticConstraintPropagation(classLoader)) {
|
|
||||||
logger.warnOnce("Found contract $this with automatic constraint propagation disabled.")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An [AttachmentConstraint] that verifies that the attachment has signers that fulfil the provided [PublicKey].
|
* An [AttachmentConstraint] that verifies that the attachment has signers that fulfil the provided [PublicKey].
|
||||||
* See: [Signature Constraints](https://docs.corda.net/design/data-model-upgrades/signature-constraints.html)
|
* See: [Signature Constraints](https://docs.corda.net/design/data-model-upgrades/signature-constraints.html)
|
||||||
*
|
*
|
||||||
* @param key A [PublicKey] that must be fulfilled by the owning keys of the attachment's signing parties.
|
* @property key A [PublicKey] that must be fulfilled by the owning keys of the attachment's signing parties.
|
||||||
*/
|
*/
|
||||||
@KeepForDJVM
|
@KeepForDJVM
|
||||||
data class SignatureAttachmentConstraint(
|
data class SignatureAttachmentConstraint(val key: PublicKey) : AttachmentConstraint {
|
||||||
val key: PublicKey
|
override fun isSatisfiedBy(attachment: Attachment): Boolean = key.isFulfilledBy(attachment.signerKeys.map { it })
|
||||||
) : AttachmentConstraint {
|
}
|
||||||
override fun isSatisfiedBy(attachment: Attachment): Boolean =
|
|
||||||
key.isFulfilledBy(attachment.signerKeys.map { it })
|
|
||||||
}
|
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
package net.corda.core.contracts
|
package net.corda.core.contracts
|
||||||
|
|
||||||
import kotlin.reflect.KClass
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This annotation is required by any [ContractState] which needs to ensure that it is only ever processed as part of a
|
* This annotation is required by any [ContractState] which needs to ensure that it is only ever processed as part of a
|
||||||
* [TransactionState] referencing the specified [Contract]. It may be omitted in the case that the [ContractState] class
|
* [TransactionState] referencing the specified [Contract]. It may be omitted in the case that the [ContractState] class
|
||||||
@ -17,16 +19,3 @@ import kotlin.reflect.KClass
|
|||||||
@Retention(AnnotationRetention.RUNTIME)
|
@Retention(AnnotationRetention.RUNTIME)
|
||||||
@Target(AnnotationTarget.CLASS)
|
@Target(AnnotationTarget.CLASS)
|
||||||
annotation class BelongsToContract(val value: KClass<out Contract>)
|
annotation class BelongsToContract(val value: KClass<out Contract>)
|
||||||
/**
|
|
||||||
* Obtain the typename of the required [ContractClass] associated with the target [ContractState], using the
|
|
||||||
* [BelongsToContract] annotation by default, but falling through to checking the state's enclosing class if there is
|
|
||||||
* one and it inherits from [Contract].
|
|
||||||
*/
|
|
||||||
val ContractState.requiredContractClassName: String? get() {
|
|
||||||
val annotation = javaClass.getAnnotation(BelongsToContract::class.java)
|
|
||||||
if (annotation != null) {
|
|
||||||
return annotation.value.java.typeName
|
|
||||||
}
|
|
||||||
val enclosingClass = javaClass.enclosingClass ?: return null
|
|
||||||
return if (Contract::class.java.isAssignableFrom(enclosingClass)) enclosingClass.typeName else null
|
|
||||||
}
|
|
@ -29,13 +29,4 @@ class ContractAttachment @JvmOverloads constructor(
|
|||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
return "ContractAttachment(attachment=${attachment.id}, contracts='$allContracts', uploader='$uploader', signed='$isSigned', version='$version')"
|
return "ContractAttachment(attachment=${attachment.id}, contracts='$allContracts', uploader='$uploader', signed='$isSigned', version='$version')"
|
||||||
}
|
}
|
||||||
|
}
|
||||||
companion object {
|
|
||||||
fun getContractVersion(attachment: Attachment) : Version =
|
|
||||||
if (attachment is ContractAttachment) {
|
|
||||||
attachment.version
|
|
||||||
} else {
|
|
||||||
DEFAULT_CORDAPP_VERSION
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -3,6 +3,7 @@ package net.corda.core.contracts
|
|||||||
|
|
||||||
import net.corda.core.KeepForDJVM
|
import net.corda.core.KeepForDJVM
|
||||||
import net.corda.core.identity.Party
|
import net.corda.core.identity.Party
|
||||||
|
import net.corda.core.internal.requiredContractClassName
|
||||||
import net.corda.core.serialization.CordaSerializable
|
import net.corda.core.serialization.CordaSerializable
|
||||||
import net.corda.core.utilities.loggerFor
|
import net.corda.core.utilities.loggerFor
|
||||||
|
|
||||||
|
@ -1,6 +0,0 @@
|
|||||||
package net.corda.core.contracts
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Contract version and flow versions are integers.
|
|
||||||
*/
|
|
||||||
typealias Version = Int
|
|
@ -4,8 +4,3 @@ package net.corda.core.cordapp
|
|||||||
* Thrown if an exception occurs in accessing or parsing cordapp configuration
|
* Thrown if an exception occurs in accessing or parsing cordapp configuration
|
||||||
*/
|
*/
|
||||||
class CordappConfigException(msg: String, e: Throwable) : Exception(msg, e)
|
class CordappConfigException(msg: String, e: Throwable) : Exception(msg, e)
|
||||||
|
|
||||||
/**
|
|
||||||
* Thrown if an exception occurs whilst parsing version identifiers within cordapp configuration
|
|
||||||
*/
|
|
||||||
class CordappInvalidVersionException(msg: String) : Exception(msg)
|
|
@ -4,8 +4,8 @@ import co.paralleluniverse.fibers.Suspendable
|
|||||||
import net.corda.core.contracts.StateAndRef
|
import net.corda.core.contracts.StateAndRef
|
||||||
import net.corda.core.crypto.SecureHash
|
import net.corda.core.crypto.SecureHash
|
||||||
import net.corda.core.internal.FetchDataFlow
|
import net.corda.core.internal.FetchDataFlow
|
||||||
|
import net.corda.core.internal.RetrieveAnyTransactionPayload
|
||||||
import net.corda.core.internal.readFully
|
import net.corda.core.internal.readFully
|
||||||
import net.corda.core.serialization.CordaSerializable
|
|
||||||
import net.corda.core.transactions.SignedTransaction
|
import net.corda.core.transactions.SignedTransaction
|
||||||
import net.corda.core.utilities.unwrap
|
import net.corda.core.utilities.unwrap
|
||||||
|
|
||||||
@ -115,11 +115,3 @@ open class DataVendingFlow(val otherSideSession: FlowSession, val payload: Any)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* This is a wildcard payload to be used by the invoker of the [DataVendingFlow] to allow unlimited access to its vault.
|
|
||||||
*
|
|
||||||
* TODO Fails with a serialization exception if it is not a list. Why?
|
|
||||||
*/
|
|
||||||
@CordaSerializable
|
|
||||||
object RetrieveAnyTransactionPayload : ArrayList<Any>()
|
|
136
core/src/main/kotlin/net/corda/core/internal/ConstraintsUtils.kt
Normal file
136
core/src/main/kotlin/net/corda/core/internal/ConstraintsUtils.kt
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
package net.corda.core.internal
|
||||||
|
|
||||||
|
import net.corda.core.contracts.*
|
||||||
|
import net.corda.core.crypto.isFulfilledBy
|
||||||
|
import net.corda.core.crypto.keys
|
||||||
|
import net.corda.core.internal.cordapp.CordappImpl
|
||||||
|
import net.corda.core.utilities.loggerFor
|
||||||
|
import net.corda.core.utilities.warnOnce
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Contract version and flow versions are integers.
|
||||||
|
*/
|
||||||
|
typealias Version = Int
|
||||||
|
|
||||||
|
private val log = loggerFor<AttachmentConstraint>()
|
||||||
|
|
||||||
|
val Attachment.contractVersion: Version get() = if (this is ContractAttachment) version else CordappImpl.DEFAULT_CORDAPP_VERSION
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obtain the typename of the required [ContractClass] associated with the target [ContractState], using the
|
||||||
|
* [BelongsToContract] annotation by default, but falling through to checking the state's enclosing class if there is
|
||||||
|
* one and it inherits from [Contract].
|
||||||
|
*/
|
||||||
|
val ContractState.requiredContractClassName: String? get() {
|
||||||
|
val annotation = javaClass.getAnnotation(BelongsToContract::class.java)
|
||||||
|
if (annotation != null) {
|
||||||
|
return annotation.value.java.typeName
|
||||||
|
}
|
||||||
|
val enclosingClass = javaClass.enclosingClass ?: return null
|
||||||
|
return if (Contract::class.java.isAssignableFrom(enclosingClass)) enclosingClass.typeName else null
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method will be used in conjunction with [NoConstraintPropagation]. It is run during transaction verification when the contract is not
|
||||||
|
* annotated with [NoConstraintPropagation]. When constraints propagation is enabled, constraints set on output states need to follow certain
|
||||||
|
* rules with regards to constraints of input states.
|
||||||
|
*
|
||||||
|
* Rules:
|
||||||
|
*
|
||||||
|
* * It is allowed for output states to inherit the exact same constraint as the input states.
|
||||||
|
*
|
||||||
|
* * The [AlwaysAcceptAttachmentConstraint] is not allowed to transition to a different constraint, as that could be used to hide malicious
|
||||||
|
* behaviour.
|
||||||
|
*
|
||||||
|
* * Anything (except the [AlwaysAcceptAttachmentConstraint]) can be transitioned to a [HashAttachmentConstraint].
|
||||||
|
*
|
||||||
|
* * You can transition from the [WhitelistedByZoneAttachmentConstraint] to the [SignatureAttachmentConstraint] only if all signers of the
|
||||||
|
* JAR are required to sign in the future.
|
||||||
|
*
|
||||||
|
* * You can transition from a [HashAttachmentConstraint] to a [SignatureAttachmentConstraint] when the following conditions are met:
|
||||||
|
*
|
||||||
|
* 1. Jar contents (per entry, by hashcode) of both original (unsigned) and signed contract jars are identical
|
||||||
|
* Note: this step is enforced in the [AttachmentsClassLoader] no overlap rule checking.
|
||||||
|
*
|
||||||
|
* 2. Java package namespace of signed contract jar is registered in the CZ network map with same public keys (as used to sign contract jar)
|
||||||
|
*/
|
||||||
|
// TODO - SignatureConstraint third party signers.
|
||||||
|
fun AttachmentConstraint.canBeTransitionedFrom(input: AttachmentConstraint, attachment: AttachmentWithContext): Boolean {
|
||||||
|
val output = this
|
||||||
|
return when {
|
||||||
|
// These branches should not happen, as this has been already checked.
|
||||||
|
input is AutomaticPlaceholderConstraint || output is AutomaticPlaceholderConstraint -> throw IllegalArgumentException("Illegal constraint: AutomaticPlaceholderConstraint.")
|
||||||
|
input is AutomaticHashConstraint || output is AutomaticHashConstraint -> throw IllegalArgumentException("Illegal constraint: AutomaticHashConstraint.")
|
||||||
|
|
||||||
|
// Transition to the same constraint.
|
||||||
|
input == output -> true
|
||||||
|
|
||||||
|
// You can't transition from the AlwaysAcceptAttachmentConstraint to anything else, as it could hide something illegal.
|
||||||
|
input is AlwaysAcceptAttachmentConstraint && output !is AlwaysAcceptAttachmentConstraint -> false
|
||||||
|
|
||||||
|
// Nothing can be migrated from the HashConstraint except a HashConstraint with the same Hash. (This check is redundant, but added for clarity)
|
||||||
|
input is HashAttachmentConstraint && output is HashAttachmentConstraint -> input == output
|
||||||
|
|
||||||
|
// Anything (except the AlwaysAcceptAttachmentConstraint) can be transformed to a HashAttachmentConstraint.
|
||||||
|
input !is HashAttachmentConstraint && output is HashAttachmentConstraint -> true
|
||||||
|
|
||||||
|
// The SignatureAttachmentConstraint allows migration from a Signature constraint with the same key.
|
||||||
|
// TODO - we don't support currently third party signers. When we do, the output key will have to be stronger then the input key.
|
||||||
|
input is SignatureAttachmentConstraint && output is SignatureAttachmentConstraint -> input.key == output.key
|
||||||
|
|
||||||
|
// You can transition from the WhitelistConstraint to the SignatureConstraint only if all signers of the JAR are required to sign in the future.
|
||||||
|
input is WhitelistedByZoneAttachmentConstraint && output is SignatureAttachmentConstraint ->
|
||||||
|
attachment.signerKeys.isNotEmpty() && output.key.keys.containsAll(attachment.signerKeys)
|
||||||
|
|
||||||
|
// Transition from Hash to Signature constraint requires
|
||||||
|
// signer(s) of signature-constrained output state is same as signer(s) of registered package namespace
|
||||||
|
input is HashAttachmentConstraint && output is SignatureAttachmentConstraint -> {
|
||||||
|
val packageOwnerPK = attachment.networkParameters.getPackageOwnerOf(attachment.contractAttachment.allContracts)
|
||||||
|
if (packageOwnerPK == null) {
|
||||||
|
log.warn("Missing registered java package owner for ${attachment.contractAttachment.contract} in network parameters: " +
|
||||||
|
"${attachment.networkParameters} (input constraint = $input, output constraint = $output)")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
else if (!packageOwnerPK.isFulfilledBy(output.key) ) {
|
||||||
|
log.warn("Java package owner keys do not match signature constrained output state keys")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
else -> false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val validConstraints = setOf(
|
||||||
|
AlwaysAcceptAttachmentConstraint::class,
|
||||||
|
HashAttachmentConstraint::class,
|
||||||
|
WhitelistedByZoneAttachmentConstraint::class,
|
||||||
|
SignatureAttachmentConstraint::class
|
||||||
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fails if the constraint is not of a known type.
|
||||||
|
* Only the Corda core is allowed to implement the [AttachmentConstraint] interface.
|
||||||
|
*/
|
||||||
|
internal fun checkConstraintValidity(state: TransactionState<*>) {
|
||||||
|
require(state.constraint::class in validConstraints) { "Found state ${state.contract} with an illegal constraint: ${state.constraint}" }
|
||||||
|
if (state.constraint is AlwaysAcceptAttachmentConstraint) {
|
||||||
|
log.warnOnce("Found state ${state.contract} that is constrained by the insecure: AlwaysAcceptAttachmentConstraint.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check for the [NoConstraintPropagation] annotation on the contractClassName. If it's present it means that the automatic secure core behaviour
|
||||||
|
* is not applied, and it's up to the contract developer to enforce a secure propagation logic.
|
||||||
|
*/
|
||||||
|
internal fun ContractClassName.contractHasAutomaticConstraintPropagation(classLoader: ClassLoader? = null): Boolean {
|
||||||
|
return (classLoader ?: NoConstraintPropagation::class.java.classLoader)
|
||||||
|
.loadClass(this)
|
||||||
|
.getAnnotation(NoConstraintPropagation::class.java) == null
|
||||||
|
}
|
||||||
|
|
||||||
|
fun ContractClassName.warnContractWithoutConstraintPropagation(classLoader: ClassLoader? = null) {
|
||||||
|
if (!this.contractHasAutomaticConstraintPropagation(classLoader)) {
|
||||||
|
log.warnOnce("Found contract $this with automatic constraint propagation disabled.")
|
||||||
|
}
|
||||||
|
}
|
@ -5,9 +5,11 @@ import net.corda.core.cordapp.Cordapp
|
|||||||
import net.corda.core.cordapp.CordappConfig
|
import net.corda.core.cordapp.CordappConfig
|
||||||
import net.corda.core.cordapp.CordappContext
|
import net.corda.core.cordapp.CordappContext
|
||||||
import net.corda.core.crypto.SecureHash
|
import net.corda.core.crypto.SecureHash
|
||||||
|
import net.corda.core.flows.DataVendingFlow
|
||||||
import net.corda.core.flows.FlowLogic
|
import net.corda.core.flows.FlowLogic
|
||||||
import net.corda.core.node.ServicesForResolution
|
import net.corda.core.node.ServicesForResolution
|
||||||
import net.corda.core.node.ZoneVersionTooLowException
|
import net.corda.core.node.ZoneVersionTooLowException
|
||||||
|
import net.corda.core.serialization.CordaSerializable
|
||||||
import net.corda.core.serialization.SerializationContext
|
import net.corda.core.serialization.SerializationContext
|
||||||
import net.corda.core.transactions.LedgerTransaction
|
import net.corda.core.transactions.LedgerTransaction
|
||||||
import net.corda.core.transactions.SignedTransaction
|
import net.corda.core.transactions.SignedTransaction
|
||||||
@ -61,15 +63,25 @@ internal fun SignedTransaction.pushToLoggingContext() {
|
|||||||
MDC.put("tx_id", id.toString())
|
MDC.put("tx_id", id.toString())
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private fun isPackageValid(packageName: String): Boolean {
|
||||||
* List implementation that applies the expensive [transform] function only when the element is accessed and caches calculated values.
|
return packageName.isNotEmpty() &&
|
||||||
* Size is very cheap as it doesn't call [transform].
|
!packageName.endsWith(".") &&
|
||||||
*/
|
packageName.split(".").all { token ->
|
||||||
class LazyMappedList<T, U>(val originalList: List<T>, val transform: (T, Int) -> U) : AbstractList<U>() {
|
Character.isJavaIdentifierStart(token[0]) && token.toCharArray().drop(1).all { Character.isJavaIdentifierPart(it) }
|
||||||
private val partialResolvedList = MutableList<U?>(originalList.size) { null }
|
}
|
||||||
|
|
||||||
override val size = originalList.size
|
|
||||||
|
|
||||||
override fun get(index: Int) = partialResolvedList[index]
|
|
||||||
?: transform(originalList[index], index).also { computed -> partialResolvedList[index] = computed }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a string is a legal Java package name.
|
||||||
|
*/
|
||||||
|
fun requirePackageValid(name: String) {
|
||||||
|
require(isPackageValid(name)) { "Invalid Java package name: `$name`." }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is a wildcard payload to be used by the invoker of the [DataVendingFlow] to allow unlimited access to its vault.
|
||||||
|
*
|
||||||
|
* TODO Fails with a serialization exception if it is not a list. Why?
|
||||||
|
*/
|
||||||
|
@CordaSerializable
|
||||||
|
object RetrieveAnyTransactionPayload : ArrayList<Any>()
|
||||||
|
@ -50,6 +50,7 @@ import java.util.stream.StreamSupport
|
|||||||
import java.util.zip.Deflater
|
import java.util.zip.Deflater
|
||||||
import java.util.zip.ZipEntry
|
import java.util.zip.ZipEntry
|
||||||
import java.util.zip.ZipOutputStream
|
import java.util.zip.ZipOutputStream
|
||||||
|
import kotlin.collections.AbstractList
|
||||||
import kotlin.reflect.KClass
|
import kotlin.reflect.KClass
|
||||||
import kotlin.reflect.full.createInstance
|
import kotlin.reflect.full.createInstance
|
||||||
|
|
||||||
@ -518,13 +519,15 @@ fun <K, V> createSimpleCache(maxSize: Int, onEject: (MutableMap.MutableEntry<K,
|
|||||||
|
|
||||||
fun <K, V> MutableMap<K, V>.toSynchronised(): MutableMap<K, V> = Collections.synchronizedMap(this)
|
fun <K, V> MutableMap<K, V>.toSynchronised(): MutableMap<K, V> = Collections.synchronizedMap(this)
|
||||||
|
|
||||||
private fun isPackageValid(packageName: String): Boolean = packageName.isNotEmpty() && !packageName.endsWith(".") && packageName.split(".").all { token ->
|
|
||||||
Character.isJavaIdentifierStart(token[0]) && token.toCharArray().drop(1).all { Character.isJavaIdentifierPart(it) }
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if a string is a legal Java package name.
|
* List implementation that applies the expensive [transform] function only when the element is accessed and caches calculated values.
|
||||||
|
* Size is very cheap as it doesn't call [transform].
|
||||||
*/
|
*/
|
||||||
fun requirePackageValid(name: String) {
|
class LazyMappedList<T, U>(val originalList: List<T>, val transform: (T, Int) -> U) : AbstractList<U>() {
|
||||||
require(isPackageValid(name)) { "Invalid Java package name: `$name`." }
|
private val partialResolvedList = MutableList<U?>(originalList.size) { null }
|
||||||
|
override val size get() = originalList.size
|
||||||
|
override fun get(index: Int): U {
|
||||||
|
return partialResolvedList[index]
|
||||||
|
?: transform(originalList[index], index).also { computed -> partialResolvedList[index] = computed }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,14 +5,12 @@ import net.corda.core.KeepForDJVM
|
|||||||
import net.corda.core.contracts.*
|
import net.corda.core.contracts.*
|
||||||
import net.corda.core.contracts.TransactionVerificationException.TransactionContractConflictException
|
import net.corda.core.contracts.TransactionVerificationException.TransactionContractConflictException
|
||||||
import net.corda.core.contracts.TransactionVerificationException.TransactionRequiredContractUnspecifiedException
|
import net.corda.core.contracts.TransactionVerificationException.TransactionRequiredContractUnspecifiedException
|
||||||
import net.corda.core.contracts.Version
|
|
||||||
import net.corda.core.crypto.SecureHash
|
import net.corda.core.crypto.SecureHash
|
||||||
import net.corda.core.crypto.isFulfilledBy
|
import net.corda.core.crypto.isFulfilledBy
|
||||||
import net.corda.core.identity.Party
|
import net.corda.core.identity.Party
|
||||||
import net.corda.core.internal.*
|
import net.corda.core.internal.*
|
||||||
import net.corda.core.internal.cordapp.CordappImpl.Companion.DEFAULT_CORDAPP_VERSION
|
import net.corda.core.internal.cordapp.CordappImpl.Companion.DEFAULT_CORDAPP_VERSION
|
||||||
import net.corda.core.internal.rules.StateContractValidationEnforcementRule
|
import net.corda.core.internal.rules.StateContractValidationEnforcementRule
|
||||||
import net.corda.core.internal.uncheckedCast
|
|
||||||
import net.corda.core.node.NetworkParameters
|
import net.corda.core.node.NetworkParameters
|
||||||
import net.corda.core.serialization.ConstructorForDeserialization
|
import net.corda.core.serialization.ConstructorForDeserialization
|
||||||
import net.corda.core.serialization.CordaSerializable
|
import net.corda.core.serialization.CordaSerializable
|
||||||
@ -91,7 +89,7 @@ private constructor(
|
|||||||
componentGroups: List<ComponentGroup>? = null,
|
componentGroups: List<ComponentGroup>? = null,
|
||||||
serializedInputs: List<SerializedStateAndRef>? = null,
|
serializedInputs: List<SerializedStateAndRef>? = null,
|
||||||
serializedReferences: List<SerializedStateAndRef>? = null,
|
serializedReferences: List<SerializedStateAndRef>? = null,
|
||||||
inputStatesContractClassNameToMaxVersion: Map<ContractClassName,Version>
|
inputStatesContractClassNameToMaxVersion: Map<ContractClassName, Version>
|
||||||
): LedgerTransaction {
|
): LedgerTransaction {
|
||||||
return LedgerTransaction(inputs, outputs, commands, attachments, id, notary, timeWindow, privacySalt, networkParameters, references, inputStatesContractClassNameToMaxVersion).apply {
|
return LedgerTransaction(inputs, outputs, commands, attachments, id, notary, timeWindow, privacySalt, networkParameters, references, inputStatesContractClassNameToMaxVersion).apply {
|
||||||
this.componentGroups = componentGroups
|
this.componentGroups = componentGroups
|
||||||
|
@ -4,8 +4,9 @@ import net.corda.core.KeepForDJVM
|
|||||||
import net.corda.core.contracts.ContractState
|
import net.corda.core.contracts.ContractState
|
||||||
import net.corda.core.contracts.TransactionState
|
import net.corda.core.contracts.TransactionState
|
||||||
import net.corda.core.flows.FlowException
|
import net.corda.core.flows.FlowException
|
||||||
|
import net.corda.core.internal.Version
|
||||||
import net.corda.core.serialization.CordaSerializable
|
import net.corda.core.serialization.CordaSerializable
|
||||||
import net.corda.core.contracts.Version
|
|
||||||
/**
|
/**
|
||||||
* A contract attachment was missing when trying to automatically attach all known contract attachments
|
* A contract attachment was missing when trying to automatically attach all known contract attachments
|
||||||
*
|
*
|
||||||
@ -13,6 +14,9 @@ import net.corda.core.contracts.Version
|
|||||||
*/
|
*/
|
||||||
@CordaSerializable
|
@CordaSerializable
|
||||||
@KeepForDJVM
|
@KeepForDJVM
|
||||||
class MissingContractAttachments @JvmOverloads constructor (val states: List<TransactionState<ContractState>>, minimumRequiredContractClassVersion: Version? = null)
|
class MissingContractAttachments
|
||||||
: FlowException("Cannot find contract attachments for ${states.map { it.contract }.distinct()}${minimumRequiredContractClassVersion?.let { ", minimum required contract class version $minimumRequiredContractClassVersion"}}. " +
|
@JvmOverloads
|
||||||
"See https://docs.corda.net/api-contract-constraints.html#debugging")
|
constructor(val states: List<TransactionState<ContractState>>, minimumRequiredContractClassVersion: Version? = null) : FlowException(
|
||||||
|
"Cannot find contract attachments for " +
|
||||||
|
"${states.map { it.contract }.distinct()}${minimumRequiredContractClassVersion?.let { ", minimum required contract class version $minimumRequiredContractClassVersion"}}. " +
|
||||||
|
"See https://docs.corda.net/api-contract-constraints.html#debugging")
|
||||||
|
@ -4,7 +4,6 @@ import co.paralleluniverse.strands.Strand
|
|||||||
import net.corda.core.CordaInternal
|
import net.corda.core.CordaInternal
|
||||||
import net.corda.core.DeleteForDJVM
|
import net.corda.core.DeleteForDJVM
|
||||||
import net.corda.core.contracts.*
|
import net.corda.core.contracts.*
|
||||||
import net.corda.core.contracts.ContractAttachment.Companion.getContractVersion
|
|
||||||
import net.corda.core.crypto.*
|
import net.corda.core.crypto.*
|
||||||
import net.corda.core.identity.Party
|
import net.corda.core.identity.Party
|
||||||
import net.corda.core.internal.*
|
import net.corda.core.internal.*
|
||||||
@ -15,8 +14,6 @@ import net.corda.core.node.ServicesForResolution
|
|||||||
import net.corda.core.node.ZoneVersionTooLowException
|
import net.corda.core.node.ZoneVersionTooLowException
|
||||||
import net.corda.core.node.services.AttachmentId
|
import net.corda.core.node.services.AttachmentId
|
||||||
import net.corda.core.node.services.KeyManagementService
|
import net.corda.core.node.services.KeyManagementService
|
||||||
import net.corda.core.node.services.vault.AttachmentQueryCriteria
|
|
||||||
import net.corda.core.node.services.vault.Builder
|
|
||||||
import net.corda.core.serialization.SerializationContext
|
import net.corda.core.serialization.SerializationContext
|
||||||
import net.corda.core.serialization.SerializationFactory
|
import net.corda.core.serialization.SerializationFactory
|
||||||
import net.corda.core.utilities.contextLogger
|
import net.corda.core.utilities.contextLogger
|
||||||
@ -26,6 +23,7 @@ import java.time.Duration
|
|||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
import java.util.ArrayDeque
|
import java.util.ArrayDeque
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
|
import kotlin.collections.ArrayList
|
||||||
import kotlin.collections.component1
|
import kotlin.collections.component1
|
||||||
import kotlin.collections.component2
|
import kotlin.collections.component2
|
||||||
|
|
||||||
@ -431,7 +429,7 @@ open class TransactionBuilder @JvmOverloads constructor(
|
|||||||
require(constraints.none { it in automaticConstraints })
|
require(constraints.none { it in automaticConstraints })
|
||||||
require(isReference || constraints.none { it is HashAttachmentConstraint })
|
require(isReference || constraints.none { it is HashAttachmentConstraint })
|
||||||
|
|
||||||
val minimumRequiredContractClassVersion = stateRefs?.map { getContractVersion(services.loadContractAttachment(it)) }?.max() ?: DEFAULT_CORDAPP_VERSION
|
val minimumRequiredContractClassVersion = stateRefs?.map { services.loadContractAttachment(it).contractVersion }?.max() ?: DEFAULT_CORDAPP_VERSION
|
||||||
return services.attachments.getContractAttachmentWithHighestContractVersion(contractClassName, minimumRequiredContractClassVersion)
|
return services.attachments.getContractAttachmentWithHighestContractVersion(contractClassName, minimumRequiredContractClassVersion)
|
||||||
?: throw MissingContractAttachments(states, minimumRequiredContractClassVersion)
|
?: throw MissingContractAttachments(states, minimumRequiredContractClassVersion)
|
||||||
}
|
}
|
||||||
|
@ -6,14 +6,10 @@ import net.corda.core.KeepForDJVM
|
|||||||
import net.corda.core.contracts.*
|
import net.corda.core.contracts.*
|
||||||
import net.corda.core.contracts.ComponentGroupEnum.COMMANDS_GROUP
|
import net.corda.core.contracts.ComponentGroupEnum.COMMANDS_GROUP
|
||||||
import net.corda.core.contracts.ComponentGroupEnum.OUTPUTS_GROUP
|
import net.corda.core.contracts.ComponentGroupEnum.OUTPUTS_GROUP
|
||||||
import net.corda.core.contracts.ContractAttachment.Companion.getContractVersion
|
|
||||||
import net.corda.core.crypto.*
|
import net.corda.core.crypto.*
|
||||||
import net.corda.core.identity.Party
|
import net.corda.core.identity.Party
|
||||||
import net.corda.core.internal.AbstractAttachment
|
import net.corda.core.internal.*
|
||||||
import net.corda.core.internal.Emoji
|
|
||||||
import net.corda.core.internal.SerializedStateAndRef
|
|
||||||
import net.corda.core.internal.cordapp.CordappImpl.Companion.DEFAULT_CORDAPP_VERSION
|
import net.corda.core.internal.cordapp.CordappImpl.Companion.DEFAULT_CORDAPP_VERSION
|
||||||
import net.corda.core.internal.createComponentGroups
|
|
||||||
import net.corda.core.node.NetworkParameters
|
import net.corda.core.node.NetworkParameters
|
||||||
import net.corda.core.node.ServiceHub
|
import net.corda.core.node.ServiceHub
|
||||||
import net.corda.core.node.ServicesForResolution
|
import net.corda.core.node.ServicesForResolution
|
||||||
@ -181,7 +177,7 @@ class WireTransaction(componentGroups: List<ComponentGroup>, val privacySalt: Pr
|
|||||||
it.toStateAndRef()
|
it.toStateAndRef()
|
||||||
}.groupBy { it.state.contract }
|
}.groupBy { it.state.contract }
|
||||||
val inputStateContractClassToMaxVersion: Map<ContractClassName, Version> = inputStateContractClassToStateRefs.mapValues {
|
val inputStateContractClassToMaxVersion: Map<ContractClassName, Version> = inputStateContractClassToStateRefs.mapValues {
|
||||||
it.value.map { getContractVersion(resolveContractAttachment(it.ref)) }.max() ?: DEFAULT_CORDAPP_VERSION
|
it.value.map { resolveContractAttachment(it.ref).contractVersion }.max() ?: DEFAULT_CORDAPP_VERSION
|
||||||
}
|
}
|
||||||
|
|
||||||
val ltx = LedgerTransaction.create(
|
val ltx = LedgerTransaction.create(
|
||||||
|
@ -3,13 +3,16 @@ package net.corda.core.contracts
|
|||||||
import com.nhaarman.mockito_kotlin.doReturn
|
import com.nhaarman.mockito_kotlin.doReturn
|
||||||
import com.nhaarman.mockito_kotlin.mock
|
import com.nhaarman.mockito_kotlin.mock
|
||||||
import com.nhaarman.mockito_kotlin.whenever
|
import com.nhaarman.mockito_kotlin.whenever
|
||||||
|
import net.corda.core.crypto.Crypto
|
||||||
import net.corda.core.crypto.SecureHash
|
import net.corda.core.crypto.SecureHash
|
||||||
import net.corda.core.crypto.SecureHash.Companion.allOnesHash
|
import net.corda.core.crypto.SecureHash.Companion.allOnesHash
|
||||||
import net.corda.core.crypto.SecureHash.Companion.zeroHash
|
import net.corda.core.crypto.SecureHash.Companion.zeroHash
|
||||||
import net.corda.core.crypto.*
|
import net.corda.core.crypto.SignableData
|
||||||
|
import net.corda.core.crypto.SignatureMetadata
|
||||||
import net.corda.core.identity.AbstractParty
|
import net.corda.core.identity.AbstractParty
|
||||||
import net.corda.core.identity.CordaX500Name
|
import net.corda.core.identity.CordaX500Name
|
||||||
import net.corda.core.internal.AttachmentWithContext
|
import net.corda.core.internal.AttachmentWithContext
|
||||||
|
import net.corda.core.internal.canBeTransitionedFrom
|
||||||
import net.corda.core.internal.inputStream
|
import net.corda.core.internal.inputStream
|
||||||
import net.corda.core.internal.toPath
|
import net.corda.core.internal.toPath
|
||||||
import net.corda.core.node.NotaryInfo
|
import net.corda.core.node.NotaryInfo
|
||||||
@ -21,9 +24,12 @@ import net.corda.finance.`issued by`
|
|||||||
import net.corda.finance.contracts.asset.Cash
|
import net.corda.finance.contracts.asset.Cash
|
||||||
import net.corda.node.services.api.IdentityServiceInternal
|
import net.corda.node.services.api.IdentityServiceInternal
|
||||||
import net.corda.testing.common.internal.testNetworkParameters
|
import net.corda.testing.common.internal.testNetworkParameters
|
||||||
import net.corda.testing.core.*
|
import net.corda.testing.core.ALICE_NAME
|
||||||
import net.corda.testing.core.internal.JarSignatureTestUtils.generateKey
|
import net.corda.testing.core.DUMMY_NOTARY_NAME
|
||||||
|
import net.corda.testing.core.SerializationEnvironmentRule
|
||||||
|
import net.corda.testing.core.TestIdentity
|
||||||
import net.corda.testing.core.internal.ContractJarTestUtils
|
import net.corda.testing.core.internal.ContractJarTestUtils
|
||||||
|
import net.corda.testing.core.internal.JarSignatureTestUtils.generateKey
|
||||||
import net.corda.testing.core.internal.SelfCleaningDir
|
import net.corda.testing.core.internal.SelfCleaningDir
|
||||||
import net.corda.testing.internal.MockCordappProvider
|
import net.corda.testing.internal.MockCordappProvider
|
||||||
import net.corda.testing.internal.rigorousMock
|
import net.corda.testing.internal.rigorousMock
|
||||||
@ -31,8 +37,6 @@ import net.corda.testing.node.MockServices
|
|||||||
import net.corda.testing.node.ledger
|
import net.corda.testing.node.ledger
|
||||||
import org.junit.*
|
import org.junit.*
|
||||||
import java.security.PublicKey
|
import java.security.PublicKey
|
||||||
import org.junit.Rule
|
|
||||||
import org.junit.Test
|
|
||||||
import java.util.jar.Attributes
|
import java.util.jar.Attributes
|
||||||
import kotlin.test.assertFailsWith
|
import kotlin.test.assertFailsWith
|
||||||
import kotlin.test.assertFalse
|
import kotlin.test.assertFalse
|
||||||
@ -52,8 +56,8 @@ class ConstraintsPropagationTests {
|
|||||||
val BOB = TestIdentity(CordaX500Name("BOB", "London", "GB"))
|
val BOB = TestIdentity(CordaX500Name("BOB", "London", "GB"))
|
||||||
val BOB_PARTY get() = BOB.party
|
val BOB_PARTY get() = BOB.party
|
||||||
val BOB_PUBKEY get() = BOB.publicKey
|
val BOB_PUBKEY get() = BOB.publicKey
|
||||||
val noPropagationContractClassName = "net.corda.core.contracts.NoPropagationContract"
|
const val noPropagationContractClassName = "net.corda.core.contracts.NoPropagationContract"
|
||||||
val propagatingContractClassName = "net.corda.core.contracts.PropagationContract"
|
const val propagatingContractClassName = "net.corda.core.contracts.PropagationContract"
|
||||||
|
|
||||||
private lateinit var keyStoreDir: SelfCleaningDir
|
private lateinit var keyStoreDir: SelfCleaningDir
|
||||||
private lateinit var hashToSignatureConstraintsKey: PublicKey
|
private lateinit var hashToSignatureConstraintsKey: PublicKey
|
||||||
|
@ -2,6 +2,7 @@ package net.corda.core.flows
|
|||||||
|
|
||||||
import co.paralleluniverse.fibers.Suspendable
|
import co.paralleluniverse.fibers.Suspendable
|
||||||
import net.corda.core.internal.FetchDataFlow
|
import net.corda.core.internal.FetchDataFlow
|
||||||
|
import net.corda.core.internal.RetrieveAnyTransactionPayload
|
||||||
import net.corda.core.utilities.UntrustworthyData
|
import net.corda.core.utilities.UntrustworthyData
|
||||||
|
|
||||||
// Flow to start data vending without sending transaction. For testing only.
|
// Flow to start data vending without sending transaction. For testing only.
|
||||||
|
@ -2,9 +2,7 @@ package net.corda.node.internal.cordapp
|
|||||||
|
|
||||||
import io.github.classgraph.ClassGraph
|
import io.github.classgraph.ClassGraph
|
||||||
import io.github.classgraph.ScanResult
|
import io.github.classgraph.ScanResult
|
||||||
import net.corda.core.contracts.warnContractWithoutConstraintPropagation
|
|
||||||
import net.corda.core.cordapp.Cordapp
|
import net.corda.core.cordapp.Cordapp
|
||||||
import net.corda.core.cordapp.CordappInvalidVersionException
|
|
||||||
import net.corda.core.crypto.SecureHash
|
import net.corda.core.crypto.SecureHash
|
||||||
import net.corda.core.crypto.sha256
|
import net.corda.core.crypto.sha256
|
||||||
import net.corda.core.flows.*
|
import net.corda.core.flows.*
|
||||||
@ -359,6 +357,11 @@ class JarScanningCordappLoader private constructor(private val cordappJarPaths:
|
|||||||
*/
|
*/
|
||||||
class MultipleCordappsForFlowException(message: String) : Exception(message)
|
class MultipleCordappsForFlowException(message: String) : Exception(message)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Thrown if an exception occurs whilst parsing version identifiers within cordapp configuration
|
||||||
|
*/
|
||||||
|
class CordappInvalidVersionException(msg: String) : Exception(msg)
|
||||||
|
|
||||||
abstract class CordappLoaderTemplate : CordappLoader {
|
abstract class CordappLoaderTemplate : CordappLoader {
|
||||||
override val flowCordappMap: Map<Class<out FlowLogic<*>>, Cordapp> by lazy {
|
override val flowCordappMap: Map<Class<out FlowLogic<*>>, Cordapp> by lazy {
|
||||||
cordapps.flatMap { corDapp -> corDapp.allFlows.map { flow -> flow to corDapp } }
|
cordapps.flatMap { corDapp -> corDapp.allFlows.map { flow -> flow to corDapp } }
|
||||||
|
@ -10,10 +10,10 @@ import net.corda.core.CordaRuntimeException
|
|||||||
import net.corda.core.contracts.Attachment
|
import net.corda.core.contracts.Attachment
|
||||||
import net.corda.core.contracts.ContractAttachment
|
import net.corda.core.contracts.ContractAttachment
|
||||||
import net.corda.core.contracts.ContractClassName
|
import net.corda.core.contracts.ContractClassName
|
||||||
import net.corda.core.contracts.Version
|
|
||||||
import net.corda.core.crypto.SecureHash
|
import net.corda.core.crypto.SecureHash
|
||||||
import net.corda.core.crypto.sha256
|
import net.corda.core.crypto.sha256
|
||||||
import net.corda.core.internal.*
|
import net.corda.core.internal.*
|
||||||
|
import net.corda.core.internal.Version
|
||||||
import net.corda.core.internal.cordapp.CordappImpl.Companion.CORDAPP_CONTRACT_VERSION
|
import net.corda.core.internal.cordapp.CordappImpl.Companion.CORDAPP_CONTRACT_VERSION
|
||||||
import net.corda.core.internal.cordapp.CordappImpl.Companion.DEFAULT_CORDAPP_VERSION
|
import net.corda.core.internal.cordapp.CordappImpl.Companion.DEFAULT_CORDAPP_VERSION
|
||||||
import net.corda.core.node.ServicesForResolution
|
import net.corda.core.node.ServicesForResolution
|
||||||
@ -39,9 +39,7 @@ import java.io.InputStream
|
|||||||
import java.nio.file.Paths
|
import java.nio.file.Paths
|
||||||
import java.security.PublicKey
|
import java.security.PublicKey
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
import java.util.NavigableMap
|
import java.util.*
|
||||||
import java.util.Optional
|
|
||||||
import java.util.TreeMap
|
|
||||||
import java.util.jar.JarInputStream
|
import java.util.jar.JarInputStream
|
||||||
import javax.annotation.concurrent.ThreadSafe
|
import javax.annotation.concurrent.ThreadSafe
|
||||||
import javax.persistence.*
|
import javax.persistence.*
|
||||||
|
Loading…
x
Reference in New Issue
Block a user