CORDA-2149 CorDapp Contract and Workflow version identifiers (#4363)

* Implementation of Contract and Workflow attribute identifiers.

* Fixes following rebase from master.

* Fix broken JUnit test.

* Fix broken JUnit test.

* Fix broken JUnit test.

* Added missing constants.

* Further clean-up.

* Updated documentation.

* Added changelog entry.

* Updated all samples (using new Gradle Plugin 4.0.37 functionality)

* Temporarily resolve gradle plugins from latest published snapshot.

* Temporarily resolve gradle plugins from latest published snapshot.

* Updates following feedback from PR review.

* Move constants into CordappInfo companion object.

* Contract and Workflow attribute `version` to `versionId` (as version is a reserved gradle variable)

* Clarified warning message on incorrect version identifier.

* Align version identifier processing logic with gradle cordapp plugin.

* Updated comment.

* Minor fixes following rebase from master.

* Fixed broken unit test.

* Improved exception reporting.

* Update to use 4.0.37 of Gradle Plugins.

* Added support for combined Contract and Workflow CorDapp info.

* Updated following discussions with Shams + cleanup.

* Updated following Shams PR review.

* Minor API improvements.

* Added missing cordapp info causing deployNodes to fail.
This commit is contained in:
josecoll 2018-12-14 09:39:23 +00:00 committed by GitHub
parent 767e37a34e
commit 9cdda3bd77
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
47 changed files with 475 additions and 231 deletions

View File

@ -1,4 +1,4 @@
gradlePluginsVersion=4.0.36 gradlePluginsVersion=4.0.37
kotlinVersion=1.2.71 kotlinVersion=1.2.71
# ***************************************************************# # ***************************************************************#
# When incrementing platformVersion make sure to update # # When incrementing platformVersion make sure to update #

View File

@ -1,8 +1,8 @@
package net.corda.core.contracts package net.corda.core.contracts
import net.corda.core.KeepForDJVM import net.corda.core.KeepForDJVM
import net.corda.core.internal.cordapp.CordappImpl.Companion.DEFAULT_CORDAPP_VERSION
import net.corda.core.serialization.CordaSerializable import net.corda.core.serialization.CordaSerializable
import net.corda.core.cordapp.DEFAULT_CORDAPP_VERSION
import java.security.PublicKey import java.security.PublicKey
/** /**

View File

@ -4,3 +4,8 @@ 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)

View File

@ -4,6 +4,10 @@ import net.corda.core.DeleteForDJVM
import net.corda.core.DoNotImplement import net.corda.core.DoNotImplement
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.internal.cordapp.CordappImpl.Companion.CORDAPP_CONTRACT_VERSION
import net.corda.core.internal.cordapp.CordappImpl.Companion.CORDAPP_WORKFLOW_VERSION
import net.corda.core.internal.cordapp.CordappImpl.Companion.UNKNOWN_VALUE
import net.corda.core.internal.cordapp.CordappImpl.Companion.parseVersion
import net.corda.core.schemas.MappedSchema import net.corda.core.schemas.MappedSchema
import net.corda.core.serialization.SerializationCustomSerializer import net.corda.core.serialization.SerializationCustomSerializer
import net.corda.core.serialization.SerializationWhitelist import net.corda.core.serialization.SerializationWhitelist
@ -47,5 +51,66 @@ interface Cordapp {
val allFlows: List<Class<out FlowLogic<*>>> val allFlows: List<Class<out FlowLogic<*>>>
val jarPath: URL val jarPath: URL
val cordappClasses: List<String> val cordappClasses: List<String>
val info: Info
val jarHash: SecureHash.SHA256 val jarHash: SecureHash.SHA256
/**
* CorDapp's information, including vendor and version.
*
* @property shortName Cordapp's shortName
* @property vendor Cordapp's vendor
* @property version Cordapp's version
*/
@DoNotImplement
interface Info {
val shortName: String
val vendor: String
val version: String
val licence: String
val minimumPlatformVersion: Int
val targetPlatformVersion: Int
fun hasUnknownFields(): Boolean
/** CorDapps that do not separate Contracts and Flows into separate jars (pre Corda 4) */
data class Default(override val shortName: String, override val vendor: String, override val version: String, override val minimumPlatformVersion: Int, override val targetPlatformVersion: Int, override val licence: String = UNKNOWN_VALUE)
: Info {
override fun hasUnknownFields(): Boolean = arrayOf(shortName, vendor, version).any { it == UNKNOWN_VALUE }
override fun toString() = "CorDapp $shortName version $version by $vendor with licence $licence"
}
/** A Contract CorDapp contains contract definitions (state, commands) and verification logic */
data class Contract(override val shortName: String, override val vendor: String, val versionId: Int, override val licence: String, override val minimumPlatformVersion: Int, override val targetPlatformVersion: Int)
: Info {
override val version: String
get() = versionId.toString()
override fun toString() = "Contract CorDapp: $shortName version $version by vendor $vendor with licence $licence"
override fun hasUnknownFields(): Boolean = arrayOf(shortName, vendor, licence).any { it == UNKNOWN_VALUE }
}
/** A Workflow CorDapp contains flows and services used to implement business transactions using contracts and states persisted to the immutable ledger */
data class Workflow(override val shortName: String, override val vendor: String, val versionId: Int, override val licence: String, override val minimumPlatformVersion: Int, override val targetPlatformVersion: Int)
: Info {
override val version: String
get() = versionId.toString()
override fun toString() = "Workflow CorDapp: $shortName version $version by vendor $vendor with licence $licence"
override fun hasUnknownFields(): Boolean = arrayOf(shortName, vendor, licence).any { it == UNKNOWN_VALUE }
}
/** A CorDapp that includes both Contract and Workflow classes (not recommended) */
// TODO: future work in Gradle cordapp plugins to enforce separation of Contract and Workflow classes into separate jars
data class ContractAndWorkflow(val contract: Contract, val workflow: Workflow, override val minimumPlatformVersion: Int, override val targetPlatformVersion: Int)
: Info {
override val shortName: String
get() = "Contract: ${contract.shortName}, Workflow: ${workflow.shortName}"
override val vendor: String
get() = "Contract: ${contract.vendor}, Workflow: ${workflow.vendor}"
override val licence: String
get() = "Contract: ${contract.licence}, Workflow: ${workflow.licence}"
override val version: String
get() = "Contract: ${contract.versionId}, Workflow: ${workflow.versionId}"
override fun toString() = "Combined CorDapp: $contract, $workflow"
override fun hasUnknownFields(): Boolean = contract.hasUnknownFields() || workflow.hasUnknownFields()
}
}
} }

View File

@ -1,5 +0,0 @@
package net.corda.core.cordapp
const val CORDAPP_CONTRACT_VERSION = "Implementation-Version" //TODO will be changed to "Corda-Contract-Version"
const val DEFAULT_CORDAPP_VERSION: Int = 1

View File

@ -2,6 +2,7 @@ package net.corda.core.internal.cordapp
import net.corda.core.DeleteForDJVM import net.corda.core.DeleteForDJVM
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.flows.FlowLogic import net.corda.core.flows.FlowLogic
import net.corda.core.internal.notary.NotaryService import net.corda.core.internal.notary.NotaryService
@ -25,7 +26,7 @@ data class CordappImpl(
override val customSchemas: Set<MappedSchema>, override val customSchemas: Set<MappedSchema>,
override val allFlows: List<Class<out FlowLogic<*>>>, override val allFlows: List<Class<out FlowLogic<*>>>,
override val jarPath: URL, override val jarPath: URL,
val info: Info, override val info: Cordapp.Info,
override val jarHash: SecureHash.SHA256, override val jarHash: SecureHash.SHA256,
val notaryService: Class<out NotaryService>?, val notaryService: Class<out NotaryService>?,
/** Indicates whether the CorDapp is loaded from external sources, or generated on node startup (virtual). */ /** Indicates whether the CorDapp is loaded from external sources, or generated on node startup (virtual). */
@ -34,8 +35,42 @@ data class CordappImpl(
companion object { companion object {
fun jarName(url: URL): String = url.toPath().fileName.toString().removeSuffix(".jar") fun jarName(url: URL): String = url.toPath().fileName.toString().removeSuffix(".jar")
}
/** CorDapp manifest entries */
const val CORDAPP_CONTRACT_NAME = "Cordapp-Contract-Name"
const val CORDAPP_CONTRACT_VERSION = "Cordapp-Contract-Version"
const val CORDAPP_CONTRACT_VENDOR = "Cordapp-Contract-Vendor"
const val CORDAPP_CONTRACT_LICENCE = "Cordapp-Contract-Licence"
const val CORDAPP_WORKFLOW_NAME = "Cordapp-Workflow-Name"
const val CORDAPP_WORKFLOW_VERSION = "Cordapp-Workflow-Version"
const val CORDAPP_WORKFLOW_VENDOR = "Cordapp-Workflow-Vendor"
const val CORDAPP_WORKFLOW_LICENCE = "Cordapp-Workflow-Licence"
const val TARGET_PLATFORM_VERSION = "Target-Platform-Version"
const val MIN_PLATFORM_VERSION = "Min-Platform-Version"
const val UNKNOWN_VALUE = "Unknown"
const val DEFAULT_CORDAPP_VERSION = 1
/** used for CorDapps that do not explicitly define attributes */
val UNKNOWN = Cordapp.Info.Default(UNKNOWN_VALUE, UNKNOWN_VALUE, UNKNOWN_VALUE,1, 1)
/** Helper method for version identifier parsing */
fun parseVersion(versionStr: String?, attributeName: String): Int {
if (versionStr == null)
throw CordappInvalidVersionException("Target versionId attribute $attributeName not specified. Please specify a whole number starting from 1.")
return try {
val version = versionStr.toInt()
if (version < 1) {
throw CordappInvalidVersionException("Target versionId ($versionStr) for attribute $attributeName must not be smaller than 1.")
}
return version
} catch (e: NumberFormatException) {
throw CordappInvalidVersionException("Version identifier ($versionStr) for attribute $attributeName must be a whole number starting from 1.")
}
}
}
/** /**
* An exhaustive list of all classes relevant to the node within this CorDapp * An exhaustive list of all classes relevant to the node within this CorDapp
* *
@ -45,14 +80,4 @@ data class CordappImpl(
val classList = rpcFlows + initiatedFlows + services + serializationWhitelists.map { javaClass } + notaryService val classList = rpcFlows + initiatedFlows + services + serializationWhitelists.map { javaClass } + notaryService
classList.mapNotNull { it?.name } + contractClassNames classList.mapNotNull { it?.name } + contractClassNames
} }
// TODO Why a seperate Info class and not just have the fields directly in CordappImpl?
data class Info(val shortName: String, val vendor: String, val version: String, val minimumPlatformVersion: Int, val targetPlatformVersion: Int) {
companion object {
const val UNKNOWN_VALUE = "Unknown"
val UNKNOWN = Info(UNKNOWN_VALUE, UNKNOWN_VALUE, UNKNOWN_VALUE, 1, 1)
}
fun hasUnknownFields(): Boolean = arrayOf(shortName, vendor, version).any { it == UNKNOWN_VALUE }
}
} }

View File

@ -1,5 +1,6 @@
package net.corda.core.internal.cordapp package net.corda.core.internal.cordapp
import net.corda.core.cordapp.Cordapp
import net.corda.core.internal.PLATFORM_VERSION import net.corda.core.internal.PLATFORM_VERSION
import net.corda.core.internal.VisibleForTesting import net.corda.core.internal.VisibleForTesting
import net.corda.core.utilities.loggerFor import net.corda.core.utilities.loggerFor
@ -10,10 +11,10 @@ import java.util.concurrent.ConcurrentHashMap
*/ */
object CordappInfoResolver { object CordappInfoResolver {
private val logger = loggerFor<CordappInfoResolver>() private val logger = loggerFor<CordappInfoResolver>()
private val cordappClasses: ConcurrentHashMap<String, Set<CordappImpl.Info>> = ConcurrentHashMap() private val cordappClasses: ConcurrentHashMap<String, Set<Cordapp.Info>> = ConcurrentHashMap()
// TODO Use the StackWalker API once we migrate to Java 9+ // TODO Use the StackWalker API once we migrate to Java 9+
private var cordappInfoResolver: () -> CordappImpl.Info? = { private var cordappInfoResolver: () -> Cordapp.Info? = {
Exception().stackTrace Exception().stackTrace
.mapNotNull { cordappClasses[it.className] } .mapNotNull { cordappClasses[it.className] }
// If there is more than one cordapp registered for a class name we can't determine the "correct" one and return null. // If there is more than one cordapp registered for a class name we can't determine the "correct" one and return null.
@ -25,7 +26,7 @@ object CordappInfoResolver {
* This could happen when trying to run different versions of the same CorDapp on the same node. * This could happen when trying to run different versions of the same CorDapp on the same node.
*/ */
@Synchronized @Synchronized
fun register(classes: List<String>, cordapp: CordappImpl.Info) { fun register(classes: List<String>, cordapp: Cordapp.Info) {
classes.forEach { classes.forEach {
if (cordappClasses.containsKey(it)) { if (cordappClasses.containsKey(it)) {
logger.warn("More than one CorDapp registered for $it.") logger.warn("More than one CorDapp registered for $it.")
@ -45,7 +46,7 @@ object CordappInfoResolver {
* @return Information about the CorDapp from which the invoker is called, null if called outside a CorDapp or the * @return Information about the CorDapp from which the invoker is called, null if called outside a CorDapp or the
* calling CorDapp cannot be reliably determined. * calling CorDapp cannot be reliably determined.
*/ */
val currentCordappInfo: CordappImpl.Info? get() = cordappInfoResolver() val currentCordappInfo: Cordapp.Info? get() = cordappInfoResolver()
/** /**
* Returns the target version of the current calling CorDapp. Defaults to the current platform version if there isn't one. * Returns the target version of the current calling CorDapp. Defaults to the current platform version if there isn't one.
@ -60,12 +61,13 @@ object CordappInfoResolver {
@VisibleForTesting @VisibleForTesting
fun <T> withCordappInfo(shortName: String = "CordappInfoResolver.withCordappInfo", fun <T> withCordappInfo(shortName: String = "CordappInfoResolver.withCordappInfo",
vendor: String = "Corda", vendor: String = "Corda",
version: String = "1.0", version: String = "1",
licence: String = "Apache",
minimumPlatformVersion: Int = 1, minimumPlatformVersion: Int = 1,
targetPlatformVersion: Int = PLATFORM_VERSION, targetPlatformVersion: Int = PLATFORM_VERSION,
block: () -> T): T { block: () -> T): T {
val currentResolver = cordappInfoResolver val currentResolver = cordappInfoResolver
cordappInfoResolver = { CordappImpl.Info(shortName, vendor, version, minimumPlatformVersion, targetPlatformVersion) } cordappInfoResolver = { Cordapp.Info.Default(shortName, vendor, version, minimumPlatformVersion, targetPlatformVersion, licence) }
try { try {
return block() return block()
} finally { } finally {

View File

@ -0,0 +1,86 @@
package net.corda.core.internal.cordapp
import net.corda.core.cordapp.Cordapp
import net.corda.core.internal.cordapp.CordappImpl.Companion.CORDAPP_CONTRACT_LICENCE
import net.corda.core.internal.cordapp.CordappImpl.Companion.CORDAPP_CONTRACT_NAME
import net.corda.core.internal.cordapp.CordappImpl.Companion.CORDAPP_CONTRACT_VENDOR
import net.corda.core.internal.cordapp.CordappImpl.Companion.CORDAPP_CONTRACT_VERSION
import net.corda.core.internal.cordapp.CordappImpl.Companion.CORDAPP_WORKFLOW_LICENCE
import net.corda.core.internal.cordapp.CordappImpl.Companion.CORDAPP_WORKFLOW_NAME
import net.corda.core.internal.cordapp.CordappImpl.Companion.CORDAPP_WORKFLOW_VENDOR
import net.corda.core.internal.cordapp.CordappImpl.Companion.CORDAPP_WORKFLOW_VERSION
import net.corda.core.internal.cordapp.CordappImpl.Companion.MIN_PLATFORM_VERSION
import net.corda.core.internal.cordapp.CordappImpl.Companion.TARGET_PLATFORM_VERSION
import net.corda.core.internal.cordapp.CordappImpl.Companion.UNKNOWN_VALUE
import net.corda.core.internal.cordapp.CordappImpl.Companion.parseVersion
import java.util.jar.Attributes
import java.util.jar.Manifest
operator fun Manifest.set(key: String, value: String): String? {
return mainAttributes.putValue(key, value)
}
operator fun Manifest.set(key: Attributes.Name, value: String): Any? {
return mainAttributes.put(key, value)
}
operator fun Manifest.get(key: String): String? = mainAttributes.getValue(key)
val Manifest.targetPlatformVersion: Int
get() {
val minPlatformVersion = this[MIN_PLATFORM_VERSION]?.toIntOrNull() ?: 1
return this[TARGET_PLATFORM_VERSION]?.toIntOrNull() ?: minPlatformVersion
}
fun Manifest.toCordappInfo(defaultName: String): Cordapp.Info {
/** Common attributes */
val minPlatformVersion = this[MIN_PLATFORM_VERSION]?.toIntOrNull() ?: 1
val targetPlatformVersion = this[TARGET_PLATFORM_VERSION]?.toIntOrNull() ?: minPlatformVersion
/** new identifiers (Corda 4) */
// is it a Contract Jar?
val contractInfo =
if (this[CORDAPP_CONTRACT_NAME] != null) {
Cordapp.Info.Contract(shortName = this[CORDAPP_CONTRACT_NAME] ?: defaultName,
vendor = this[CORDAPP_CONTRACT_VENDOR] ?: UNKNOWN_VALUE,
versionId = parseVersion(this[CORDAPP_CONTRACT_VERSION], CORDAPP_CONTRACT_VERSION),
licence = this[CORDAPP_CONTRACT_LICENCE] ?: UNKNOWN_VALUE,
minimumPlatformVersion = minPlatformVersion,
targetPlatformVersion = targetPlatformVersion
)
} else null
// is it a Workflow (flows and services) Jar?
val workflowInfo =
if (this[CORDAPP_WORKFLOW_NAME] != null) {
Cordapp.Info.Workflow(shortName = this[CORDAPP_WORKFLOW_NAME] ?: defaultName,
vendor = this[CORDAPP_WORKFLOW_VENDOR] ?: UNKNOWN_VALUE,
versionId = parseVersion(this[CORDAPP_WORKFLOW_VERSION],CORDAPP_WORKFLOW_VERSION),
licence = this[CORDAPP_WORKFLOW_LICENCE] ?: UNKNOWN_VALUE,
minimumPlatformVersion = minPlatformVersion,
targetPlatformVersion = targetPlatformVersion
)
} else null
// combined Contract and Workflow Jar ?
if (contractInfo != null && workflowInfo != null) {
return Cordapp.Info.ContractAndWorkflow(contractInfo, workflowInfo, minPlatformVersion, targetPlatformVersion)
}
else if (contractInfo != null) return contractInfo
else if (workflowInfo != null) return workflowInfo
/** need to maintain backwards compatibility so use old identifiers if existent */
val shortName = this["Name"] ?: defaultName
val vendor = this["Implementation-Vendor"] ?: UNKNOWN_VALUE
val version = this["Implementation-Version"] ?: UNKNOWN_VALUE
return Cordapp.Info.Default(
shortName = shortName,
vendor = vendor,
version = version,
licence = UNKNOWN_VALUE,
minimumPlatformVersion = minPlatformVersion,
targetPlatformVersion = targetPlatformVersion
)
}

View File

@ -1,13 +1,12 @@
package net.corda.core.internal.rules package net.corda.core.internal.rules
import net.corda.core.contracts.ContractState import net.corda.core.contracts.ContractState
import net.corda.core.utilities.contextLogger import net.corda.core.internal.cordapp.targetPlatformVersion
import net.corda.core.utilities.warnOnce import net.corda.core.utilities.warnOnce
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import java.net.URL import java.net.URL
import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.ConcurrentHashMap
import java.util.jar.JarInputStream import java.util.jar.JarInputStream
import java.util.jar.Manifest
// This file provides rules that depend on the targetVersion of the current Contract or Flow. // This file provides rules that depend on the targetVersion of the current Contract or Flow.
// Rules defined in this package are automatically removed from the DJVM in core-deterministic, // Rules defined in this package are automatically removed from the DJVM in core-deterministic,
@ -48,8 +47,3 @@ object StateContractValidationEnforcementRule {
return targetVersion >= 4 return targetVersion >= 4
} }
} }
private val Manifest.targetPlatformVersion: Int get() {
val minPlatformVersion = mainAttributes.getValue("Min-Platform-Version")?.toInt() ?: 1
return mainAttributes.getValue("Target-Platform-Version")?.toInt() ?: minPlatformVersion
}

View File

@ -6,6 +6,7 @@ import net.corda.core.contracts.TransactionVerificationException.OverlappingAtta
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.VisibleForTesting import net.corda.core.internal.VisibleForTesting
import net.corda.core.internal.cordapp.targetPlatformVersion
import net.corda.core.internal.createSimpleCache import net.corda.core.internal.createSimpleCache
import net.corda.core.internal.isUploaderTrusted import net.corda.core.internal.isUploaderTrusted
import net.corda.core.internal.toSynchronised import net.corda.core.internal.toSynchronised
@ -18,7 +19,6 @@ import java.io.ByteArrayOutputStream
import java.io.IOException import java.io.IOException
import java.io.InputStream import java.io.InputStream
import java.net.* import java.net.*
import java.util.jar.Manifest
/** /**
* A custom ClassLoader that knows how to load classes from a set of attachments. The attachments themselves only * A custom ClassLoader that knows how to load classes from a set of attachments. The attachments themselves only
@ -108,14 +108,6 @@ class AttachmentsClassLoader(attachments: List<Attachment>, parent: ClassLoader
} }
} }
// This was reused from: https://github.com/corda/corda/pull/4240.
// TODO - Once that is merged it should be extracted to a utility.
private val Manifest.targetPlatformVersion: Int
get() {
val minPlatformVersion = mainAttributes.getValue("Min-Platform-Version")?.toInt() ?: 1
return mainAttributes.getValue("Target-Platform-Version")?.toInt() ?: minPlatformVersion
}
@VisibleForTesting @VisibleForTesting
private fun readAttachment(attachment: Attachment, filepath: String): ByteArray { private fun readAttachment(attachment: Attachment, filepath: String): ByteArray {
ByteArrayOutputStream().use { ByteArrayOutputStream().use {

View File

@ -6,11 +6,11 @@ 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.contracts.Version
import net.corda.core.cordapp.DEFAULT_CORDAPP_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.rules.StateContractValidationEnforcementRule import net.corda.core.internal.rules.StateContractValidationEnforcementRule
import net.corda.core.internal.uncheckedCast import net.corda.core.internal.uncheckedCast
import net.corda.core.node.NetworkParameters import net.corda.core.node.NetworkParameters

View File

@ -4,11 +4,11 @@ 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.cordapp.DEFAULT_CORDAPP_VERSION
import net.corda.core.contracts.ContractAttachment.Companion.getContractVersion 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.*
import net.corda.core.internal.cordapp.CordappImpl.Companion.DEFAULT_CORDAPP_VERSION
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

View File

@ -7,13 +7,12 @@ 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.contracts.ContractAttachment.Companion.getContractVersion
import net.corda.core.contracts.Version
import net.corda.core.cordapp.DEFAULT_CORDAPP_VERSION
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.AbstractAttachment
import net.corda.core.internal.Emoji import net.corda.core.internal.Emoji
import net.corda.core.internal.SerializedStateAndRef import net.corda.core.internal.SerializedStateAndRef
import net.corda.core.internal.cordapp.CordappImpl.Companion.DEFAULT_CORDAPP_VERSION
import net.corda.core.internal.createComponentGroups 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

View File

@ -4,19 +4,22 @@ import co.paralleluniverse.fibers.Suspendable
import com.natpryce.hamkrest.* import com.natpryce.hamkrest.*
import com.natpryce.hamkrest.assertion.assert import com.natpryce.hamkrest.assertion.assert
import net.corda.core.contracts.Attachment import net.corda.core.contracts.Attachment
import net.corda.core.cordapp.DEFAULT_CORDAPP_VERSION
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.testing.internal.matchers.flow.willReturn
import net.corda.testing.internal.matchers.flow.willThrow
import net.corda.core.flows.mixins.WithMockNet import net.corda.core.flows.mixins.WithMockNet
import net.corda.core.identity.CordaX500Name import net.corda.core.identity.CordaX500Name
import net.corda.core.identity.Party import net.corda.core.identity.Party
import net.corda.core.internal.FetchAttachmentsFlow import net.corda.core.internal.FetchAttachmentsFlow
import net.corda.core.internal.FetchDataFlow import net.corda.core.internal.FetchDataFlow
import net.corda.core.internal.cordapp.CordappImpl.Companion.DEFAULT_CORDAPP_VERSION
import net.corda.core.internal.hash import net.corda.core.internal.hash
import net.corda.node.services.persistence.NodeAttachmentService import net.corda.node.services.persistence.NodeAttachmentService
import net.corda.testing.core.* import net.corda.testing.core.ALICE_NAME
import net.corda.testing.core.BOB_NAME
import net.corda.testing.core.makeUnique
import net.corda.testing.core.singleIdentity
import net.corda.testing.internal.fakeAttachment import net.corda.testing.internal.fakeAttachment
import net.corda.testing.internal.matchers.flow.willReturn
import net.corda.testing.internal.matchers.flow.willThrow
import net.corda.testing.node.internal.InternalMockNetwork import net.corda.testing.node.internal.InternalMockNetwork
import net.corda.testing.node.internal.InternalMockNodeParameters import net.corda.testing.node.internal.InternalMockNodeParameters
import net.corda.testing.node.internal.TestStartedNode import net.corda.testing.node.internal.TestStartedNode

View File

@ -1,5 +1,6 @@
package net.corda.core.internal.cordapp package net.corda.core.internal.cordapp
import net.corda.core.cordapp.Cordapp
import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThat
import org.junit.After import org.junit.After
import org.junit.Before import org.junit.Before
@ -17,7 +18,7 @@ class CordappInfoResolverTest {
fun `the correct cordapp resolver is used after calling withCordappInfo`() { fun `the correct cordapp resolver is used after calling withCordappInfo`() {
val defaultTargetVersion = 222 val defaultTargetVersion = 222
CordappInfoResolver.register(listOf(javaClass.name), CordappImpl.Info("test", "test", "2", 3, defaultTargetVersion)) CordappInfoResolver.register(listOf(javaClass.name), Cordapp.Info.Default("test", "test", "2", 3, defaultTargetVersion))
assertEquals(defaultTargetVersion, CordappInfoResolver.currentTargetVersion) assertEquals(defaultTargetVersion, CordappInfoResolver.currentTargetVersion)
val expectedTargetVersion = 555 val expectedTargetVersion = 555
@ -30,8 +31,8 @@ class CordappInfoResolverTest {
@Test @Test
fun `when more than one cordapp is registered for the same class, the resolver returns null`() { fun `when more than one cordapp is registered for the same class, the resolver returns null`() {
CordappInfoResolver.register(listOf(javaClass.name), CordappImpl.Info("test", "test", "2", 3, 222)) CordappInfoResolver.register(listOf(javaClass.name), Cordapp.Info.Default("test", "test", "2", 3, 222))
CordappInfoResolver.register(listOf(javaClass.name), CordappImpl.Info("test1", "test1", "1", 2, 456)) CordappInfoResolver.register(listOf(javaClass.name), Cordapp.Info.Default("test1", "test1", "1", 2, 456))
assertThat(CordappInfoResolver.currentCordappInfo).isNull() assertThat(CordappInfoResolver.currentCordappInfo).isNull()
} }
} }

View File

@ -4,7 +4,6 @@ import com.nhaarman.mockito_kotlin.doReturn
import com.nhaarman.mockito_kotlin.whenever import com.nhaarman.mockito_kotlin.whenever
import net.corda.core.contracts.* import net.corda.core.contracts.*
import net.corda.core.cordapp.CordappProvider import net.corda.core.cordapp.CordappProvider
import net.corda.core.cordapp.DEFAULT_CORDAPP_VERSION
import net.corda.core.crypto.CompositeKey import net.corda.core.crypto.CompositeKey
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.identity.Party import net.corda.core.identity.Party
@ -12,6 +11,7 @@ import net.corda.core.internal.AbstractAttachment
import net.corda.core.internal.DEPLOYED_CORDAPP_UPLOADER import net.corda.core.internal.DEPLOYED_CORDAPP_UPLOADER
import net.corda.core.internal.PLATFORM_VERSION import net.corda.core.internal.PLATFORM_VERSION
import net.corda.core.internal.RPC_UPLOADER import net.corda.core.internal.RPC_UPLOADER
import net.corda.core.internal.cordapp.CordappImpl.Companion.DEFAULT_CORDAPP_VERSION
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.node.services.AttachmentStorage import net.corda.core.node.services.AttachmentStorage

View File

@ -18,6 +18,13 @@ Unreleased
For a given contract class, the contract attachment of the output states must be of the same or newer version than the contract attachment of the input states. For a given contract class, the contract attachment of the output states must be of the same or newer version than the contract attachment of the input states.
See :ref:`Contract attachment non-downgrade rule <contract_non-downgrade_rule_ref>` for further information. See :ref:`Contract attachment non-downgrade rule <contract_non-downgrade_rule_ref>` for further information.
* Standardised CorDapp version identifiers in jar manifests (aligned with associated cordapp Gradle plugin changes).
Updated all samples to reflect new conventions.
* Introduction of unique CorDapp version identifiers in jar manifests for contract and flows/services CorDapps.
Updated all sample CorDapps to reflect new conventions.
See :ref:`CorDapp separation <cordapp_separation_ref>` for further information.
* Automatic Constraints propagation for hash-constrained states to signature-constrained states. * Automatic Constraints propagation for hash-constrained states to signature-constrained states.
This allows Corda 4 signed CorDapps using signature constraints to consume existing hash constrained states generated This allows Corda 4 signed CorDapps using signature constraints to consume existing hash constrained states generated
by unsigned CorDapps in previous versions of Corda. by unsigned CorDapps in previous versions of Corda.

View File

@ -368,13 +368,20 @@ There is an example project that demonstrates in ``samples`` called ``cordapp-co
Minimum and target platform version Minimum and target platform version
----------------------------------- -----------------------------------
CorDapps can advertise their minimum and target platform version. The minimum platform version indicates that a node has to run at least this version in order to be able to run this CorDapp. The target platform version indicates that a CorDapp was tested with this version of the Corda Platform and should be run at this API level if possible. It provides a means of maintaining behavioural compatibility for the cases where the platform's behaviour has changed. These attributes are specified in the JAR manifest of the CorDapp, for example: CorDapps can advertise their minimum and target platform version. The minimum platform version indicates that a node has to run at least this
version in order to be able to run this CorDapp. The target platform version indicates that a CorDapp was tested with this version of the Corda
Platform and should be run at this API level if possible. It provides a means of maintaining behavioural compatibility for the cases where the
platform's behaviour has changed. These attributes are specified in the JAR manifest of the CorDapp, for example:
.. sourcecode:: groovy .. sourcecode:: groovy
'Min-Platform-Version': 4 'Min-Platform-Version': 4
'Target-Platform-Version': 4 'Target-Platform-Version': 4
**Defaults**
- ``Target-Platform-Version`` (mandatory) is a whole number and must comply with the rules mentioned above.
- ``Min-Platform-Version`` (optional) will default to 1 if not specified.
Using the `cordapp` Gradle plugin, this can be achieved by putting this in your CorDapp's `build.gradle`: Using the `cordapp` Gradle plugin, this can be achieved by putting this in your CorDapp's `build.gradle`:
.. container:: codeset .. container:: codeset
@ -382,23 +389,80 @@ Using the `cordapp` Gradle plugin, this can be achieved by putting this in your
.. sourcecode:: groovy .. sourcecode:: groovy
cordapp { cordapp {
info {
targetPlatformVersion 4 targetPlatformVersion 4
minimumPlatformVersion 4 minimumPlatformVersion 4
} }
}
Without using the `cordapp` plugin, you can achieve the same by modifying the jar task as shown in this example: .. _cordapp_separation_ref:
Separation of CorDapp contracts, flows and services
---------------------------------------------------
It is recommended that **contract** code (states, commands, verification logic) be packaged separately from **business flows** (and associated services).
This decoupling enables *contracts* to evolve independently from the *flows* and *services* that use them. Contracts may even be specified and implemented by different
providers (eg. Corda currently ships with a cash financial contract which in turn is used in many other flows and many other CorDapps).
As of Corda 4, CorDapps can explicitly differentiate their type by specifying the following attributes in the JAR manifest:
.. sourcecode:: groovy
'Cordapp-Contract-Name'
'Cordapp-Contract-Version'
'Cordapp-Contract-Vendor'
'Cordapp-Contract-Licence'
'Cordapp-Workflow-Name'
'Cordapp-Workflow-Version'
'Cordapp-Workflow-Vendor'
'Cordapp-Workflow-Licence'
**Defaults**
``Cordapp-Contract-Name`` (optional) if specified, the following Contract related attributes are also used:
- ``Cordapp-Contract-Version`` (mandatory), must be a whole number starting from 1.
- ``Cordapp-Contract-Vendor`` (optional), defaults to UNKNOWN if not specified.
- ``Cordapp-Contract-Licence`` (optional), defaults to UNKNOWN if not specified.
``Cordapp-Workflow-Name`` (optional) if specified, the following Workflow related attributes are also used:
- ``Cordapp-Workflow-Version`` (mandatory), must be a whole number starting from 1.
- ``Cordapp-Workflow-Vendor`` (optional), defaults to UNKNOWN if not specified.
- ``Cordapp-Workflow-Licence`` (optional), defaults to UNKNOWN if not specified.
As with the general CorDapp attributes (minimum and target platform version), these can be specified using the Gradle `cordapp` plugin as follows:
For a contract only CorDapp we specify the `contract` tag:
.. container:: codeset .. container:: codeset
.. sourcecode:: groovy .. sourcecode:: groovy
jar { cordapp {
manifest { targetPlatformVersion 4
attributes( minimumPlatformVersion 3
'Min-Platform-Version': 4 contract {
'Target-Platform-Version': 4 name "my contract name"
) versionId 1
vendor "my company"
licence "my licence"
} }
} }
For a CorDapp that contains flows and/or services we specify the `workflow` tag:
.. container:: codeset
.. sourcecode:: groovy
cordapp {
targetPlatformVersion 4
minimumPlatformVersion 3
workflow {
name "my workflow name"
versionId 1
vendor "my company"
licence "my licence"
}
}
.. note:: It is possible, but *not recommended*, to include everything in a single CorDapp jar and use both the ``contract`` and ``workflow`` Gradle plugin tags.

View File

@ -35,12 +35,13 @@ publish {
name 'corda-notary-bft-smart' name 'corda-notary-bft-smart'
} }
cordapp { cordapp {
info {
name "net/corda/experimental/notary-bft-smart"
vendor "Corda Open Source"
targetPlatformVersion corda_platform_version.toInteger() targetPlatformVersion corda_platform_version.toInteger()
minimumPlatformVersion 1 minimumPlatformVersion 1
workflow {
name "net/corda/experimental/notary-bft-smart"
versionId 1
vendor "R3"
licence "Open Source (Apache 2)"
} }
} }

View File

@ -36,10 +36,12 @@ publish {
} }
cordapp { cordapp {
info {
name "net/corda/experimental/notary-raft"
vendor "Corda Open Source"
targetPlatformVersion corda_platform_version.toInteger() targetPlatformVersion corda_platform_version.toInteger()
minimumPlatformVersion 1 minimumPlatformVersion 1
workflow {
name "net/corda/experimental/notary-raft"
versionId 1
vendor "R3"
licence "Open Source (Apache 2)"
} }
} }

View File

@ -62,30 +62,22 @@ artifacts {
jar { jar {
baseName 'corda-finance' baseName 'corda-finance'
exclude "META-INF/*.DSA"
exclude "META-INF/*.RSA"
exclude "META-INF/*.SF"
exclude "META-INF/*.MF"
exclude "META-INF/LICENSE"
exclude "META-INF/NOTICE"
manifest {
attributes(
"Manifest-Version": "1.0",
"Specification-Title": description,
"Specification-Version": version,
"Specification-Vendor": "Corda Open Source",
"Implementation-Title": "$group.$baseName",
)
}
} }
cordapp { cordapp {
info {
name "net/corda/finance"
vendor "Corda Open Source"
targetPlatformVersion corda_platform_version.toInteger() targetPlatformVersion corda_platform_version.toInteger()
minimumPlatformVersion 1 minimumPlatformVersion 1
contract {
name "net/corda/finance/contracts"
versionId 1
vendor "R3"
licence "Open Source (Apache 2)"
}
workflow {
name "net/corda/finance/flows"
versionId 1
vendor "R3"
licence "Open Source (Apache 2)"
} }
// By default the Cordapp is signed by Corda development certificate, for production build pass the following system properties to Gradle to use specific keystore e.g: // By default the Cordapp is signed by Corda development certificate, for production build pass the following system properties to Gradle to use specific keystore e.g:
// ./gradlew -Dsigning.enabled="true" -Dsigning.keystore="/path/to/keystore.jks" -Dsigning.alias="alias" -Dsigning.storepass="password" -Dsigning.keypass="password" // ./gradlew -Dsigning.enabled="true" -Dsigning.keystore="/path/to/keystore.jks" -Dsigning.alias="alias" -Dsigning.storepass="password" -Dsigning.keypass="password"

View File

@ -4,13 +4,13 @@ import com.nhaarman.mockito_kotlin.any
import com.nhaarman.mockito_kotlin.doReturn import com.nhaarman.mockito_kotlin.doReturn
import com.nhaarman.mockito_kotlin.whenever import com.nhaarman.mockito_kotlin.whenever
import net.corda.core.contracts.* import net.corda.core.contracts.*
import net.corda.core.cordapp.DEFAULT_CORDAPP_VERSION
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
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.identity.Party import net.corda.core.identity.Party
import net.corda.core.internal.DEPLOYED_CORDAPP_UPLOADER import net.corda.core.internal.DEPLOYED_CORDAPP_UPLOADER
import net.corda.core.internal.RPC_UPLOADER import net.corda.core.internal.RPC_UPLOADER
import net.corda.core.internal.cordapp.CordappImpl.Companion.DEFAULT_CORDAPP_VERSION
import net.corda.core.node.ServicesForResolution import net.corda.core.node.ServicesForResolution
import net.corda.core.node.services.AttachmentStorage import net.corda.core.node.services.AttachmentStorage
import net.corda.core.node.services.NetworkParametersStorage import net.corda.core.node.services.NetworkParametersStorage

View File

@ -35,8 +35,8 @@ import kotlin.test.assertNotNull
class SignatureConstraintVersioningTests { class SignatureConstraintVersioningTests {
private val base = cordappForPackages(MessageState::class.packageName, DummyMessageContract::class.packageName) private val base = cordappForPackages(MessageState::class.packageName, DummyMessageContract::class.packageName)
private val oldCordapp = base.withCordappVersion("2") private val oldCordapp = base.withVersion("2")
private val newCordapp = base.withCordappVersion("3") private val newCordapp = base.withVersion("3")
private val user = User("mark", "dadada", setOf(startFlow<CreateMessage>(), startFlow<ConsumeMessage>(), invokeRpc("vaultQuery"))) private val user = User("mark", "dadada", setOf(startFlow<CreateMessage>(), startFlow<ConsumeMessage>(), invokeRpc("vaultQuery")))
private val message = Message("Hello world!") private val message = Message("Hello world!")
private val transformetMessage = Message(message.value + "A") private val transformetMessage = Message(message.value + "A")

View File

@ -243,9 +243,7 @@ open class NodeStartup : NodeStartupLogging {
} }
protected open fun logLoadedCorDapps(corDapps: List<CordappImpl>) { protected open fun logLoadedCorDapps(corDapps: List<CordappImpl>) {
fun CordappImpl.Info.description() = "$shortName version $version by $vendor" Node.printBasicNodeInfo("Loaded ${corDapps.size} CorDapp(s)", corDapps.map { it.info }.joinToString(", "))
Node.printBasicNodeInfo("Loaded ${corDapps.size} CorDapp(s)", corDapps.map { it.info }.joinToString(", ", transform = CordappImpl.Info::description))
corDapps.map { it.info }.filter { it.hasUnknownFields() }.let { malformed -> corDapps.map { it.info }.filter { it.hasUnknownFields() }.let { malformed ->
if (malformed.isNotEmpty()) { if (malformed.isNotEmpty()) {
logger.warn("Found ${malformed.size} CorDapp(s) with unknown information. They will be unable to run on Corda in the future.") logger.warn("Found ${malformed.size} CorDapp(s) with unknown information. They will be unable to run on Corda in the future.")

View File

@ -9,7 +9,9 @@ import net.corda.core.crypto.sha256
import net.corda.core.flows.* import net.corda.core.flows.*
import net.corda.core.internal.* import net.corda.core.internal.*
import net.corda.core.internal.cordapp.CordappImpl import net.corda.core.internal.cordapp.CordappImpl
import net.corda.core.internal.cordapp.CordappImpl.Companion.UNKNOWN
import net.corda.core.internal.cordapp.CordappInfoResolver import net.corda.core.internal.cordapp.CordappInfoResolver
import net.corda.core.internal.cordapp.toCordappInfo
import net.corda.core.internal.notary.NotaryService import net.corda.core.internal.notary.NotaryService
import net.corda.core.internal.notary.SinglePartyNotaryService import net.corda.core.internal.notary.SinglePartyNotaryService
import net.corda.core.node.services.CordaService import net.corda.core.node.services.CordaService
@ -129,7 +131,7 @@ class JarScanningCordappLoader private constructor(private val cordappJarPaths:
return cordapps return cordapps
} }
private fun RestrictedScanResult.toCordapp(url: RestrictedURL): CordappImpl { private fun RestrictedScanResult.toCordapp(url: RestrictedURL): CordappImpl {
val info = url.url.openStream().let(::JarInputStream).use { it.manifest?.toCordappInfo(CordappImpl.jarName(url.url)) ?: CordappImpl.Info.UNKNOWN } val info = url.url.openStream().let(::JarInputStream).use { it.manifest?.toCordappInfo(CordappImpl.jarName(url.url)) ?: UNKNOWN }
return CordappImpl( return CordappImpl(
findContractClassNames(this), findContractClassNames(this),
findInitiatedFlows(this), findInitiatedFlows(this),

View File

@ -1,53 +0,0 @@
package net.corda.node.internal.cordapp
import net.corda.core.cordapp.DEFAULT_CORDAPP_VERSION
import net.corda.core.internal.cordapp.CordappImpl
import net.corda.core.internal.cordapp.CordappImpl.Info.Companion.UNKNOWN_VALUE
import java.util.jar.Attributes
import java.util.jar.Manifest
//TODO implementationVersion parmemater and update `Implementation-Version` when we finally agree on a naming split for Contracts vs Flows jars.
fun createTestManifest(name: String, title: String, version: String, vendor: String, targetVersion: Int, implementationVersion: String): Manifest {
val manifest = Manifest()
// Mandatory manifest attribute. If not present, all other entries are silently skipped.
manifest.mainAttributes[Attributes.Name.MANIFEST_VERSION] = "1.0"
manifest["Name"] = name
manifest["Specification-Title"] = title
manifest["Specification-Version"] = version
manifest["Specification-Vendor"] = vendor
manifest["Implementation-Title"] = title
manifest[Attributes.Name.IMPLEMENTATION_VERSION] = implementationVersion
manifest["Implementation-Vendor"] = vendor
manifest["Target-Platform-Version"] = targetVersion.toString()
return manifest
}
operator fun Manifest.set(key: String, value: String): String? {
return mainAttributes.putValue(key, value)
}
operator fun Manifest.set(key: Attributes.Name, value: String): Any? {
return mainAttributes.put(key, value)
}
operator fun Manifest.get(key: String): String? = mainAttributes.getValue(key)
fun Manifest.toCordappInfo(defaultShortName: String): CordappImpl.Info {
val shortName = this["Name"] ?: defaultShortName
val vendor = this["Implementation-Vendor"] ?: UNKNOWN_VALUE
val version = this["Implementation-Version"] ?: DEFAULT_CORDAPP_VERSION.toString()
val minPlatformVersion = this["Min-Platform-Version"]?.toIntOrNull() ?: 1
val targetPlatformVersion = this["Target-Platform-Version"]?.toIntOrNull() ?: minPlatformVersion
return CordappImpl.Info(
shortName = shortName,
vendor = vendor,
version = version,
minimumPlatformVersion = minPlatformVersion,
targetPlatformVersion = targetPlatformVersion
)
}

View File

@ -1,5 +1,6 @@
package net.corda.node.internal.cordapp package net.corda.node.internal.cordapp
import net.corda.core.cordapp.Cordapp
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.flows.ContractUpgradeFlow import net.corda.core.flows.ContractUpgradeFlow
import net.corda.core.internal.cordapp.CordappImpl import net.corda.core.internal.cordapp.CordappImpl
@ -28,7 +29,7 @@ internal object VirtualCordapp {
serializationWhitelists = listOf(), serializationWhitelists = listOf(),
serializationCustomSerializers = listOf(), serializationCustomSerializers = listOf(),
customSchemas = setOf(), customSchemas = setOf(),
info = CordappImpl.Info("corda-core", versionInfo.vendor, versionInfo.releaseVersion, 1, versionInfo.platformVersion), info = Cordapp.Info.Default("corda-core", versionInfo.vendor, versionInfo.releaseVersion, 1, versionInfo.platformVersion),
allFlows = listOf(), allFlows = listOf(),
jarPath = ContractUpgradeFlow.javaClass.location, // Core JAR location jarPath = ContractUpgradeFlow.javaClass.location, // Core JAR location
jarHash = SecureHash.allOnesHash, jarHash = SecureHash.allOnesHash,
@ -49,7 +50,7 @@ internal object VirtualCordapp {
serializationWhitelists = listOf(), serializationWhitelists = listOf(),
serializationCustomSerializers = listOf(), serializationCustomSerializers = listOf(),
customSchemas = setOf(NodeNotarySchemaV1), customSchemas = setOf(NodeNotarySchemaV1),
info = CordappImpl.Info("corda-notary", versionInfo.vendor, versionInfo.releaseVersion, 1, versionInfo.platformVersion), info = Cordapp.Info.Default("corda-notary", versionInfo.vendor, versionInfo.releaseVersion, 1, versionInfo.platformVersion),
allFlows = listOf(), allFlows = listOf(),
jarPath = SimpleNotaryService::class.java.location, jarPath = SimpleNotaryService::class.java.location,
jarHash = SecureHash.allOnesHash, jarHash = SecureHash.allOnesHash,

View File

@ -7,18 +7,20 @@ import com.google.common.hash.Hashing
import com.google.common.hash.HashingInputStream import com.google.common.hash.HashingInputStream
import com.google.common.io.CountingInputStream import com.google.common.io.CountingInputStream
import net.corda.core.CordaRuntimeException import net.corda.core.CordaRuntimeException
import net.corda.core.contracts.* import net.corda.core.contracts.Attachment
import net.corda.core.cordapp.CORDAPP_CONTRACT_VERSION import net.corda.core.contracts.ContractAttachment
import net.corda.core.contracts.ContractClassName
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.cordapp.CordappImpl.Companion.DEFAULT_CORDAPP_VERSION
import net.corda.core.internal.cordapp.CordappImpl.Companion.CORDAPP_CONTRACT_VERSION
import net.corda.core.node.ServicesForResolution import net.corda.core.node.ServicesForResolution
import net.corda.core.node.services.AttachmentId import net.corda.core.node.services.AttachmentId
import net.corda.core.node.services.vault.AttachmentQueryCriteria import net.corda.core.node.services.vault.AttachmentQueryCriteria
import net.corda.core.node.services.vault.AttachmentSort import net.corda.core.node.services.vault.AttachmentSort
import net.corda.core.serialization.* import net.corda.core.serialization.*
import net.corda.core.utilities.contextLogger import net.corda.core.utilities.contextLogger
import net.corda.core.cordapp.DEFAULT_CORDAPP_VERSION
import net.corda.node.services.vault.HibernateAttachmentQueryCriteriaParser import net.corda.node.services.vault.HibernateAttachmentQueryCriteriaParser
import net.corda.node.utilities.NonInvalidatingCache import net.corda.node.utilities.NonInvalidatingCache
import net.corda.node.utilities.NonInvalidatingWeightBasedCache import net.corda.node.utilities.NonInvalidatingWeightBasedCache

View File

@ -4,16 +4,12 @@ import co.paralleluniverse.fibers.Suspendable
import com.codahale.metrics.MetricRegistry import com.codahale.metrics.MetricRegistry
import com.google.common.jimfs.Configuration import com.google.common.jimfs.Configuration
import com.google.common.jimfs.Jimfs import com.google.common.jimfs.Jimfs
import net.corda.testing.core.internal.ContractJarTestUtils.makeTestContractJar
import net.corda.testing.core.internal.ContractJarTestUtils.makeTestJar
import net.corda.testing.core.internal.ContractJarTestUtils.makeTestSignedContractJar
import net.corda.testing.core.internal.SelfCleaningDir
import net.corda.core.contracts.ContractAttachment import net.corda.core.contracts.ContractAttachment
import net.corda.core.cordapp.DEFAULT_CORDAPP_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.flows.FlowLogic import net.corda.core.flows.FlowLogic
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.node.ServicesForResolution import net.corda.core.node.ServicesForResolution
import net.corda.core.node.services.vault.AttachmentQueryCriteria.AttachmentsQueryCriteria import net.corda.core.node.services.vault.AttachmentQueryCriteria.AttachmentsQueryCriteria
import net.corda.core.node.services.vault.AttachmentSort import net.corda.core.node.services.vault.AttachmentSort
@ -23,6 +19,10 @@ import net.corda.core.utilities.getOrThrow
import net.corda.node.services.transactions.PersistentUniquenessProvider import net.corda.node.services.transactions.PersistentUniquenessProvider
import net.corda.nodeapi.internal.persistence.CordaPersistence import net.corda.nodeapi.internal.persistence.CordaPersistence
import net.corda.nodeapi.internal.persistence.DatabaseConfig import net.corda.nodeapi.internal.persistence.DatabaseConfig
import net.corda.testing.core.internal.ContractJarTestUtils.makeTestContractJar
import net.corda.testing.core.internal.ContractJarTestUtils.makeTestJar
import net.corda.testing.core.internal.ContractJarTestUtils.makeTestSignedContractJar
import net.corda.testing.core.internal.SelfCleaningDir
import net.corda.testing.internal.LogHelper import net.corda.testing.internal.LogHelper
import net.corda.testing.internal.TestingNamedCacheFactory import net.corda.testing.internal.TestingNamedCacheFactory
import net.corda.testing.internal.configureDatabase import net.corda.testing.internal.configureDatabase

View File

@ -114,10 +114,12 @@ jar {
} }
cordapp { cordapp {
info {
name "net/corda/samples/attachment-demo"
vendor "Corda Open Source"
targetPlatformVersion corda_platform_version.toInteger() targetPlatformVersion corda_platform_version.toInteger()
minimumPlatformVersion 1 minimumPlatformVersion 1
workflow {
name "net/corda/samples/attachment-demo"
versionId 1
vendor "R3"
licence "Open Source (Apache 2)"
} }
} }

View File

@ -115,10 +115,12 @@ jar {
} }
cordapp { cordapp {
info {
name "net/corda/samples/bank-of-corda-demo"
vendor "Corda Open Source"
targetPlatformVersion corda_platform_version.toInteger() targetPlatformVersion corda_platform_version.toInteger()
minimumPlatformVersion 1 minimumPlatformVersion 1
workflow {
name "net/corda/samples/bank-of-corda-demo"
versionId 1
vendor "R3"
licence "Open Source (Apache 2)"
} }
} }

View File

@ -59,10 +59,12 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar', nodeTask,
} }
cordapp { cordapp {
info {
name "net/corda/samples/cordapp-configuration"
vendor "Corda Open Source"
targetPlatformVersion corda_platform_version.toInteger() targetPlatformVersion corda_platform_version.toInteger()
minimumPlatformVersion 1 minimumPlatformVersion 1
workflow {
name "net/corda/samples/cordapp-configuration"
versionId 1
vendor "R3"
licence "Open Source (Apache 2)"
} }
} }

View File

@ -172,10 +172,18 @@ artifacts {
} }
cordapp { cordapp {
info {
name "net/corda/irs-demo"
vendor "Corda Open Source"
targetPlatformVersion corda_platform_version.toInteger() targetPlatformVersion corda_platform_version.toInteger()
minimumPlatformVersion 1 minimumPlatformVersion 1
contract {
name "net/corda/irs-demo/contract"
versionId 1
vendor "R3"
licence "Open Source (Apache 2)"
}
workflow {
name "net/corda/irs-demo/flows"
versionId 1
vendor "R3"
licence "Open Source (Apache 2)"
} }
} }

View File

@ -53,10 +53,12 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar', nodeTask])
} }
cordapp { cordapp {
info {
name "net/corda/samples/network-verifier"
vendor "Corda Open Source"
targetPlatformVersion corda_platform_version.toInteger() targetPlatformVersion corda_platform_version.toInteger()
minimumPlatformVersion 1 minimumPlatformVersion 1
workflow {
name "net/corda/samples/network-verifier"
versionId 1
vendor "R3"
licence "Open Source (Apache 2)"
} }
} }

View File

@ -255,10 +255,12 @@ jar {
} }
cordapp { cordapp {
info {
name "net/corda/samples/notary-demo"
vendor "Corda Open Source"
targetPlatformVersion corda_platform_version.toInteger() targetPlatformVersion corda_platform_version.toInteger()
minimumPlatformVersion 1 minimumPlatformVersion 1
workflow {
name "net/corda/samples/notary-demo"
versionId 1
vendor "R3"
licence "Open Source (Apache 2)"
} }
} }

View File

@ -140,8 +140,17 @@ task integrationTest(type: Test, dependsOn: []) {
} }
cordapp { cordapp {
info {
vendor = 'R3'
targetPlatformVersion = corda_platform_version.toInteger() targetPlatformVersion = corda_platform_version.toInteger()
contract {
name "net/corda/vega/contracts"
versionId 1
vendor "R3"
licence "Open Source (Apache 2)"
}
workflow {
name "net/corda/vega/flows"
versionId 1
vendor "R3"
licence "Open Source (Apache 2)"
} }
} }

View File

@ -4,10 +4,8 @@ def javaHome = System.getProperty('java.home')
def shrinkJar = file("$buildDir/libs/${project.name}-${project.version}-tiny.jar") def shrinkJar = file("$buildDir/libs/${project.name}-${project.version}-tiny.jar")
cordapp { cordapp {
info {
vendor = 'R3'
targetPlatformVersion = corda_platform_version.toInteger() targetPlatformVersion = corda_platform_version.toInteger()
} minimumPlatformVersion 1
signing { signing {
// Cordapp is signed after the "shrink" task. // Cordapp is signed after the "shrink" task.
enabled false enabled false
@ -16,6 +14,12 @@ cordapp {
// Cannot seal JAR because other module also defines classes in the package net.corda.vega.analytics // Cannot seal JAR because other module also defines classes in the package net.corda.vega.analytics
enabled false enabled false
} }
contract {
name "net/corda/vega/contracts"
versionId 1
vendor "R3"
licence "Open Source (Apache 2)"
}
} }
configurations { configurations {

View File

@ -2,14 +2,18 @@ apply plugin: 'net.corda.plugins.quasar-utils'
apply plugin: 'net.corda.plugins.cordapp' apply plugin: 'net.corda.plugins.cordapp'
cordapp { cordapp {
info {
vendor = 'R3'
targetPlatformVersion = corda_platform_version.toInteger() targetPlatformVersion = corda_platform_version.toInteger()
} minimumPlatformVersion 1
sealing { sealing {
// Cannot seal JAR because other module also defines classes in the package net.corda.vega.analytics // Cannot seal JAR because other module also defines classes in the package net.corda.vega.analytics
enabled false enabled false
} }
workflow {
name "net/corda/vega/flows"
versionId 1
vendor "R3"
licence "Open Source (Apache 2)"
}
} }
dependencies { dependencies {

View File

@ -140,10 +140,12 @@ jar {
} }
cordapp { cordapp {
info {
name "net/corda/samples/trader-demo"
vendor "Corda Open Source"
targetPlatformVersion corda_platform_version.toInteger() targetPlatformVersion corda_platform_version.toInteger()
minimumPlatformVersion 1 minimumPlatformVersion 1
workflow {
name "net/corda/samples/trader-demo"
versionId 1
vendor "R3"
licence "Open Source (Apache 2)"
} }
} }

View File

@ -1,7 +1,6 @@
package net.corda.testing.node package net.corda.testing.node
import net.corda.core.DoNotImplement import net.corda.core.DoNotImplement
import net.corda.core.cordapp.DEFAULT_CORDAPP_VERSION
import net.corda.core.internal.PLATFORM_VERSION import net.corda.core.internal.PLATFORM_VERSION
import net.corda.testing.node.internal.TestCordappImpl import net.corda.testing.node.internal.TestCordappImpl
import net.corda.testing.node.internal.simplifyScanPackages import net.corda.testing.node.internal.simplifyScanPackages
@ -27,9 +26,6 @@ interface TestCordapp {
/** Returns the target platform version, defaults to the current platform version if not specified. */ /** Returns the target platform version, defaults to the current platform version if not specified. */
val targetVersion: Int val targetVersion: Int
/** Returns the cordapp version. */
val cordappVersion: String
/** Returns the config for this CorDapp, defaults to empty if not specified. */ /** Returns the config for this CorDapp, defaults to empty if not specified. */
val config: Map<String, Any> val config: Map<String, Any>
@ -61,8 +57,6 @@ interface TestCordapp {
* Optionally can pass in the location of an existing java key store to use */ * Optionally can pass in the location of an existing java key store to use */
fun signJar(keyStorePath: Path? = null): TestCordappImpl fun signJar(keyStorePath: Path? = null): TestCordappImpl
fun withCordappVersion(version: String): TestCordappImpl
class Factory { class Factory {
companion object { companion object {
/** /**
@ -80,11 +74,10 @@ interface TestCordapp {
fun fromPackages(packageNames: Collection<String>): TestCordapp { fun fromPackages(packageNames: Collection<String>): TestCordapp {
return TestCordappImpl( return TestCordappImpl(
name = "test-name", name = "test-name",
version = "1.0", version = "1",
vendor = "test-vendor", vendor = "test-vendor",
title = "test-title", title = "test-title",
targetVersion = PLATFORM_VERSION, targetVersion = PLATFORM_VERSION,
cordappVersion = DEFAULT_CORDAPP_VERSION.toString(),
config = emptyMap(), config = emptyMap(),
packages = simplifyScanPackages(packageNames), packages = simplifyScanPackages(packageNames),
classes = emptySet() classes = emptySet()

View File

@ -8,7 +8,6 @@ data class TestCordappImpl(override val name: String,
override val vendor: String, override val vendor: String,
override val title: String, override val title: String,
override val targetVersion: Int, override val targetVersion: Int,
override val cordappVersion: String,
override val config: Map<String, Any>, override val config: Map<String, Any>,
override val packages: Set<String>, override val packages: Set<String>,
override val signJar: Boolean = false, override val signJar: Boolean = false,
@ -26,8 +25,6 @@ data class TestCordappImpl(override val name: String,
override fun withTargetVersion(targetVersion: Int): TestCordappImpl = copy(targetVersion = targetVersion) override fun withTargetVersion(targetVersion: Int): TestCordappImpl = copy(targetVersion = targetVersion)
override fun withCordappVersion(version: String): TestCordappImpl = copy(cordappVersion = version)
override fun withConfig(config: Map<String, Any>): TestCordappImpl = copy(config = config) override fun withConfig(config: Map<String, Any>): TestCordappImpl = copy(config = config)
override fun signJar(keyStorePath: Path?): TestCordappImpl = copy(signJar = true, keyStorePath = keyStorePath) override fun signJar(keyStorePath: Path?): TestCordappImpl = copy(signJar = true, keyStorePath = keyStorePath)

View File

@ -1,15 +1,22 @@
package net.corda.testing.node.internal package net.corda.testing.node.internal
import io.github.classgraph.ClassGraph import io.github.classgraph.ClassGraph
import net.corda.core.internal.cordapp.*
import net.corda.core.internal.cordapp.CordappImpl.Companion.CORDAPP_CONTRACT_NAME
import net.corda.core.internal.cordapp.CordappImpl.Companion.CORDAPP_CONTRACT_VERSION
import net.corda.core.internal.cordapp.CordappImpl.Companion.CORDAPP_WORKFLOW_NAME
import net.corda.core.internal.cordapp.CordappImpl.Companion.CORDAPP_WORKFLOW_VERSION
import net.corda.core.internal.cordapp.CordappImpl.Companion.TARGET_PLATFORM_VERSION
import net.corda.core.internal.outputStream import net.corda.core.internal.outputStream
import net.corda.node.internal.cordapp.createTestManifest
import net.corda.testing.node.TestCordapp import net.corda.testing.node.TestCordapp
import java.io.BufferedOutputStream import java.io.BufferedOutputStream
import java.nio.file.Path import java.nio.file.Path
import java.nio.file.attribute.FileTime import java.nio.file.attribute.FileTime
import java.time.Instant import java.time.Instant
import java.util.jar.Attributes
import java.util.jar.JarFile import java.util.jar.JarFile
import java.util.jar.JarOutputStream import java.util.jar.JarOutputStream
import java.util.jar.Manifest
import java.util.zip.ZipEntry import java.util.zip.ZipEntry
import kotlin.reflect.KClass import kotlin.reflect.KClass
@ -66,7 +73,7 @@ fun TestCordappImpl.packageAsJar(file: Path) {
.scan() .scan()
scanResult.use { scanResult.use {
val manifest = createTestManifest(name, title, version, vendor, targetVersion, cordappVersion) val manifest = createTestManifest(name, title, version, vendor, targetVersion)
JarOutputStream(file.outputStream()).use { jos -> JarOutputStream(file.outputStream()).use { jos ->
val time = FileTime.from(Instant.EPOCH) val time = FileTime.from(Instant.EPOCH)
val manifestEntry = ZipEntry(JarFile.MANIFEST_NAME).setCreationTime(time).setLastAccessTime(time).setLastModifiedTime(time) val manifestEntry = ZipEntry(JarFile.MANIFEST_NAME).setCreationTime(time).setLastAccessTime(time).setLastModifiedTime(time)
@ -85,3 +92,22 @@ fun TestCordappImpl.packageAsJar(file: Path) {
} }
} }
} }
fun createTestManifest(name: String, title: String, version: String, vendor: String, targetVersion: Int): Manifest {
val manifest = Manifest()
// Mandatory manifest attribute. If not present, all other entries are silently skipped.
manifest[Attributes.Name.MANIFEST_VERSION.toString()] = "1.0"
manifest["Name"] = name
manifest[Attributes.Name.IMPLEMENTATION_TITLE] = title
manifest[Attributes.Name.IMPLEMENTATION_VERSION] = version
manifest[Attributes.Name.IMPLEMENTATION_VENDOR] = vendor
manifest[CORDAPP_CONTRACT_NAME] = name
manifest[CORDAPP_CONTRACT_VERSION] = version
manifest[CORDAPP_WORKFLOW_NAME] = name
manifest[CORDAPP_WORKFLOW_VERSION] = version
manifest[TARGET_PLATFORM_VERSION] = targetVersion.toString()
return manifest
}

View File

@ -1,7 +1,10 @@
package net.corda.testing.node.internal package net.corda.testing.node.internal
import net.corda.core.internal.cordapp.CordappImpl.Companion.CORDAPP_CONTRACT_NAME
import net.corda.core.internal.cordapp.CordappImpl.Companion.CORDAPP_WORKFLOW_NAME
import net.corda.core.internal.cordapp.CordappImpl.Companion.TARGET_PLATFORM_VERSION
import net.corda.core.internal.cordapp.get
import net.corda.core.internal.inputStream import net.corda.core.internal.inputStream
import net.corda.node.internal.cordapp.get
import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThat
import org.junit.Rule import org.junit.Rule
import org.junit.Test import org.junit.Test
@ -34,8 +37,9 @@ class TestCordappsUtilsTest {
val jarFile = packageAsJar(cordapp) val jarFile = packageAsJar(cordapp)
JarInputStream(jarFile.inputStream()).use { JarInputStream(jarFile.inputStream()).use {
assertThat(it.manifest["Target-Platform-Version"]).isEqualTo("123") assertThat(it.manifest[TARGET_PLATFORM_VERSION]).isEqualTo("123")
assertThat(it.manifest["Name"]).isEqualTo("TestCordappsUtilsTest") assertThat(it.manifest[CORDAPP_CONTRACT_NAME]).isEqualTo("TestCordappsUtilsTest")
assertThat(it.manifest[CORDAPP_WORKFLOW_NAME]).isEqualTo("TestCordappsUtilsTest")
} }
} }

View File

@ -1,18 +1,21 @@
package net.corda.testing.core.internal package net.corda.testing.core.internal
import net.corda.core.internal.cordapp.CordappImpl.Companion.CORDAPP_CONTRACT_VERSION
import net.corda.core.internal.delete
import net.corda.core.internal.div
import net.corda.core.internal.toPath
import net.corda.testing.core.ALICE_NAME
import net.corda.testing.core.internal.JarSignatureTestUtils.addManifest import net.corda.testing.core.internal.JarSignatureTestUtils.addManifest
import net.corda.testing.core.internal.JarSignatureTestUtils.createJar import net.corda.testing.core.internal.JarSignatureTestUtils.createJar
import net.corda.testing.core.internal.JarSignatureTestUtils.generateKey import net.corda.testing.core.internal.JarSignatureTestUtils.generateKey
import net.corda.testing.core.internal.JarSignatureTestUtils.signJar import net.corda.testing.core.internal.JarSignatureTestUtils.signJar
import net.corda.core.internal.delete
import net.corda.core.internal.div
import net.corda.core.internal.toPath
import net.corda.core.cordapp.CORDAPP_CONTRACT_VERSION
import net.corda.testing.core.ALICE_NAME
import java.io.OutputStream import java.io.OutputStream
import java.net.URI import java.net.URI
import java.net.URL import java.net.URL
import java.nio.file.* import java.nio.file.Files
import java.nio.file.Path
import java.nio.file.Paths
import java.nio.file.StandardCopyOption
import java.security.PublicKey import java.security.PublicKey
import java.util.jar.Attributes import java.util.jar.Attributes
import java.util.jar.JarEntry import java.util.jar.JarEntry

View File

@ -16,7 +16,7 @@ import net.corda.core.schemas.MappedSchema
import net.corda.core.serialization.internal.effectiveSerializationEnv import net.corda.core.serialization.internal.effectiveSerializationEnv
import net.corda.core.transactions.WireTransaction import net.corda.core.transactions.WireTransaction
import net.corda.core.utilities.loggerFor import net.corda.core.utilities.loggerFor
import net.corda.node.internal.cordapp.set import net.corda.core.internal.cordapp.set
import net.corda.node.internal.createCordaPersistence import net.corda.node.internal.createCordaPersistence
import net.corda.node.internal.security.RPCSecurityManagerImpl import net.corda.node.internal.security.RPCSecurityManagerImpl
import net.corda.node.internal.startHikariPool import net.corda.node.internal.startHikariPool

View File

@ -5,6 +5,7 @@ import net.corda.core.cordapp.Cordapp
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.internal.DEPLOYED_CORDAPP_UPLOADER import net.corda.core.internal.DEPLOYED_CORDAPP_UPLOADER
import net.corda.core.internal.cordapp.CordappImpl import net.corda.core.internal.cordapp.CordappImpl
import net.corda.core.internal.cordapp.CordappImpl.Companion.UNKNOWN
import net.corda.core.node.services.AttachmentId import net.corda.core.node.services.AttachmentId
import net.corda.core.node.services.AttachmentStorage import net.corda.core.node.services.AttachmentStorage
import net.corda.node.cordapp.CordappLoader import net.corda.node.cordapp.CordappLoader
@ -40,7 +41,7 @@ class MockCordappProvider(
serializationCustomSerializers = emptyList(), serializationCustomSerializers = emptyList(),
customSchemas = emptySet(), customSchemas = emptySet(),
jarPath = Paths.get("").toUri().toURL(), jarPath = Paths.get("").toUri().toURL(),
info = CordappImpl.Info.UNKNOWN, info = UNKNOWN,
allFlows = emptyList(), allFlows = emptyList(),
jarHash = SecureHash.allOnesHash, jarHash = SecureHash.allOnesHash,
notaryService = null notaryService = null

View File

@ -3,11 +3,11 @@ package net.corda.testing.services
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.cordapp.DEFAULT_CORDAPP_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.AbstractAttachment import net.corda.core.internal.AbstractAttachment
import net.corda.core.internal.UNKNOWN_UPLOADER import net.corda.core.internal.UNKNOWN_UPLOADER
import net.corda.core.internal.cordapp.CordappImpl.Companion.DEFAULT_CORDAPP_VERSION
import net.corda.core.internal.readFully import net.corda.core.internal.readFully
import net.corda.core.node.services.AttachmentId import net.corda.core.node.services.AttachmentId
import net.corda.core.node.services.AttachmentStorage import net.corda.core.node.services.AttachmentStorage