diff --git a/core/src/main/kotlin/net/corda/core/internal/cordapp/CordappImpl.kt b/core/src/main/kotlin/net/corda/core/internal/cordapp/CordappImpl.kt index 5a459783e8..38891ef254 100644 --- a/core/src/main/kotlin/net/corda/core/internal/cordapp/CordappImpl.kt +++ b/core/src/main/kotlin/net/corda/core/internal/cordapp/CordappImpl.kt @@ -40,10 +40,11 @@ data class CordappImpl( override val cordappClasses: List = (rpcFlows + initiatedFlows + services + serializationWhitelists.map { javaClass }).map { 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) { + data class Info(val shortName: String, val vendor: String, val version: String, val minimumPlatformVersion: Int, val targetPlatformVersion: Int) { companion object { private const val UNKNOWN_VALUE = "Unknown" - val UNKNOWN = Info(UNKNOWN_VALUE, UNKNOWN_VALUE, UNKNOWN_VALUE) + + val UNKNOWN = Info(UNKNOWN_VALUE, UNKNOWN_VALUE, UNKNOWN_VALUE, 1, 1) } fun hasUnknownFields(): Boolean = arrayOf(shortName, vendor, version).any { it == UNKNOWN_VALUE } diff --git a/core/src/main/kotlin/net/corda/core/internal/cordapp/CordappInfoResolver.kt b/core/src/main/kotlin/net/corda/core/internal/cordapp/CordappInfoResolver.kt new file mode 100644 index 0000000000..835a3c47c0 --- /dev/null +++ b/core/src/main/kotlin/net/corda/core/internal/cordapp/CordappInfoResolver.kt @@ -0,0 +1,65 @@ +package net.corda.core.internal.cordapp + +import net.corda.core.utilities.loggerFor +import java.util.concurrent.ConcurrentHashMap + +/** + * Provides a way to acquire information about the calling CorDapp. + */ +object CordappInfoResolver { + private val logger = loggerFor() + private val cordappClasses: ConcurrentHashMap> = ConcurrentHashMap() + + // TODO use the StackWalker API once we migrate to Java 9+ + private var cordappInfoResolver: () -> CordappImpl.Info? = { + Exception().stackTrace + .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. + .firstOrNull { it.size < 2 }?.single() + } + + /* + * Associates class names with CorDapps or logs a warning when a CorDapp is already registered for a given class. + * This could happen when trying to run different versions of the same CorDapp on the same node. + */ + @Synchronized + fun register(classes: List, cordapp: CordappImpl.Info) { + classes.forEach { + if (cordappClasses.containsKey(it)) { + logger.warn("More than one CorDapp registered for $it.") + cordappClasses[it] = cordappClasses[it]!! + cordapp + } else { + cordappClasses[it] = setOf(cordapp) + } + } + } + + /* + * This should only be used when making a change that would break compatibility with existing CorDapps. The change + * can then be version-gated, meaning the old behaviour is used if the calling CorDapp's target version is lower + * than the platform version that introduces the new behaviour. + * In situations where a `[CordappProvider]` is available the CorDapp context should be obtained from there. + * + * @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.. + */ + fun getCorDappInfo(): CordappImpl.Info? = cordappInfoResolver() + + /** + * Temporarily switch out the internal resolver for another one. For use in testing. + */ + @Synchronized + fun withCordappInfoResolution(tempResolver: () -> CordappImpl.Info?, block: () -> Unit) { + val resolver = cordappInfoResolver + cordappInfoResolver = tempResolver + try { + block() + } finally { + cordappInfoResolver = resolver + } + } + + internal fun clear() { + cordappClasses.clear() + } +} \ No newline at end of file diff --git a/core/src/test/kotlin/net/corda/core/internal/cordapp/CordappInfoResolverTest.kt b/core/src/test/kotlin/net/corda/core/internal/cordapp/CordappInfoResolverTest.kt new file mode 100644 index 0000000000..2ae7403e1a --- /dev/null +++ b/core/src/test/kotlin/net/corda/core/internal/cordapp/CordappInfoResolverTest.kt @@ -0,0 +1,42 @@ +package net.corda.core.internal.cordapp + +import org.junit.After +import org.junit.Before +import org.junit.Test +import kotlin.test.assertEquals + +class CordappInfoResolverTest { + + @Before + @After + fun clearCordappInfoResolver() { + CordappInfoResolver.clear() + } + + @Test() + fun `The correct cordapp resolver is used after calling withCordappResolution`() { + val defaultTargetVersion = 222 + + CordappInfoResolver.register(listOf(javaClass.name), CordappImpl.Info("test", "test", "2", 3, defaultTargetVersion)) + assertEquals(defaultTargetVersion, returnCallingTargetVersion()) + + val expectedTargetVersion = 555 + CordappInfoResolver.withCordappInfoResolution( { CordappImpl.Info("foo", "bar", "1", 2, expectedTargetVersion) }) + { + val actualTargetVersion = returnCallingTargetVersion() + assertEquals(expectedTargetVersion, actualTargetVersion) + } + assertEquals(defaultTargetVersion, returnCallingTargetVersion()) + } + + @Test() + 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), CordappImpl.Info("test1", "test1", "1", 2, 456)) + assertEquals(0, returnCallingTargetVersion()) + } + + private fun returnCallingTargetVersion(): Int { + return CordappInfoResolver.getCorDappInfo()?.targetPlatformVersion ?: 0 + } +} \ No newline at end of file diff --git a/docs/source/changelog.rst b/docs/source/changelog.rst index 187628a4fc..78e75bd607 100644 --- a/docs/source/changelog.rst +++ b/docs/source/changelog.rst @@ -7,6 +7,8 @@ release, see :doc:`upgrade-notes`. Unreleased ---------- +* Introduce minimum and target platform version for CorDapps. + * New overload for ``CordaRPCClient.start()`` method allowing to specify target legal identity to use for RPC call. * Case insensitive vault queries can be specified via a boolean on applicable SQL criteria builder operators. By default queries will be case sensitive. diff --git a/docs/source/cordapp-build-systems.rst b/docs/source/cordapp-build-systems.rst index 7d3a4a8cb7..af785ae777 100644 --- a/docs/source/cordapp-build-systems.rst +++ b/docs/source/cordapp-build-systems.rst @@ -189,3 +189,30 @@ CorDapp configuration can be accessed from ``CordappContext::config`` whenever a There is an example project that demonstrates in ``samples`` called ``cordapp-configuration`` and API documentation in ``_. + + +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: + +.. sourcecode:: groovy + + 'Min-Platform-Version': 3 + 'Target-Platform-Version': 4 + + +In gradle, this can be achieved by modifying the jar task as shown in this example: + +.. container:: codeset + + .. sourcecode:: groovy + + jar { + manifest { + attributes( + 'Min-Platform-Version': 3 + 'Target-Platform-Version': 4 + ) + } + } diff --git a/docs/source/versioning.rst b/docs/source/versioning.rst index 62b7cdc7a2..9d89d5c7b3 100644 --- a/docs/source/versioning.rst +++ b/docs/source/versioning.rst @@ -19,8 +19,4 @@ and the release version - a change in the major, minor or patch values may or ma The Platform Version is part of the node's ``NodeInfo`` object, which is available from the ``ServiceHub``. This enables a CorDapp to find out which version it's running on and determine whether a desired feature is available. When a node registers with the Network Map Service it will use the node's Platform Version to enforce a minimum version requirement -for the network. - -.. note:: A future release may introduce the concept of a target platform version, which would be similar to Android's - ``targetSdkVersion``, and would provide a means of maintaining behavioural compatibility for the cases where the - platform's behaviour has changed. \ No newline at end of file +for the network. \ No newline at end of file diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/AttachmentsClassLoaderStaticContractTests.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/AttachmentsClassLoaderStaticContractTests.kt index 0909707ac4..c699439e19 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/AttachmentsClassLoaderStaticContractTests.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/AttachmentsClassLoaderStaticContractTests.kt @@ -15,6 +15,7 @@ import net.corda.core.serialization.deserialize import net.corda.core.serialization.serialize import net.corda.core.transactions.LedgerTransaction import net.corda.core.transactions.TransactionBuilder +import net.corda.node.VersionInfo import net.corda.node.cordapp.CordappLoader import net.corda.node.internal.cordapp.CordappProviderImpl import net.corda.node.internal.cordapp.JarScanningCordappLoader @@ -110,7 +111,7 @@ class AttachmentsClassLoaderStaticContractTests { val cordapps = cordappsForPackages(packages) return testDirectory().let { directory -> cordapps.packageInDirectory(directory) - JarScanningCordappLoader.fromDirectories(listOf(directory)) + JarScanningCordappLoader.fromDirectories(listOf(directory), VersionInfo.UNKNOWN) } } diff --git a/node/src/integration-test/kotlin/net/corda/node/services/AttachmentLoadingTests.kt b/node/src/integration-test/kotlin/net/corda/node/services/AttachmentLoadingTests.kt index fb17259383..d21de023cb 100644 --- a/node/src/integration-test/kotlin/net/corda/node/services/AttachmentLoadingTests.kt +++ b/node/src/integration-test/kotlin/net/corda/node/services/AttachmentLoadingTests.kt @@ -23,8 +23,10 @@ import net.corda.core.serialization.SerializationFactory import net.corda.core.transactions.TransactionBuilder import net.corda.core.utilities.contextLogger import net.corda.core.utilities.getOrThrow +import net.corda.node.VersionInfo import net.corda.node.internal.cordapp.JarScanningCordappLoader import net.corda.node.internal.cordapp.CordappProviderImpl +import net.corda.nodeapi.internal.PLATFORM_VERSION import net.corda.testing.common.internal.testNetworkParameters import net.corda.testing.core.DUMMY_BANK_A_NAME import net.corda.testing.core.DUMMY_NOTARY_NAME @@ -50,7 +52,7 @@ class AttachmentLoadingTests { @JvmField val testSerialization = SerializationEnvironmentRule() private val attachments = MockAttachmentStorage() - private val provider = CordappProviderImpl(JarScanningCordappLoader.fromJarUrls(listOf(isolatedJAR)), MockCordappConfigProvider(), attachments).apply { + private val provider = CordappProviderImpl(JarScanningCordappLoader.fromJarUrls(listOf(isolatedJAR), VersionInfo.UNKNOWN), MockCordappConfigProvider(), attachments).apply { start(testNetworkParameters().whitelistedContractImplementations) } private val cordapp get() = provider.cordapps.first() diff --git a/node/src/main/kotlin/net/corda/node/VersionInfo.kt b/node/src/main/kotlin/net/corda/node/VersionInfo.kt index eacbb25133..b3d0ec0fbb 100644 --- a/node/src/main/kotlin/net/corda/node/VersionInfo.kt +++ b/node/src/main/kotlin/net/corda/node/VersionInfo.kt @@ -1,5 +1,7 @@ package net.corda.node +import net.corda.nodeapi.internal.PLATFORM_VERSION + /** * Encapsulates various pieces of version information of the node. */ @@ -17,6 +19,6 @@ data class VersionInfo( val vendor: String) { companion object { - val UNKNOWN = VersionInfo(1, "Unknown", "Unknown", "Unknown") + val UNKNOWN = VersionInfo(PLATFORM_VERSION, "Unknown", "Unknown", "Unknown") } -} +} \ No newline at end of file diff --git a/node/src/main/kotlin/net/corda/node/internal/cordapp/JarScanningCordappLoader.kt b/node/src/main/kotlin/net/corda/node/internal/cordapp/JarScanningCordappLoader.kt index be3c8e4deb..c691af76c7 100644 --- a/node/src/main/kotlin/net/corda/node/internal/cordapp/JarScanningCordappLoader.kt +++ b/node/src/main/kotlin/net/corda/node/internal/cordapp/JarScanningCordappLoader.kt @@ -8,6 +8,7 @@ import net.corda.core.crypto.sha256 import net.corda.core.flows.* import net.corda.core.internal.* import net.corda.core.internal.cordapp.CordappImpl +import net.corda.core.internal.cordapp.CordappInfoResolver import net.corda.core.node.services.CordaService import net.corda.core.schemas.MappedSchema import net.corda.core.serialization.SerializationCustomSerializer @@ -35,7 +36,7 @@ import kotlin.streams.toList * @property cordappJarPaths The classpath of cordapp JARs */ class JarScanningCordappLoader private constructor(private val cordappJarPaths: List, - versionInfo: VersionInfo = VersionInfo.UNKNOWN) : CordappLoaderTemplate() { + private val versionInfo: VersionInfo = VersionInfo.UNKNOWN) : CordappLoaderTemplate() { override val cordapps: List by lazy { loadCordapps() + coreCordapp } @@ -104,13 +105,25 @@ class JarScanningCordappLoader private constructor(private val cordappJarPaths: serializationWhitelists = listOf(), serializationCustomSerializers = listOf(), customSchemas = setOf(), - info = CordappImpl.Info("corda-core", versionInfo.vendor, versionInfo.releaseVersion), + info = CordappImpl.Info("corda-core", versionInfo.vendor, versionInfo.releaseVersion, 1, versionInfo.platformVersion), allFlows = listOf(), jarPath = ContractUpgradeFlow.javaClass.location, // Core JAR location jarHash = SecureHash.allOnesHash ) - private fun loadCordapps(): List = cordappJarPaths.map { scanCordapp(it).toCordapp(it) } + private fun loadCordapps(): List { + val cordapps = cordappJarPaths.map { scanCordapp(it).toCordapp(it) } + .filter { + if (it.info.minimumPlatformVersion > versionInfo.platformVersion) { + logger.warn("Not loading CorDapp ${it.info.shortName} (${it.info.vendor}) as it requires minimum platform version ${it.info.minimumPlatformVersion} (This node is running version ${versionInfo.platformVersion}).") + false + } else { + true + } + } + cordapps.forEach { CordappInfoResolver.register(it.cordappClasses, it.info) } + return cordapps + } private fun RestrictedScanResult.toCordapp(url: RestrictedURL): CordappImpl { val info = url.url.openStream().let(::JarInputStream).use { it.manifest }.toCordappInfo(CordappImpl.jarName(url.url)) diff --git a/node/src/main/kotlin/net/corda/node/internal/cordapp/ManifestUtils.kt b/node/src/main/kotlin/net/corda/node/internal/cordapp/ManifestUtils.kt index 08dd924243..c71a5bf7cb 100644 --- a/node/src/main/kotlin/net/corda/node/internal/cordapp/ManifestUtils.kt +++ b/node/src/main/kotlin/net/corda/node/internal/cordapp/ManifestUtils.kt @@ -38,5 +38,8 @@ fun Manifest?.toCordappInfo(defaultShortName: String): CordappImpl.Info { this?.mainAttributes?.getValue("Implementation-Version")?.let { version -> info = info.copy(version = version) } + val minPlatformVersion = this?.mainAttributes?.getValue("Min-Platform-Version")?.toInt() ?: 1 + val targetPlatformVersion = this?.mainAttributes?.getValue("Target-Platform-Version")?.toInt() ?: minPlatformVersion + info = info.copy(minimumPlatformVersion = minPlatformVersion, targetPlatformVersion = targetPlatformVersion) return info -} +} \ No newline at end of file diff --git a/node/src/test/kotlin/net/corda/node/internal/cordapp/CordappProviderImplTests.kt b/node/src/test/kotlin/net/corda/node/internal/cordapp/CordappProviderImplTests.kt index 8d1f712399..d622f834f8 100644 --- a/node/src/test/kotlin/net/corda/node/internal/cordapp/CordappProviderImplTests.kt +++ b/node/src/test/kotlin/net/corda/node/internal/cordapp/CordappProviderImplTests.kt @@ -3,6 +3,8 @@ package net.corda.node.internal.cordapp import com.typesafe.config.Config import com.typesafe.config.ConfigFactory import net.corda.core.node.services.AttachmentStorage +import net.corda.node.VersionInfo +import net.corda.nodeapi.internal.PLATFORM_VERSION import net.corda.testing.common.internal.testNetworkParameters import net.corda.testing.internal.MockCordappConfigProvider import net.corda.testing.services.MockAttachmentStorage @@ -75,7 +77,7 @@ class CordappProviderImplTests { fun `test cordapp configuration`() { val configProvider = MockCordappConfigProvider() configProvider.cordappConfigs[isolatedCordappName] = validConfig - val loader = JarScanningCordappLoader.fromJarUrls(listOf(isolatedJAR)) + val loader = JarScanningCordappLoader.fromJarUrls(listOf(isolatedJAR), VersionInfo.UNKNOWN) val provider = CordappProviderImpl(loader, configProvider, attachmentStore).apply { start(whitelistedContractImplementations) } val expected = provider.getAppContext(provider.cordapps.first()).config @@ -84,7 +86,7 @@ class CordappProviderImplTests { } private fun newCordappProvider(vararg urls: URL): CordappProviderImpl { - val loader = JarScanningCordappLoader.fromJarUrls(urls.toList()) + val loader = JarScanningCordappLoader.fromJarUrls(urls.toList(), VersionInfo.UNKNOWN) return CordappProviderImpl(loader, stubConfigProvider, attachmentStore).apply { start(whitelistedContractImplementations) } } } diff --git a/node/src/test/kotlin/net/corda/node/internal/cordapp/JarScanningCordappLoaderTest.kt b/node/src/test/kotlin/net/corda/node/internal/cordapp/JarScanningCordappLoaderTest.kt index e46cb15278..e71786f8b6 100644 --- a/node/src/test/kotlin/net/corda/node/internal/cordapp/JarScanningCordappLoaderTest.kt +++ b/node/src/test/kotlin/net/corda/node/internal/cordapp/JarScanningCordappLoaderTest.kt @@ -2,7 +2,9 @@ package net.corda.node.internal.cordapp import co.paralleluniverse.fibers.Suspendable import net.corda.core.flows.* +import net.corda.node.VersionInfo import net.corda.node.cordapp.CordappLoader +import net.corda.nodeapi.internal.PLATFORM_VERSION import net.corda.testing.node.internal.cordappsForPackages import net.corda.testing.node.internal.getTimestampAsDirectoryName import net.corda.testing.node.internal.packageInDirectory @@ -101,13 +103,70 @@ class JarScanningCordappLoaderTest { @Test fun `cordapp classloader can load cordapp classes`() { val isolatedJAR = JarScanningCordappLoaderTest::class.java.getResource("isolated.jar")!! - val loader = JarScanningCordappLoader.fromJarUrls(listOf(isolatedJAR)) + val loader = JarScanningCordappLoader.fromJarUrls(listOf(isolatedJAR), VersionInfo.UNKNOWN) loader.appClassLoader.loadClass(isolatedContractId) loader.appClassLoader.loadClass(isolatedFlowName) } - private fun cordappLoaderForPackages(packages: Iterable): CordappLoader { + @Test + fun `cordapp classloader sets target and min version to 1 if not specified`() { + val jar = JarScanningCordappLoaderTest::class.java.getResource("versions/no-min-or-target-version.jar")!! + val loader = JarScanningCordappLoader.fromJarUrls(listOf(jar), VersionInfo.UNKNOWN) + loader.cordapps.filter { !it.info.shortName.equals("corda-core") }.forEach { + assertThat(it.info.targetPlatformVersion).isEqualTo(1) + assertThat(it.info.minimumPlatformVersion).isEqualTo(1) + } + } + + @Test + fun `cordapp classloader returns correct values for minPlatformVersion and targetVersion`() { + // load jar with min and target version in manifest + // make sure classloader extracts correct values + val jar = JarScanningCordappLoaderTest::class.java.getResource("versions/min-2-target-3.jar")!! + val loader = JarScanningCordappLoader.fromJarUrls(listOf(jar), VersionInfo.UNKNOWN) + // exclude the core cordapp + val cordapp = loader.cordapps.filter { it.cordappClasses.contains("net.corda.core.internal.cordapp.CordappImpl")}.single() + assertThat(cordapp.info.targetPlatformVersion).isEqualTo(3) + assertThat(cordapp.info.minimumPlatformVersion).isEqualTo(2) + } + + @Test + fun `cordapp classloader sets target version to min version if target version is not specified`() { + // load jar with minVersion but not targetVersion in manifest + val jar = JarScanningCordappLoaderTest::class.java.getResource("versions/min-2-no-target.jar")!! + val loader = JarScanningCordappLoader.fromJarUrls(listOf(jar), VersionInfo.UNKNOWN) + // exclude the core cordapp + val cordapp = loader.cordapps.filter { it.cordappClasses.contains("net.corda.core.internal.cordapp.CordappImpl")}.single() + assertThat(cordapp.info.targetPlatformVersion).isEqualTo(2) + assertThat(cordapp.info.minimumPlatformVersion).isEqualTo(2) + } + + @Test + fun `cordapp classloader does not load apps when their min platform version is greater than the platform version`() { + val jar = JarScanningCordappLoaderTest::class.java.getResource("versions/min-2-target-3.jar")!! + val loader = JarScanningCordappLoader.fromJarUrls(listOf(jar), VersionInfo.UNKNOWN.copy(platformVersion = 1)) + // exclude the core cordapp + assertThat(loader.cordapps.size).isEqualTo(1) + } + + @Test + fun `cordapp classloader does load apps when their min platform version is less than the platform version`() { + val jar = JarScanningCordappLoaderTest::class.java.getResource("versions/min-2-target-3.jar")!! + val loader = JarScanningCordappLoader.fromJarUrls(listOf(jar), VersionInfo.UNKNOWN.copy(platformVersion = 1000)) + // exclude the core cordapp + assertThat(loader.cordapps.size).isEqualTo(2) + } + + @Test + fun `cordapp classloader does load apps when their min platform version is equal to the platform version`() { + val jar = JarScanningCordappLoaderTest::class.java.getResource("versions/min-2-target-3.jar")!! + val loader = JarScanningCordappLoader.fromJarUrls(listOf(jar), VersionInfo.UNKNOWN.copy(platformVersion = 2)) + // exclude the core cordapp + assertThat(loader.cordapps.size).isEqualTo(2) + } + + private fun cordappLoaderForPackages(packages: Iterable, versionInfo: VersionInfo = VersionInfo.UNKNOWN): CordappLoader { val cordapps = cordappsForPackages(packages) return testDirectory().let { directory -> diff --git a/node/src/test/resources/net/corda/node/internal/cordapp/versions/min-2-no-target.jar b/node/src/test/resources/net/corda/node/internal/cordapp/versions/min-2-no-target.jar new file mode 100644 index 0000000000..4496915841 Binary files /dev/null and b/node/src/test/resources/net/corda/node/internal/cordapp/versions/min-2-no-target.jar differ diff --git a/node/src/test/resources/net/corda/node/internal/cordapp/versions/min-2-target-3.jar b/node/src/test/resources/net/corda/node/internal/cordapp/versions/min-2-target-3.jar new file mode 100644 index 0000000000..1b66fba425 Binary files /dev/null and b/node/src/test/resources/net/corda/node/internal/cordapp/versions/min-2-target-3.jar differ diff --git a/node/src/test/resources/net/corda/node/internal/cordapp/versions/no-min-or-target-version.jar b/node/src/test/resources/net/corda/node/internal/cordapp/versions/no-min-or-target-version.jar new file mode 100644 index 0000000000..408e70145d Binary files /dev/null and b/node/src/test/resources/net/corda/node/internal/cordapp/versions/no-min-or-target-version.jar differ diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockServices.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockServices.kt index 6e085b6c70..be91c42c1f 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockServices.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockServices.kt @@ -18,6 +18,7 @@ import net.corda.core.node.services.* import net.corda.core.serialization.SerializeAsToken import net.corda.core.transactions.SignedTransaction import net.corda.core.utilities.NetworkHostAndPort +import net.corda.node.VersionInfo import net.corda.node.cordapp.CordappLoader import net.corda.node.internal.ServicesForResolutionImpl import net.corda.node.internal.configureDatabase @@ -27,6 +28,7 @@ import net.corda.node.services.identity.InMemoryIdentityService import net.corda.node.services.schema.NodeSchemaService import net.corda.node.services.transactions.InMemoryTransactionVerifierService import net.corda.node.services.vault.NodeVaultService +import net.corda.nodeapi.internal.PLATFORM_VERSION import net.corda.nodeapi.internal.persistence.CordaPersistence import net.corda.nodeapi.internal.persistence.DatabaseConfig import net.corda.nodeapi.internal.persistence.HibernateConfiguration @@ -69,10 +71,10 @@ open class MockServices private constructor( companion object { - private fun cordappLoaderForPackages(packages: Iterable): CordappLoader { + private fun cordappLoaderForPackages(packages: Iterable, versionInfo: VersionInfo = VersionInfo.UNKNOWN): CordappLoader { val cordappPaths = TestCordappDirectories.forPackages(packages) - return JarScanningCordappLoader.fromDirectories(cordappPaths) + return JarScanningCordappLoader.fromDirectories(cordappPaths, versionInfo) } /** diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/InternalMockNetwork.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/InternalMockNetwork.kt index ba52e79e72..cebb7f6d30 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/InternalMockNetwork.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/InternalMockNetwork.kt @@ -276,7 +276,7 @@ open class InternalMockNetwork(defaultParameters: MockNetworkParameters = MockNe } } - open class MockNode(args: MockNodeArgs, cordappLoader: CordappLoader = JarScanningCordappLoader.fromDirectories(args.config.cordappDirectories)) : AbstractNode( + open class MockNode(args: MockNodeArgs, cordappLoader: CordappLoader = JarScanningCordappLoader.fromDirectories(args.config.cordappDirectories, args.version)) : AbstractNode( args.config, TestClock(Clock.systemUTC()), DefaultNamedCacheFactory(), @@ -474,7 +474,7 @@ open class InternalMockNetwork(defaultParameters: MockNetworkParameters = MockNe val cordappDirectories = sharedCorDappsDirectories + TestCordappDirectories.cached(cordapps) doReturn(cordappDirectories).whenever(config).cordappDirectories - val node = nodeFactory(MockNodeArgs(config, this, id, parameters.entropyRoot, parameters.version), JarScanningCordappLoader.fromDirectories(cordappDirectories)) + val node = nodeFactory(MockNodeArgs(config, this, id, parameters.entropyRoot, parameters.version), JarScanningCordappLoader.fromDirectories(cordappDirectories, parameters.version)) _nodes += node if (start) { node.start()