mirror of
https://github.com/corda/corda.git
synced 2025-05-28 21:24:24 +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
|
||||
|
||||
import net.corda.core.CordaInternal
|
||||
import net.corda.core.DoNotImplement
|
||||
import net.corda.core.KeepForDJVM
|
||||
import net.corda.core.contracts.AlwaysAcceptAttachmentConstraint.isSatisfiedBy
|
||||
import net.corda.core.crypto.SecureHash
|
||||
import net.corda.core.crypto.isFulfilledBy
|
||||
import net.corda.core.crypto.keys
|
||||
import net.corda.core.internal.AttachmentWithContext
|
||||
import net.corda.core.internal.isUploaderTrusted
|
||||
import net.corda.core.serialization.internal.AttachmentsClassLoader
|
||||
import net.corda.core.serialization.CordaSerializable
|
||||
import net.corda.core.utilities.contextLogger
|
||||
import net.corda.core.utilities.warnOnce
|
||||
import org.slf4j.LoggerFactory
|
||||
import net.corda.core.transactions.TransactionBuilder
|
||||
import java.lang.annotation.Inherited
|
||||
import java.security.PublicKey
|
||||
|
||||
@ -34,72 +29,6 @@ annotation class NoConstraintPropagation
|
||||
interface AttachmentConstraint {
|
||||
/** Returns whether the given contract attachment can be used with the [ContractState] associated with this constraint object. */
|
||||
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. */
|
||||
@ -138,7 +67,11 @@ object WhitelistedByZoneAttachmentConstraint : AttachmentConstraint {
|
||||
}
|
||||
|
||||
@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 {
|
||||
override fun isSatisfiedBy(attachment: Attachment): Boolean {
|
||||
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.
|
||||
* It is the default constraint of all output states.
|
||||
* 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. 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.
|
||||
* 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].
|
||||
* 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
|
||||
data class SignatureAttachmentConstraint(
|
||||
val key: PublicKey
|
||||
) : AttachmentConstraint {
|
||||
override fun isSatisfiedBy(attachment: Attachment): Boolean =
|
||||
key.isFulfilledBy(attachment.signerKeys.map { it })
|
||||
}
|
||||
data class SignatureAttachmentConstraint(val key: PublicKey) : AttachmentConstraint {
|
||||
override fun isSatisfiedBy(attachment: Attachment): Boolean = key.isFulfilledBy(attachment.signerKeys.map { it })
|
||||
}
|
||||
|
@ -1,5 +1,7 @@
|
||||
package net.corda.core.contracts
|
||||
|
||||
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
|
||||
* [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)
|
||||
@Target(AnnotationTarget.CLASS)
|
||||
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 {
|
||||
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.identity.Party
|
||||
import net.corda.core.internal.requiredContractClassName
|
||||
import net.corda.core.serialization.CordaSerializable
|
||||
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
|
||||
*/
|
||||
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.crypto.SecureHash
|
||||
import net.corda.core.internal.FetchDataFlow
|
||||
import net.corda.core.internal.RetrieveAnyTransactionPayload
|
||||
import net.corda.core.internal.readFully
|
||||
import net.corda.core.serialization.CordaSerializable
|
||||
import net.corda.core.transactions.SignedTransaction
|
||||
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.CordappContext
|
||||
import net.corda.core.crypto.SecureHash
|
||||
import net.corda.core.flows.DataVendingFlow
|
||||
import net.corda.core.flows.FlowLogic
|
||||
import net.corda.core.node.ServicesForResolution
|
||||
import net.corda.core.node.ZoneVersionTooLowException
|
||||
import net.corda.core.serialization.CordaSerializable
|
||||
import net.corda.core.serialization.SerializationContext
|
||||
import net.corda.core.transactions.LedgerTransaction
|
||||
import net.corda.core.transactions.SignedTransaction
|
||||
@ -61,15 +63,25 @@ internal fun SignedTransaction.pushToLoggingContext() {
|
||||
MDC.put("tx_id", id.toString())
|
||||
}
|
||||
|
||||
/**
|
||||
* 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].
|
||||
*/
|
||||
class LazyMappedList<T, U>(val originalList: List<T>, val transform: (T, Int) -> U) : AbstractList<U>() {
|
||||
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 }
|
||||
private fun isPackageValid(packageName: String): Boolean {
|
||||
return 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.
|
||||
*/
|
||||
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.ZipEntry
|
||||
import java.util.zip.ZipOutputStream
|
||||
import kotlin.collections.AbstractList
|
||||
import kotlin.reflect.KClass
|
||||
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)
|
||||
|
||||
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) {
|
||||
require(isPackageValid(name)) { "Invalid Java package name: `$name`." }
|
||||
class LazyMappedList<T, U>(val originalList: List<T>, val transform: (T, Int) -> U) : AbstractList<U>() {
|
||||
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.TransactionVerificationException.TransactionContractConflictException
|
||||
import net.corda.core.contracts.TransactionVerificationException.TransactionRequiredContractUnspecifiedException
|
||||
import net.corda.core.contracts.Version
|
||||
import net.corda.core.crypto.SecureHash
|
||||
import net.corda.core.crypto.isFulfilledBy
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.core.internal.*
|
||||
import net.corda.core.internal.cordapp.CordappImpl.Companion.DEFAULT_CORDAPP_VERSION
|
||||
import net.corda.core.internal.rules.StateContractValidationEnforcementRule
|
||||
import net.corda.core.internal.uncheckedCast
|
||||
import net.corda.core.node.NetworkParameters
|
||||
import net.corda.core.serialization.ConstructorForDeserialization
|
||||
import net.corda.core.serialization.CordaSerializable
|
||||
@ -91,7 +89,7 @@ private constructor(
|
||||
componentGroups: List<ComponentGroup>? = null,
|
||||
serializedInputs: List<SerializedStateAndRef>? = null,
|
||||
serializedReferences: List<SerializedStateAndRef>? = null,
|
||||
inputStatesContractClassNameToMaxVersion: Map<ContractClassName,Version>
|
||||
inputStatesContractClassNameToMaxVersion: Map<ContractClassName, Version>
|
||||
): LedgerTransaction {
|
||||
return LedgerTransaction(inputs, outputs, commands, attachments, id, notary, timeWindow, privacySalt, networkParameters, references, inputStatesContractClassNameToMaxVersion).apply {
|
||||
this.componentGroups = componentGroups
|
||||
|
@ -4,8 +4,9 @@ import net.corda.core.KeepForDJVM
|
||||
import net.corda.core.contracts.ContractState
|
||||
import net.corda.core.contracts.TransactionState
|
||||
import net.corda.core.flows.FlowException
|
||||
import net.corda.core.internal.Version
|
||||
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
|
||||
*
|
||||
@ -13,6 +14,9 @@ import net.corda.core.contracts.Version
|
||||
*/
|
||||
@CordaSerializable
|
||||
@KeepForDJVM
|
||||
class MissingContractAttachments @JvmOverloads 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")
|
||||
class MissingContractAttachments
|
||||
@JvmOverloads
|
||||
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.DeleteForDJVM
|
||||
import net.corda.core.contracts.*
|
||||
import net.corda.core.contracts.ContractAttachment.Companion.getContractVersion
|
||||
import net.corda.core.crypto.*
|
||||
import net.corda.core.identity.Party
|
||||
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.services.AttachmentId
|
||||
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.SerializationFactory
|
||||
import net.corda.core.utilities.contextLogger
|
||||
@ -26,6 +23,7 @@ import java.time.Duration
|
||||
import java.time.Instant
|
||||
import java.util.ArrayDeque
|
||||
import java.util.UUID
|
||||
import kotlin.collections.ArrayList
|
||||
import kotlin.collections.component1
|
||||
import kotlin.collections.component2
|
||||
|
||||
@ -431,7 +429,7 @@ open class TransactionBuilder @JvmOverloads constructor(
|
||||
require(constraints.none { it in automaticConstraints })
|
||||
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)
|
||||
?: throw MissingContractAttachments(states, minimumRequiredContractClassVersion)
|
||||
}
|
||||
|
@ -6,14 +6,10 @@ import net.corda.core.KeepForDJVM
|
||||
import net.corda.core.contracts.*
|
||||
import net.corda.core.contracts.ComponentGroupEnum.COMMANDS_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.identity.Party
|
||||
import net.corda.core.internal.AbstractAttachment
|
||||
import net.corda.core.internal.Emoji
|
||||
import net.corda.core.internal.SerializedStateAndRef
|
||||
import net.corda.core.internal.*
|
||||
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.ServiceHub
|
||||
import net.corda.core.node.ServicesForResolution
|
||||
@ -181,7 +177,7 @@ class WireTransaction(componentGroups: List<ComponentGroup>, val privacySalt: Pr
|
||||
it.toStateAndRef()
|
||||
}.groupBy { it.state.contract }
|
||||
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(
|
||||
|
@ -3,13 +3,16 @@ package net.corda.core.contracts
|
||||
import com.nhaarman.mockito_kotlin.doReturn
|
||||
import com.nhaarman.mockito_kotlin.mock
|
||||
import com.nhaarman.mockito_kotlin.whenever
|
||||
import net.corda.core.crypto.Crypto
|
||||
import net.corda.core.crypto.SecureHash
|
||||
import net.corda.core.crypto.SecureHash.Companion.allOnesHash
|
||||
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.CordaX500Name
|
||||
import net.corda.core.internal.AttachmentWithContext
|
||||
import net.corda.core.internal.canBeTransitionedFrom
|
||||
import net.corda.core.internal.inputStream
|
||||
import net.corda.core.internal.toPath
|
||||
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.node.services.api.IdentityServiceInternal
|
||||
import net.corda.testing.common.internal.testNetworkParameters
|
||||
import net.corda.testing.core.*
|
||||
import net.corda.testing.core.internal.JarSignatureTestUtils.generateKey
|
||||
import net.corda.testing.core.ALICE_NAME
|
||||
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.JarSignatureTestUtils.generateKey
|
||||
import net.corda.testing.core.internal.SelfCleaningDir
|
||||
import net.corda.testing.internal.MockCordappProvider
|
||||
import net.corda.testing.internal.rigorousMock
|
||||
@ -31,8 +37,6 @@ import net.corda.testing.node.MockServices
|
||||
import net.corda.testing.node.ledger
|
||||
import org.junit.*
|
||||
import java.security.PublicKey
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import java.util.jar.Attributes
|
||||
import kotlin.test.assertFailsWith
|
||||
import kotlin.test.assertFalse
|
||||
@ -52,8 +56,8 @@ class ConstraintsPropagationTests {
|
||||
val BOB = TestIdentity(CordaX500Name("BOB", "London", "GB"))
|
||||
val BOB_PARTY get() = BOB.party
|
||||
val BOB_PUBKEY get() = BOB.publicKey
|
||||
val noPropagationContractClassName = "net.corda.core.contracts.NoPropagationContract"
|
||||
val propagatingContractClassName = "net.corda.core.contracts.PropagationContract"
|
||||
const val noPropagationContractClassName = "net.corda.core.contracts.NoPropagationContract"
|
||||
const val propagatingContractClassName = "net.corda.core.contracts.PropagationContract"
|
||||
|
||||
private lateinit var keyStoreDir: SelfCleaningDir
|
||||
private lateinit var hashToSignatureConstraintsKey: PublicKey
|
||||
|
@ -2,6 +2,7 @@ package net.corda.core.flows
|
||||
|
||||
import co.paralleluniverse.fibers.Suspendable
|
||||
import net.corda.core.internal.FetchDataFlow
|
||||
import net.corda.core.internal.RetrieveAnyTransactionPayload
|
||||
import net.corda.core.utilities.UntrustworthyData
|
||||
|
||||
// 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.ScanResult
|
||||
import net.corda.core.contracts.warnContractWithoutConstraintPropagation
|
||||
import net.corda.core.cordapp.Cordapp
|
||||
import net.corda.core.cordapp.CordappInvalidVersionException
|
||||
import net.corda.core.crypto.SecureHash
|
||||
import net.corda.core.crypto.sha256
|
||||
import net.corda.core.flows.*
|
||||
@ -359,6 +357,11 @@ class JarScanningCordappLoader private constructor(private val cordappJarPaths:
|
||||
*/
|
||||
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 {
|
||||
override val flowCordappMap: Map<Class<out FlowLogic<*>>, Cordapp> by lazy {
|
||||
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.ContractAttachment
|
||||
import net.corda.core.contracts.ContractClassName
|
||||
import net.corda.core.contracts.Version
|
||||
import net.corda.core.crypto.SecureHash
|
||||
import net.corda.core.crypto.sha256
|
||||
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.DEFAULT_CORDAPP_VERSION
|
||||
import net.corda.core.node.ServicesForResolution
|
||||
@ -39,9 +39,7 @@ import java.io.InputStream
|
||||
import java.nio.file.Paths
|
||||
import java.security.PublicKey
|
||||
import java.time.Instant
|
||||
import java.util.NavigableMap
|
||||
import java.util.Optional
|
||||
import java.util.TreeMap
|
||||
import java.util.*
|
||||
import java.util.jar.JarInputStream
|
||||
import javax.annotation.concurrent.ThreadSafe
|
||||
import javax.persistence.*
|
||||
|
Loading…
x
Reference in New Issue
Block a user