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 813c203d22..97a5672846 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 @@ -5,6 +5,7 @@ import io.github.classgraph.ClassInfo import io.github.classgraph.ScanResult import net.corda.common.logging.errorReporting.CordappErrors import net.corda.common.logging.errorReporting.ErrorCode +import net.corda.core.CordaRuntimeException import net.corda.core.cordapp.Cordapp import net.corda.core.crypto.SecureHash import net.corda.core.crypto.sha256 @@ -105,12 +106,15 @@ class JarScanningCordappLoader private constructor(private val cordappJarPaths: } private fun loadCordapps(): List { + val invalidCordapps = mutableMapOf() + val cordapps = cordappJarPaths .map { url -> scanCordapp(url).use { it.toCordapp(url) } } .filter { if (it.minimumPlatformVersion > versionInfo.platformVersion) { logger.warn("Not loading CorDapp ${it.info.shortName} (${it.info.vendor}) as it requires minimum " + "platform version ${it.minimumPlatformVersion} (This node is running version ${versionInfo.platformVersion}).") + invalidCordapps.put("CorDapp requires minimumPlatformVersion: ${it.minimumPlatformVersion}, but was: ${versionInfo.platformVersion}", it.jarPath) false } else { true @@ -125,12 +129,19 @@ class JarScanningCordappLoader private constructor(private val cordappJarPaths: if (certificates.isEmpty() || (certificates - blockedCertificates).isNotEmpty()) true // Cordapp is not signed or it is signed by at least one non-blacklisted certificate else { - logger.warn("Not loading CorDapp ${it.info.shortName} (${it.info.vendor}) as it is signed by development key(s) only: " + + logger.warn("Not loading CorDapp ${it.info.shortName} (${it.info.vendor}) as it is signed by blacklisted key(s) only (probably development key): " + "${blockedCertificates.map { it.publicKey }}.") + invalidCordapps.put("Corresponding contracts are signed by blacklisted key(s) only (probably development key),", it.jarPath) false } } } + + if (invalidCordapps.isNotEmpty()) { + throw InvalidCordappException("Invalid Cordapps found, that couldn't be loaded: " + + "${invalidCordapps.map { "Problem: ${it.key} in Cordapp ${it.value}" }}, ") + } + cordapps.forEach(::register) return cordapps } @@ -447,7 +458,7 @@ class MultipleCordappsForFlowException( message: String, flowName: String, jars: String -) : Exception(message), ErrorCode { +) : CordaRuntimeException(message), ErrorCode { override val code = CordappErrors.MULTIPLE_CORDAPPS_FOR_FLOW override val parameters = listOf(flowName, jars) } @@ -459,19 +470,24 @@ class CordappInvalidVersionException( msg: String, override val code: CordappErrors, override val parameters: List = listOf() -) : Exception(msg), ErrorCode +) : CordaRuntimeException(msg), ErrorCode /** * Thrown if duplicate CorDapps are installed on the node */ class DuplicateCordappsInstalledException(app: Cordapp, duplicates: Set) - : IllegalStateException("The CorDapp (name: ${app.info.shortName}, file: ${app.name}) " + + : CordaRuntimeException("IllegalStateExcepion", "The CorDapp (name: ${app.info.shortName}, file: ${app.name}) " + "is installed multiple times on the node. The following files correspond to the exact same content: " + - "${duplicates.map { it.name }}"), ErrorCode { + "${duplicates.map { it.name }}", null), ErrorCode { override val code = CordappErrors.DUPLICATE_CORDAPPS_INSTALLED override val parameters = listOf(app.info.shortName, app.name, duplicates.map { it.name }) } +/** + * Thrown if an exception occurs during loading cordapps. + */ +class InvalidCordappException(message: String) : CordaRuntimeException(message) + abstract class CordappLoaderTemplate : CordappLoader { companion object { 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 2bd8a10258..1a62060097 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 @@ -203,7 +203,7 @@ class CordappProviderImplTests { Files.copy(signedJarPath, duplicateJarPath) val urls = asList(signedJarPath.toUri().toURL(), duplicateJarPath.toUri().toURL()) JarScanningCordappLoader.fromJarUrls(urls, VersionInfo.UNKNOWN).use { - assertFailsWith { + assertFailsWith { CordappProviderImpl(it, stubConfigProvider, attachmentStore).apply { start() } } } 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 831df948da..9bc3f15efe 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 @@ -137,11 +137,10 @@ class JarScanningCordappLoaderTest { assertThat(cordapp.minimumPlatformVersion).isEqualTo(2) } - @Test(timeout=300_000) + @Test(expected = InvalidCordappException::class, timeout = 300_000) fun `cordapp classloader does not load apps when their min platform version is greater than the node platform version`() { val jar = JarScanningCordappLoaderTest::class.java.getResource("versions/min-2-no-target.jar")!! - val loader = JarScanningCordappLoader.fromJarUrls(listOf(jar), VersionInfo.UNKNOWN.copy(platformVersion = 1)) - assertThat(loader.cordapps).hasSize(0) + JarScanningCordappLoader.fromJarUrls(listOf(jar), VersionInfo.UNKNOWN.copy(platformVersion = 1)).cordapps } @Test(timeout=300_000) @@ -165,11 +164,10 @@ class JarScanningCordappLoaderTest { assertThat(loader.cordapps).hasSize(1) } - @Test(timeout=300_000) + @Test(expected = InvalidCordappException::class, timeout = 300_000) fun `cordapp classloader does not load app signed by blacklisted certificate`() { val jar = JarScanningCordappLoaderTest::class.java.getResource("signed/signed-by-dev-key.jar")!! - val loader = JarScanningCordappLoader.fromJarUrls(listOf(jar), cordappsSignerKeyFingerprintBlacklist = DEV_PUB_KEY_HASHES) - assertThat(loader.cordapps).hasSize(0) + JarScanningCordappLoader.fromJarUrls(listOf(jar), cordappsSignerKeyFingerprintBlacklist = DEV_PUB_KEY_HASHES).cordapps } @Test(timeout=300_000)