diff --git a/.ci/dev/open-j9/Jenkinsfile b/.ci/dev/open-j9/Jenkinsfile new file mode 100644 index 0000000000..cb490c9b1f --- /dev/null +++ b/.ci/dev/open-j9/Jenkinsfile @@ -0,0 +1,49 @@ +import static com.r3.build.BuildControl.killAllExistingBuildsForJob +@Library('existing-build-control') +import static com.r3.build.BuildControl.killAllExistingBuildsForJob + +killAllExistingBuildsForJob(env.JOB_NAME, env.BUILD_NUMBER.toInteger()) + +pipeline { + agent { label 'open-j9' } + options { + timestamps() + timeout(time: 10, unit: 'HOURS') + } + + environment { + EXECUTOR_NUMBER = "${env.EXECUTOR_NUMBER}" + } + + stages { + stage('Unit Tests') { + steps { + sh "./gradlew clean --continue test --info" + } + } + stage('Integration Tests') { + steps { + sh "./gradlew clean --continue integrationTest --info" + } + } + stage('Smoke Tests') { + steps { + sh "./gradlew clean --continue smokeTest --info" + } + } + stage('Slow Integration Tests') { + steps { + sh "./gradlew clean --continue slowIntegrationTest --info" + } + } + } + + post { + always { + junit '**/build/test-results/**/*.xml' + } + cleanup { + deleteDir() /* clean up our workspace */ + } + } +} \ No newline at end of file diff --git a/constants.properties b/constants.properties index 5b3e020471..d3b5385722 100644 --- a/constants.properties +++ b/constants.properties @@ -16,11 +16,11 @@ guavaVersion=28.0-jre # Quasar version to use with Java 8: quasarVersion=0.7.13_r3 # Quasar version to use with Java 11: -quasarVersion11=0.8.0_r3 +quasarVersion11=0.8.1_r3 jdkClassifier11=jdk11 proguardVersion=6.1.1 bouncycastleVersion=1.66 -classgraphVersion=4.8.78 +classgraphVersion=4.8.89 disruptorVersion=3.4.2 typesafeConfigVersion=1.3.4 jsr305Version=3.0.2 diff --git a/core-tests/src/test/kotlin/net/corda/coretests/flows/FlowSleepTest.kt b/core-tests/src/test/kotlin/net/corda/coretests/flows/FlowSleepTest.kt index 194c78bee8..a18e04446d 100644 --- a/core-tests/src/test/kotlin/net/corda/coretests/flows/FlowSleepTest.kt +++ b/core-tests/src/test/kotlin/net/corda/coretests/flows/FlowSleepTest.kt @@ -18,6 +18,8 @@ import net.corda.testing.core.BOB_NAME import net.corda.testing.core.singleIdentity import net.corda.testing.driver.DriverParameters import net.corda.testing.driver.driver +import net.corda.testing.internal.IS_OPENJ9 +import org.junit.Assume import org.junit.Test import java.time.Duration import java.time.Instant @@ -27,6 +29,7 @@ class FlowSleepTest { @Test(timeout = 300_000) fun `flow can sleep`() { + Assume.assumeTrue(!IS_OPENJ9) driver(DriverParameters(notarySpecs = emptyList(), startNodesInProcess = true)) { val alice = startNode(providedName = ALICE_NAME).getOrThrow() val (start, finish) = alice.rpc.startFlow(::SleepyFlow).returnValue.getOrThrow(1.minutes) @@ -52,6 +55,7 @@ class FlowSleepTest { @Test(timeout = 300_000) fun `flow can sleep and perform other suspending functions`() { + Assume.assumeTrue(!IS_OPENJ9) // ensures that events received while the flow is sleeping are not processed driver(DriverParameters(notarySpecs = emptyList(), startNodesInProcess = true)) { val (alice, bob) = listOf(ALICE_NAME, BOB_NAME) diff --git a/core/src/main/kotlin/net/corda/core/crypto/Crypto.kt b/core/src/main/kotlin/net/corda/core/crypto/Crypto.kt index 0dc3fcbc59..0e2ea7be43 100644 --- a/core/src/main/kotlin/net/corda/core/crypto/Crypto.kt +++ b/core/src/main/kotlin/net/corda/core/crypto/Crypto.kt @@ -35,6 +35,8 @@ import org.bouncycastle.asn1.x9.X9ObjectIdentifiers import org.bouncycastle.crypto.CryptoServicesRegistrar import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey +import org.bouncycastle.jcajce.provider.asymmetric.edec.BCEdDSAPrivateKey +import org.bouncycastle.jcajce.provider.asymmetric.edec.BCEdDSAPublicKey import org.bouncycastle.jcajce.provider.asymmetric.rsa.BCRSAPrivateKey import org.bouncycastle.jcajce.provider.asymmetric.rsa.BCRSAPublicKey import org.bouncycastle.jce.ECNamedCurveTable @@ -308,11 +310,11 @@ object Crypto { fun decodePrivateKey(encodedKey: ByteArray): PrivateKey { val keyInfo = PrivateKeyInfo.getInstance(encodedKey) if (keyInfo.privateKeyAlgorithm.algorithm == ASN1ObjectIdentifier(CordaOID.ALIAS_PRIVATE_KEY)) { - return decodeAliasPrivateKey(keyInfo) + return convertIfBCEdDSAPrivateKey(decodeAliasPrivateKey(keyInfo)) } val signatureScheme = findSignatureScheme(keyInfo.privateKeyAlgorithm) val keyFactory = keyFactory(signatureScheme) - return keyFactory.generatePrivate(PKCS8EncodedKeySpec(encodedKey)) + return convertIfBCEdDSAPrivateKey(keyFactory.generatePrivate(PKCS8EncodedKeySpec(encodedKey))) } @DeleteForDJVM @@ -354,7 +356,7 @@ object Crypto { } try { val keyFactory = keyFactory(signatureScheme) - return keyFactory.generatePrivate(PKCS8EncodedKeySpec(encodedKey)) + return convertIfBCEdDSAPrivateKey(keyFactory.generatePrivate(PKCS8EncodedKeySpec(encodedKey))) } catch (ikse: InvalidKeySpecException) { throw InvalidKeySpecException("This private key cannot be decoded, please ensure it is PKCS8 encoded and that " + "it corresponds to the input scheme's code name.", ikse) @@ -373,7 +375,7 @@ object Crypto { val subjectPublicKeyInfo = SubjectPublicKeyInfo.getInstance(encodedKey) val signatureScheme = findSignatureScheme(subjectPublicKeyInfo.algorithm) val keyFactory = keyFactory(signatureScheme) - return keyFactory.generatePublic(X509EncodedKeySpec(encodedKey)) + return convertIfBCEdDSAPublicKey(keyFactory.generatePublic(X509EncodedKeySpec(encodedKey))) } /** @@ -408,7 +410,7 @@ object Crypto { } try { val keyFactory = keyFactory(signatureScheme) - return keyFactory.generatePublic(X509EncodedKeySpec(encodedKey)) + return convertIfBCEdDSAPublicKey(keyFactory.generatePublic(X509EncodedKeySpec(encodedKey))) } catch (ikse: InvalidKeySpecException) { throw InvalidKeySpecException("This public key cannot be decoded, please ensure it is X509 encoded and " + "that it corresponds to the input scheme's code name.", ikse) @@ -988,6 +990,20 @@ object Crypto { } } + private fun convertIfBCEdDSAPublicKey(key: PublicKey): PublicKey { + return when (key) { + is BCEdDSAPublicKey -> EdDSAPublicKey(X509EncodedKeySpec(key.encoded)) + else -> key + } + } + + private fun convertIfBCEdDSAPrivateKey(key: PrivateKey): PrivateKey { + return when (key) { + is BCEdDSAPrivateKey -> EdDSAPrivateKey(PKCS8EncodedKeySpec(key.encoded)) + else -> key + } + } + /** * Convert a public key to a supported implementation. * @param key a public key. @@ -1015,6 +1031,7 @@ object Crypto { is BCSphincs256PublicKey -> key is EdDSAPublicKey -> key is CompositeKey -> key + is BCEdDSAPublicKey -> convertIfBCEdDSAPublicKey(key) else -> decodePublicKey(key.encoded) } } @@ -1035,6 +1052,7 @@ object Crypto { is BCRSAPrivateKey -> key is BCSphincs256PrivateKey -> key is EdDSAPrivateKey -> key + is BCEdDSAPrivateKey -> convertIfBCEdDSAPrivateKey(key) else -> decodePrivateKey(key.encoded) } } diff --git a/gradle.properties b/gradle.properties index eec02efd03..d70b133fa3 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ kotlin.incremental=true -org.gradle.jvmargs=-XX:+UseG1GC -Xmx1g -Dfile.encoding=UTF-8 +org.gradle.jvmargs=-XX:+UseG1GC -Xmx4g -Dfile.encoding=UTF-8 org.gradle.caching=false owasp.failOnError=false owasp.failBuildOnCVSS=11.0 diff --git a/node-api-tests/src/test/kotlin/net/corda/nodeapitests/internal/crypto/X509UtilitiesTest.kt b/node-api-tests/src/test/kotlin/net/corda/nodeapitests/internal/crypto/X509UtilitiesTest.kt index a5b1aab4ee..61f792a27a 100644 --- a/node-api-tests/src/test/kotlin/net/corda/nodeapitests/internal/crypto/X509UtilitiesTest.kt +++ b/node-api-tests/src/test/kotlin/net/corda/nodeapitests/internal/crypto/X509UtilitiesTest.kt @@ -50,10 +50,13 @@ import net.corda.nodeapi.internal.crypto.loadOrCreateKeyStore import net.corda.nodeapi.internal.crypto.save import net.corda.nodeapi.internal.crypto.toBc import net.corda.nodeapi.internal.crypto.x509 +import net.corda.testing.internal.IS_OPENJ9 import net.i2p.crypto.eddsa.EdDSAPrivateKey import org.assertj.core.api.Assertions.assertThat import org.bouncycastle.asn1.x509.* +import org.bouncycastle.jcajce.provider.asymmetric.edec.BCEdDSAPrivateKey import org.bouncycastle.pqc.jcajce.provider.sphincs.BCSphincs256PrivateKey +import org.junit.Assume import org.junit.Rule import org.junit.Test import org.junit.rules.TemporaryFolder @@ -374,6 +377,7 @@ class X509UtilitiesTest { @Test(timeout=300_000) fun `create server cert and use in OpenSSL channel`() { + Assume.assumeTrue(!IS_OPENJ9) val sslConfig = CertificateStoreStubs.P2P.withCertificatesDirectory(tempFolder.root.toPath(), keyStorePassword = "serverstorepass") val (rootCa, intermediateCa) = createDevIntermediateCaCertPath() @@ -446,7 +450,9 @@ class X509UtilitiesTest { private fun getCorrectKeyFromKeystore(signatureScheme: SignatureScheme, uncastedClass: Class, castedClass: Class) { val keyPair = generateKeyPair(signatureScheme) val (keyFromKeystore, keyFromKeystoreCasted) = storeAndGetKeysFromKeystore(keyPair) - assertThat(keyFromKeystore).isInstanceOf(uncastedClass) + if (uncastedClass == EdDSAPrivateKey::class.java && keyFromKeystore !is BCEdDSAPrivateKey) { + assertThat(keyFromKeystore).isInstanceOf(uncastedClass) + } assertThat(keyFromKeystoreCasted).isInstanceOf(castedClass) } diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/KeyStoreConfigHelpers.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/KeyStoreConfigHelpers.kt index 76bf478b31..7dc49d24df 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/KeyStoreConfigHelpers.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/KeyStoreConfigHelpers.kt @@ -77,6 +77,16 @@ fun createDevNetworkMapCa(rootCa: CertificateAndKeyPair = DEV_ROOT_CA): Certific return CertificateAndKeyPair(cert, keyPair) } +fun createDevNetworkParametersCa(rootCa: CertificateAndKeyPair = DEV_ROOT_CA): CertificateAndKeyPair { + val keyPair = generateKeyPair() + val cert = X509Utilities.createCertificate( + CertificateType.NETWORK_PARAMETERS, + rootCa.certificate, + rootCa.keyPair, + X500Principal("CN=Network Parameters,O=R3 Ltd,L=London,C=GB"), + keyPair.public) + return CertificateAndKeyPair(cert, keyPair) +} /** * Create a dev node CA cert, as a sub-cert of the given [intermediateCa], and matching key pair using the given * [CordaX500Name] as the cert subject. diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/kryo/Kryo.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/kryo/Kryo.kt index d3dc46d203..346a22a207 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/kryo/Kryo.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/kryo/Kryo.kt @@ -463,12 +463,18 @@ fun Kryo.serializationContext(): SerializeAsTokenContext? = context.get(serializ class ThrowableSerializer(kryo: Kryo, type: Class) : Serializer(false, true) { private companion object { + private val IS_OPENJ9 = System.getProperty("java.vm.name").toLowerCase().contains("openj9") private val suppressedField = Throwable::class.java.getDeclaredField("suppressedExceptions") private val sentinelValue = let { - val sentinelField = Throwable::class.java.getDeclaredField("SUPPRESSED_SENTINEL") - sentinelField.isAccessible = true - sentinelField.get(null) + if (!IS_OPENJ9) { + val sentinelField = Throwable::class.java.getDeclaredField("SUPPRESSED_SENTINEL") + sentinelField.isAccessible = true + sentinelField.get(null) + } + else { + Collections.EMPTY_LIST + } } init { diff --git a/node/src/integration-test/kotlin/net/corda/node/flows/FlowEntityManagerTest.kt b/node/src/integration-test/kotlin/net/corda/node/flows/FlowEntityManagerTest.kt index e6be1b1804..fac7f780c5 100644 --- a/node/src/integration-test/kotlin/net/corda/node/flows/FlowEntityManagerTest.kt +++ b/node/src/integration-test/kotlin/net/corda/node/flows/FlowEntityManagerTest.kt @@ -30,7 +30,9 @@ import net.corda.testing.core.DummyCommandData import net.corda.testing.core.singleIdentity import net.corda.testing.driver.DriverParameters import net.corda.testing.driver.driver +import org.apache.commons.lang3.SystemUtils import org.hibernate.exception.ConstraintViolationException +import org.junit.Assume import org.junit.Before import org.junit.Test import java.lang.RuntimeException @@ -315,6 +317,8 @@ class FlowEntityManagerTest : AbstractFlowEntityManagerTest() { @Test(timeout = 300_000) fun `constraint violation that is caught inside an entity manager should allow a flow to continue processing as normal`() { + // This test is generating JDK11 contract code on JDK11 + Assume.assumeTrue(!SystemUtils.IS_JAVA_11) var counter = 0 StaffedFlowHospital.onFlowDischarged.add { _, _ -> ++counter } driver(DriverParameters(startNodesInProcess = true)) { diff --git a/node/src/integration-test/kotlin/net/corda/node/persistence/H2SecurityTests.kt b/node/src/integration-test/kotlin/net/corda/node/persistence/H2SecurityTests.kt index ba9e9cb1c5..0a8d8159d8 100644 --- a/node/src/integration-test/kotlin/net/corda/node/persistence/H2SecurityTests.kt +++ b/node/src/integration-test/kotlin/net/corda/node/persistence/H2SecurityTests.kt @@ -50,7 +50,8 @@ class H2SecurityTests { inMemoryDB = false, startNodesInProcess = false, notarySpecs = emptyList(), - cordappsForAllNodes = emptyList() + cordappsForAllNodes = emptyList(), + premigrateH2Database = false )) { val port = getFreePort() startNode(customOverrides = mapOf(h2AddressKey to "localhost:$port", dbPasswordKey to "x")).getOrThrow() @@ -71,7 +72,8 @@ class H2SecurityTests { inMemoryDB = false, startNodesInProcess = false, notarySpecs = emptyList(), - cordappsForAllNodes = listOf(enclosedCordapp()) + cordappsForAllNodes = listOf(enclosedCordapp()), + premigrateH2Database = false )) { val port = getFreePort() val nodeHandle = startNode(rpcUsers = listOf(user), customOverrides = mapOf(h2AddressKey to "localhost:$port", diff --git a/node/src/integration-test/kotlin/net/corda/node/services/NonDeterministicContractVerifyTest.kt b/node/src/integration-test/kotlin/net/corda/node/services/NonDeterministicContractVerifyTest.kt index bf69f0f66b..264502e448 100644 --- a/node/src/integration-test/kotlin/net/corda/node/services/NonDeterministicContractVerifyTest.kt +++ b/node/src/integration-test/kotlin/net/corda/node/services/NonDeterministicContractVerifyTest.kt @@ -62,7 +62,7 @@ class NonDeterministicContractVerifyTest { .returnValue.getOrThrow() } assertThat(ex) - .hasMessageMatching("^NoSuchMethodError: .*\\Qsandbox.java.time.Instant.now()\\E.*\$") + .hasMessageMatching("^NoSuchMethodError: .*[\\Qsandbox.java.time.Instant.now()\\E|\\Qsandbox.java/time/Instant/now()\\E].*\$") } } @@ -101,7 +101,7 @@ class NonDeterministicContractVerifyTest { .returnValue.getOrThrow() } assertThat(ex) - .hasMessageMatching("^NoSuchMethodError: .*\\Qsandbox.java.util.UUID.randomUUID()\\E.*\$") + .hasMessageMatching("^NoSuchMethodError: .*[\\Qsandbox.java.util.UUID.randomUUID()\\E|\\Qsandbox/java/util/UUID/randomUUID()\\E].*\$") } } diff --git a/node/src/integration-test/kotlin/net/corda/node/services/statemachine/LargeTransactionsTest.kt b/node/src/integration-test/kotlin/net/corda/node/services/statemachine/LargeTransactionsTest.kt index 6d23503efb..9e27f15d76 100644 --- a/node/src/integration-test/kotlin/net/corda/node/services/statemachine/LargeTransactionsTest.kt +++ b/node/src/integration-test/kotlin/net/corda/node/services/statemachine/LargeTransactionsTest.kt @@ -82,7 +82,8 @@ class LargeTransactionsTest { driver(DriverParameters( startNodesInProcess = true, cordappsForAllNodes = listOf(DUMMY_CONTRACTS_CORDAPP, enclosedCordapp()), - networkParameters = testNetworkParameters(maxMessageSize = 15.MB.toInt(), maxTransactionSize = 13.MB.toInt()) + networkParameters = testNetworkParameters(maxMessageSize = 15.MB.toInt(), maxTransactionSize = 13.MB.toInt()), + premigrateH2Database = false )) { val rpcUser = User("admin", "admin", setOf("ALL")) val (alice, _) = listOf(ALICE_NAME, BOB_NAME).map { startNode(providedName = it, rpcUsers = listOf(rpcUser)) }.transpose().getOrThrow() diff --git a/node/src/main/kotlin/net/corda/node/internal/DBNetworkParametersStorage.kt b/node/src/main/kotlin/net/corda/node/internal/DBNetworkParametersStorage.kt index 637afabca3..f93b579ace 100644 --- a/node/src/main/kotlin/net/corda/node/internal/DBNetworkParametersStorage.kt +++ b/node/src/main/kotlin/net/corda/node/internal/DBNetworkParametersStorage.kt @@ -19,7 +19,6 @@ import net.corda.node.utilities.AppendOnlyPersistentMap import net.corda.nodeapi.internal.crypto.X509CertificateFactory import net.corda.nodeapi.internal.crypto.X509Utilities import net.corda.nodeapi.internal.network.SignedNetworkParameters -import net.corda.nodeapi.internal.network.verifiedNetworkMapCert import net.corda.nodeapi.internal.network.verifiedNetworkParametersCert import net.corda.nodeapi.internal.persistence.CordaPersistence import net.corda.nodeapi.internal.persistence.NODE_DATABASE_PREFIX @@ -86,7 +85,7 @@ class DBNetworkParametersStorage( override fun saveParameters(signedNetworkParameters: SignedNetworkParameters) { log.trace { "Saving new network parameters to network parameters storage." } - val networkParameters = signedNetworkParameters.verifiedNetworkMapCert(trustRoot) + val networkParameters = signedNetworkParameters.verifiedNetworkParametersCert(trustRoot) val hash = signedNetworkParameters.raw.hash log.trace { "Parameters to save $networkParameters with hash $hash" } database.transaction { 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 bb2fce1a58..4098576b03 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 @@ -348,7 +348,7 @@ class JarScanningCordappLoader private constructor(private val cordappJarPaths: val cordappElement = cordappJarPath.url.toString() logger.info("Scanning CorDapp in $cordappElement") val scanResult = ClassGraph() - .filterClasspathElements { elt -> elt == cordappElement } + .filterClasspathElementsByURL { elt -> elt == cordappJarPath.url } .overrideClassLoaders(appClassLoader) .ignoreParentClassLoaders() .enableAllInfo() diff --git a/node/src/test/kotlin/net/corda/node/messaging/TwoPartyTradeFlowTests.kt b/node/src/test/kotlin/net/corda/node/messaging/TwoPartyTradeFlowTests.kt index 6c8ce1d7bd..0c49ee44ac 100644 --- a/node/src/test/kotlin/net/corda/node/messaging/TwoPartyTradeFlowTests.kt +++ b/node/src/test/kotlin/net/corda/node/messaging/TwoPartyTradeFlowTests.kt @@ -42,12 +42,14 @@ import net.corda.testing.core.* import net.corda.testing.dsl.LedgerDSL import net.corda.testing.dsl.TestLedgerDSLInterpreter import net.corda.testing.dsl.TestTransactionDSLInterpreter +import net.corda.testing.internal.IS_OPENJ9 import net.corda.testing.internal.LogHelper import net.corda.testing.internal.vault.VaultFiller import net.corda.testing.node.internal.* import net.corda.testing.node.ledger import org.assertj.core.api.Assertions.assertThat import org.junit.After +import org.junit.Assume import org.junit.Before import org.junit.Test import org.junit.runner.RunWith @@ -211,6 +213,7 @@ class TwoPartyTradeFlowTests(private val anonymous: Boolean) { @Test(timeout=300_000) fun `shutdown and restore`() { + Assume.assumeTrue(!IS_OPENJ9) mockNet = InternalMockNetwork(cordappsForAllNodes = listOf(FINANCE_CONTRACTS_CORDAPP)) val notaryNode = mockNet.defaultNotaryNode val notary = mockNet.defaultNotaryIdentity diff --git a/node/src/test/kotlin/net/corda/node/services/network/NetworkParametersReaderTest.kt b/node/src/test/kotlin/net/corda/node/services/network/NetworkParametersReaderTest.kt index f44331d296..b2c04d5806 100644 --- a/node/src/test/kotlin/net/corda/node/services/network/NetworkParametersReaderTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/network/NetworkParametersReaderTest.kt @@ -2,16 +2,23 @@ package net.corda.node.services.network import com.google.common.jimfs.Configuration import com.google.common.jimfs.Jimfs +import net.corda.core.identity.CordaX500Name import net.corda.core.internal.* import net.corda.core.serialization.deserialize import net.corda.core.utilities.days import net.corda.core.utilities.seconds +import net.corda.coretesting.internal.DEV_INTERMEDIATE_CA import net.corda.node.VersionInfo import net.corda.node.internal.NetworkParametersReader import net.corda.nodeapi.internal.network.* import net.corda.testing.common.internal.testNetworkParameters import net.corda.testing.core.SerializationEnvironmentRule import net.corda.coretesting.internal.DEV_ROOT_CA +import net.corda.nodeapi.internal.createDevNetworkMapCa +import net.corda.nodeapi.internal.createDevNetworkParametersCa +import net.corda.nodeapi.internal.createDevNodeCa +import net.corda.nodeapi.internal.crypto.CertificateAndKeyPair +import net.corda.testing.core.TestIdentity import net.corda.testing.node.internal.network.NetworkMapServer import org.assertj.core.api.Assertions.assertThat import org.junit.After @@ -21,6 +28,7 @@ import org.junit.Test import java.net.URL import java.nio.file.FileSystem import kotlin.test.assertEquals +import kotlin.test.assertFailsWith import kotlin.test.assertFalse import kotlin.test.assertNotNull @@ -84,4 +92,23 @@ class NetworkParametersReaderTest { val parameters = inByteArray.deserialize() assertThat(parameters.verified().eventHorizon).isEqualTo(Int.MAX_VALUE.days) } + + @Test(timeout = 300_000) + fun `verifying works with NETWORK_PARAMETERS role and NETWORK_MAP role, but fails for NODE_CA role`() { + val netParameters = testNetworkParameters(epoch = 1) + val certKeyPairNetworkParameters: CertificateAndKeyPair = createDevNetworkParametersCa() + val netParamsForNetworkParameters= certKeyPairNetworkParameters.sign(netParameters) + netParamsForNetworkParameters.verifiedNetworkParametersCert(DEV_ROOT_CA.certificate) + + val certKeyPairNetworkMap: CertificateAndKeyPair = createDevNetworkMapCa() + val netParamsForNetworkMap = certKeyPairNetworkMap.sign(netParameters) + netParamsForNetworkMap.verifiedNetworkParametersCert(DEV_ROOT_CA.certificate) + + val megaCorp = TestIdentity(CordaX500Name("MegaCorp", "London", "GB")) + val x = createDevNodeCa(DEV_INTERMEDIATE_CA, megaCorp.name) + val netParamsForNode = x.sign(netParameters) + assertFailsWith(IllegalArgumentException::class, "Incorrect cert role: NODE_CA") { + netParamsForNode.verifiedNetworkParametersCert(DEV_ROOT_CA.certificate) + } + } } \ No newline at end of file diff --git a/node/src/test/kotlin/net/corda/node/services/statemachine/FlowFrameworkTests.kt b/node/src/test/kotlin/net/corda/node/services/statemachine/FlowFrameworkTests.kt index 6a50dcdb42..8aabbdbe82 100644 --- a/node/src/test/kotlin/net/corda/node/services/statemachine/FlowFrameworkTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/statemachine/FlowFrameworkTests.kt @@ -54,6 +54,7 @@ import net.corda.testing.core.BOB_NAME import net.corda.testing.core.dummyCommand import net.corda.testing.core.singleIdentity import net.corda.testing.flows.registerCordappFlowFactory +import net.corda.testing.internal.IS_OPENJ9 import net.corda.testing.internal.LogHelper import net.corda.testing.node.InMemoryMessagingNetwork.MessageTransfer import net.corda.testing.node.InMemoryMessagingNetwork.ServicePeerAllocationStrategy.RoundRobin @@ -74,6 +75,7 @@ import org.junit.Assert.assertEquals import org.junit.Assert.assertNotEquals import org.junit.Assert.assertNotNull import org.junit.Assert.assertNull +import org.junit.Assume import org.junit.Before import org.junit.Test import rx.Notification @@ -82,6 +84,7 @@ import java.sql.SQLTransientConnectionException import java.time.Clock import java.time.Duration import java.time.Instant +import java.time.temporal.ChronoUnit import java.util.UUID import java.util.concurrent.TimeoutException import java.util.function.Predicate @@ -384,6 +387,7 @@ class FlowFrameworkTests { @Test(timeout = 300_000) fun `Flow metadata finish time is set in database when the flow finishes`() { + Assume.assumeTrue(!IS_OPENJ9) val terminationSignal = Semaphore(0) val clientId = UUID.randomUUID().toString() val flow = aliceNode.services.startFlowWithClientId(clientId, NoOpFlow(terminateUponSignal = terminationSignal)) @@ -397,7 +401,7 @@ class FlowFrameworkTests { aliceNode.database.transaction { val metadata = session.find(DBCheckpointStorage.DBFlowMetadata::class.java, flow.id.uuid.toString()) assertNotNull(metadata.finishInstant) - assertTrue(metadata.finishInstant!! >= metadata.startInstant) + assertTrue(metadata.finishInstant!!.truncatedTo(ChronoUnit.MILLIS) >= metadata.startInstant.truncatedTo(ChronoUnit.MILLIS)) } } diff --git a/node/src/test/kotlin/net/corda/node/services/statemachine/FlowMetadataRecordingTest.kt b/node/src/test/kotlin/net/corda/node/services/statemachine/FlowMetadataRecordingTest.kt index 97fa69f6a5..63b1cfa2ce 100644 --- a/node/src/test/kotlin/net/corda/node/services/statemachine/FlowMetadataRecordingTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/statemachine/FlowMetadataRecordingTest.kt @@ -48,6 +48,7 @@ import org.junit.Before import org.junit.Ignore import org.junit.Test import java.time.Instant +import java.time.temporal.ChronoUnit import java.util.UUID import java.util.concurrent.CompletableFuture import java.util.concurrent.Executors @@ -118,7 +119,8 @@ class FlowMetadataRecordingTest { assertThat(it.launchingCordapp).contains("custom-cordapp") assertEquals(PLATFORM_VERSION, it.platformVersion) assertEquals(user.username, it.startedBy) - assertEquals(context!!.trace.invocationId.timestamp, it.invocationInstant) + assertEquals(context!!.trace.invocationId.timestamp.truncatedTo((ChronoUnit.MILLIS)), + it.invocationInstant.truncatedTo(ChronoUnit.MILLIS)) assertTrue(it.startInstant >= it.invocationInstant) assertNull(it.finishInstant) } @@ -159,7 +161,8 @@ class FlowMetadataRecordingTest { assertThat(it.launchingCordapp).contains("custom-cordapp") assertEquals(PLATFORM_VERSION, it.platformVersion) assertEquals(user.username, it.startedBy) - assertEquals(context!!.trace.invocationId.timestamp, it.invocationInstant) + assertEquals(context!!.trace.invocationId.timestamp.truncatedTo(ChronoUnit.MILLIS), + it.invocationInstant.truncatedTo(ChronoUnit.MILLIS)) assertTrue(it.startInstant >= it.invocationInstant) assertNull(it.finishInstant) } @@ -261,7 +264,8 @@ class FlowMetadataRecordingTest { assertThat(it.launchingCordapp).contains("custom-cordapp") assertEquals(8, it.platformVersion) assertEquals(nodeAHandle.nodeInfo.singleIdentity().name.toString(), it.startedBy) - assertEquals(context!!.trace.invocationId.timestamp, it.invocationInstant) + assertEquals(context!!.trace.invocationId.timestamp.truncatedTo(ChronoUnit.MILLIS), + it.invocationInstant.truncatedTo(ChronoUnit.MILLIS)) assertTrue(it.startInstant >= it.invocationInstant) assertNull(it.finishInstant) } @@ -309,7 +313,8 @@ class FlowMetadataRecordingTest { assertThat(it.launchingCordapp).contains("custom-cordapp") assertEquals(PLATFORM_VERSION, it.platformVersion) assertEquals(MyService::class.java.name, it.startedBy) - assertEquals(context!!.trace.invocationId.timestamp, it.invocationInstant) + assertEquals(context!!.trace.invocationId.timestamp.truncatedTo(ChronoUnit.MILLIS), + it.invocationInstant.truncatedTo(ChronoUnit.MILLIS)) assertTrue(it.startInstant >= it.invocationInstant) assertNull(it.finishInstant) } @@ -364,7 +369,8 @@ class FlowMetadataRecordingTest { assertThat(it.launchingCordapp).contains("custom-cordapp") assertEquals(PLATFORM_VERSION, it.platformVersion) assertEquals("Scheduler", it.startedBy) - assertEquals(context!!.trace.invocationId.timestamp, it.invocationInstant) + assertEquals(context!!.trace.invocationId.timestamp.truncatedTo(ChronoUnit.MILLIS), + it.invocationInstant.truncatedTo(ChronoUnit.MILLIS)) assertTrue(it.startInstant >= it.invocationInstant) assertNull(it.finishInstant) } diff --git a/node/src/test/kotlin/net/corda/node/services/statemachine/RetryFlowMockTest.kt b/node/src/test/kotlin/net/corda/node/services/statemachine/RetryFlowMockTest.kt index aca41ff4e2..3d4ddfab46 100644 --- a/node/src/test/kotlin/net/corda/node/services/statemachine/RetryFlowMockTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/statemachine/RetryFlowMockTest.kt @@ -23,6 +23,7 @@ import net.corda.node.services.messaging.Message import net.corda.node.services.persistence.DBTransactionStorage import net.corda.nodeapi.internal.persistence.contextTransaction import net.corda.testing.core.TestIdentity +import net.corda.testing.internal.IS_OPENJ9 import net.corda.testing.node.internal.InternalMockNetwork import net.corda.testing.node.internal.MessagingServiceSpy import net.corda.testing.node.internal.TestStartedNode @@ -33,6 +34,7 @@ import org.assertj.core.api.Assertions.assertThatExceptionOfType import org.h2.util.Utils import org.junit.After import org.junit.Assert.assertTrue +import org.junit.Assume import org.junit.Before import org.junit.Test import java.sql.SQLException @@ -129,6 +131,7 @@ class RetryFlowMockTest { @Test(timeout=300_000) fun `Early end session message does not hang receiving flow`() { + Assume.assumeTrue(!IS_OPENJ9) val partyB = nodeB.info.legalIdentities.first() assertThatExceptionOfType(UnexpectedFlowEndException::class.java).isThrownBy { nodeA.startFlow(UnbalancedSendAndReceiveFlow(partyB)).getOrThrow(20.seconds) diff --git a/node/src/test/kotlin/net/corda/node/services/vault/NodeVaultServiceTest.kt b/node/src/test/kotlin/net/corda/node/services/vault/NodeVaultServiceTest.kt index f2f141cac5..682af85bd8 100644 --- a/node/src/test/kotlin/net/corda/node/services/vault/NodeVaultServiceTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/vault/NodeVaultServiceTest.kt @@ -35,6 +35,7 @@ import net.corda.testing.common.internal.testNetworkParameters import net.corda.testing.contracts.DummyContract import net.corda.testing.contracts.DummyState import net.corda.testing.core.* +import net.corda.testing.internal.IS_OPENJ9 import net.corda.testing.internal.LogHelper import net.corda.testing.internal.vault.* import net.corda.testing.node.MockServices @@ -468,6 +469,7 @@ class NodeVaultServiceTest { @Test(timeout=300_000) fun `unconsumedStatesForSpending from two issuer parties`() { + Assume.assumeTrue(!IS_OPENJ9) // openj9 OOM issue database.transaction { vaultFiller.fillWithSomeTestCash(100.DOLLARS, issuerServices, 1, DUMMY_CASH_ISSUER) vaultFiller.fillWithSomeTestCash(100.DOLLARS, bocServices, 1, BOC.ref(1)) diff --git a/node/src/test/kotlin/net/corda/node/services/vault/VaultQueryJoinTest.kt b/node/src/test/kotlin/net/corda/node/services/vault/VaultQueryJoinTest.kt index 59d246d3a8..4cca24dd3c 100644 --- a/node/src/test/kotlin/net/corda/node/services/vault/VaultQueryJoinTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/vault/VaultQueryJoinTest.kt @@ -25,15 +25,20 @@ import net.corda.testing.node.MockNetwork import net.corda.testing.node.MockNetworkParameters import net.corda.testing.node.StartedMockNode import net.corda.testing.node.internal.cordappsForPackages +import org.apache.commons.lang3.SystemUtils import org.junit.AfterClass +import org.junit.Assume import org.junit.BeforeClass import org.junit.Test +import org.junit.jupiter.api.condition.DisabledOnJre +import org.junit.jupiter.api.condition.JRE import javax.persistence.Column import javax.persistence.Entity import javax.persistence.Index import javax.persistence.Table import kotlin.test.assertEquals +@DisabledOnJre(JRE.JAVA_11) class VaultQueryJoinTest { companion object { private var mockNetwork: MockNetwork? = null @@ -46,6 +51,7 @@ class VaultQueryJoinTest { @BeforeClass @JvmStatic fun setup() { + Assume.assumeTrue(!SystemUtils.IS_JAVA_11) mockNetwork = MockNetwork( MockNetworkParameters( cordappsForAllNodes = cordappsForPackages( diff --git a/node/src/test/kotlin/net/corda/node/services/vault/VaultQueryTests.kt b/node/src/test/kotlin/net/corda/node/services/vault/VaultQueryTests.kt index 0eeb7939fe..e07a26b1b2 100644 --- a/node/src/test/kotlin/net/corda/node/services/vault/VaultQueryTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/vault/VaultQueryTests.kt @@ -32,6 +32,7 @@ import net.corda.nodeapi.internal.persistence.CordaPersistence import net.corda.nodeapi.internal.persistence.DatabaseConfig import net.corda.nodeapi.internal.persistence.DatabaseTransaction import net.corda.testing.core.* +import net.corda.testing.internal.IS_OPENJ9 import net.corda.testing.internal.chooseIdentity import net.corda.testing.internal.configureDatabase import net.corda.testing.internal.vault.* @@ -40,6 +41,7 @@ import net.corda.testing.node.MockServices.Companion.makeTestDatabaseAndMockServ import net.corda.testing.node.makeTestIdentityService import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThatCode +import org.junit.Assume import org.junit.ClassRule import org.junit.Ignore import org.junit.Rule @@ -1689,6 +1691,7 @@ abstract class VaultQueryTestsBase : VaultQueryParties { // pagination: invalid page number @Test(timeout=300_000) fun `invalid page number`() { + Assume.assumeTrue(!IS_OPENJ9) // openj9 OOM issue expectedEx.expect(VaultQueryException::class.java) expectedEx.expectMessage("Page specification: invalid page number") @@ -2235,6 +2238,7 @@ abstract class VaultQueryTestsBase : VaultQueryParties { @Test(timeout=300_000) fun `unconsumed fungible states for owners`() { + Assume.assumeTrue(!IS_OPENJ9) // openj9 OOM issue database.transaction { vaultFillerCashNotary.fillWithSomeTestCash(100.DOLLARS, notaryServices, 1, DUMMY_CASH_ISSUER) vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, 1, MEGA_CORP.ref(0), MEGA_CORP) @@ -2289,6 +2293,7 @@ abstract class VaultQueryTestsBase : VaultQueryParties { @Test(timeout=300_000) fun `unconsumed cash balances for all currencies`() { + Assume.assumeTrue(!IS_OPENJ9) // openj9 OOM issue database.transaction { listOf(100.DOLLARS, 200.DOLLARS, 300.POUNDS, 400.POUNDS, 500.SWISS_FRANCS, 600.SWISS_FRANCS).zip(1..6).forEach { (howMuch, states) -> vaultFiller.fillWithSomeTestCash(howMuch, notaryServices, states, DUMMY_CASH_ISSUER) @@ -2471,6 +2476,7 @@ abstract class VaultQueryTestsBase : VaultQueryParties { // specifying Query on Linear state attributes @Test(timeout=300_000) fun `unconsumed linear heads for linearId between two timestamps`() { + Assume.assumeTrue(!IS_OPENJ9) // openj9 OOM issue database.transaction { val start = services.clock.instant() vaultFiller.fillWithSomeTestLinearStates(1, "TEST") @@ -2776,6 +2782,8 @@ abstract class VaultQueryTestsBase : VaultQueryParties { } } + //linus one OOM issue + @Ignore @Test(timeout=300_000) fun `record a transaction with number of inputs greater than vault page size`() { val notary = dummyNotary diff --git a/testing/node-driver/src/integration-test/kotlin/net/corda/testing/node/internal/CordaCliWrapperErrorHandlingTests.kt b/testing/node-driver/src/integration-test/kotlin/net/corda/testing/node/internal/CordaCliWrapperErrorHandlingTests.kt index 091c267c7c..6a4c572349 100644 --- a/testing/node-driver/src/integration-test/kotlin/net/corda/testing/node/internal/CordaCliWrapperErrorHandlingTests.kt +++ b/testing/node-driver/src/integration-test/kotlin/net/corda/testing/node/internal/CordaCliWrapperErrorHandlingTests.kt @@ -1,7 +1,9 @@ package net.corda.testing.node.internal +import net.corda.testing.internal.IS_OPENJ9 import org.hamcrest.MatcherAssert.assertThat import org.hamcrest.Matchers.matchesPattern +import org.junit.Assume import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.Parameterized @@ -17,7 +19,7 @@ class CordaCliWrapperErrorHandlingTests(val arguments: List, val outputR val className = "net.corda.testing.node.internal.SampleCordaCliWrapper" private val stackTraceRegex = "^.+Exception[^\\n]++(\\s+at .++)+[\\s\\S]*" - private val exceptionWithoutStackTraceRegex ="${className}(\\s+.+)" + private val exceptionWithoutStackTraceRegex ="(\\?\\[31m)*\\Q${className}\\E(\\?\\[0m)*(\\s+.+)" private val emptyStringRegex = "^$" @JvmStatic @@ -31,7 +33,8 @@ class CordaCliWrapperErrorHandlingTests(val arguments: List, val outputR @Test(timeout=300_000) fun `Run CordaCliWrapper sample app with arguments and check error output matches regExp`() { - + // For openj9 the process error output appears sometimes to be garbled. + Assume.assumeTrue(!IS_OPENJ9) val process = ProcessUtilities.startJavaProcess( className = className, arguments = arguments, diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/driver/Driver.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/driver/Driver.kt index 87a1333545..cfd06a585b 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/driver/Driver.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/driver/Driver.kt @@ -43,21 +43,28 @@ data class NotaryHandle(val identity: Party, val validating: Boolean, val nodeHa interface NodeHandle : AutoCloseable { /** Get the [NodeInfo] for this node */ val nodeInfo: NodeInfo + /** * Interface to the node's RPC system. The first RPC user will be used to login if are any, otherwise a default one * will be added and that will be used. */ val rpc: CordaRPCOps + /** Get the p2p address for this node **/ val p2pAddress: NetworkHostAndPort + /** Get the rpc address for this node **/ val rpcAddress: NetworkHostAndPort + /** Get the rpc admin address for this node **/ val rpcAdminAddress: NetworkHostAndPort + /** Get the JMX server address for this node, if JMX is enabled **/ val jmxAddress: NetworkHostAndPort? + /** Get a [List] of [User]'s for this node **/ val rpcUsers: List + /** The location of the node's base directory **/ val baseDirectory: Path @@ -67,7 +74,8 @@ interface NodeHandle : AutoCloseable { fun stop() } -fun NodeHandle.logFile(): File = (baseDirectory / "logs").toFile().walk().filter { it.name.startsWith("node-") && it.extension == "log" }.single() +fun NodeHandle.logFile(): File = (baseDirectory / "logs").toFile().walk().filter { it.name.startsWith("node-") && it.extension == "log" } + .single() /** Interface which represents an out of process node and exposes its process handle. **/ @DoNotImplement @@ -91,7 +99,8 @@ interface InProcess : NodeHandle { * Starts an already constructed flow. Note that you must be on the server thread to call this method. * @param context indicates who started the flow, see: [InvocationContext]. */ - fun startFlow(logic: FlowLogic): CordaFuture = internalServices.startFlow(logic, internalServices.newContext()).getOrThrow().resultFuture + fun startFlow(logic: FlowLogic): CordaFuture = internalServices.startFlow(logic, internalServices.newContext()) + .getOrThrow().resultFuture } /** @@ -206,7 +215,8 @@ fun driver(defaultParameters: DriverParameters = DriverParameters(), dsl: Dr djvmBootstrapSource = defaultParameters.djvmBootstrapSource, djvmCordaSource = defaultParameters.djvmCordaSource, environmentVariables = defaultParameters.environmentVariables, - allowHibernateToManageAppSchema = defaultParameters.allowHibernateToManageAppSchema + allowHibernateToManageAppSchema = defaultParameters.allowHibernateToManageAppSchema, + premigrateH2Database = defaultParameters.premigrateH2Database ), coerce = { it }, dsl = dsl @@ -245,6 +255,8 @@ fun driver(defaultParameters: DriverParameters = DriverParameters(), dsl: Dr * @property cordappsForAllNodes [TestCordapp]s that will be added to each node started by the [DriverDSL]. * @property djvmBootstrapSource Location of a JAR containing the Java APIs for the DJVM to use. * @property djvmCordaSource Locations of JARs of user-supplied classes to execute within the DJVM sandbox. + * @property premigrateH2Database Whether to use a prebuilt H2 database schema or start from an empty schema. + * This can save time for tests which do not need to migrate from a blank schema. */ @Suppress("unused") data class DriverParameters( @@ -263,12 +275,13 @@ data class DriverParameters( @Suppress("DEPRECATION") val jmxPolicy: JmxPolicy = JmxPolicy(), val networkParameters: NetworkParameters = testNetworkParameters(notaries = emptyList()), val notaryCustomOverrides: Map = emptyMap(), - val inMemoryDB: Boolean = true, + val inMemoryDB: Boolean = false, val cordappsForAllNodes: Collection? = null, val djvmBootstrapSource: Path? = null, val djvmCordaSource: List = emptyList(), - val environmentVariables : Map = emptyMap(), - val allowHibernateToManageAppSchema: Boolean = true + val environmentVariables: Map = emptyMap(), + val allowHibernateToManageAppSchema: Boolean = true, + val premigrateH2Database: Boolean = true ) { constructor(cordappsForAllNodes: Collection) : this(isDebug = false, cordappsForAllNodes = cordappsForAllNodes) @@ -376,6 +389,49 @@ data class DriverParameters( cordappsForAllNodes = null ) + constructor( + isDebug: Boolean = false, + driverDirectory: Path = Paths.get("build") / "node-driver" / getTimestampAsDirectoryName(), + portAllocation: PortAllocation = incrementalPortAllocation(), + debugPortAllocation: PortAllocation = incrementalPortAllocation(), + systemProperties: Map = emptyMap(), + useTestClock: Boolean = false, + startNodesInProcess: Boolean = false, + waitForAllNodesToFinish: Boolean = false, + notarySpecs: List = listOf(NotarySpec(DUMMY_NOTARY_NAME)), + extraCordappPackagesToScan: List = emptyList(), + @Suppress("DEPRECATION") jmxPolicy: JmxPolicy = JmxPolicy(), + networkParameters: NetworkParameters = testNetworkParameters(notaries = emptyList()), + notaryCustomOverrides: Map = emptyMap(), + inMemoryDB: Boolean = false, + cordappsForAllNodes: Collection? = null, + djvmBootstrapSource: Path? = null, + djvmCordaSource: List = emptyList(), + environmentVariables: Map = emptyMap(), + allowHibernateToManageAppSchema: Boolean = true + ) : this( + isDebug, + driverDirectory, + portAllocation, + debugPortAllocation, + systemProperties, + useTestClock, + startNodesInProcess, + waitForAllNodesToFinish, + notarySpecs, + extraCordappPackagesToScan, + jmxPolicy, + networkParameters, + notaryCustomOverrides, + inMemoryDB, + cordappsForAllNodes, + djvmBootstrapSource, + djvmCordaSource, + environmentVariables, + allowHibernateToManageAppSchema, + premigrateH2Database = true + ) + constructor( isDebug: Boolean, driverDirectory: Path, @@ -417,6 +473,7 @@ data class DriverParameters( fun withStartNodesInProcess(startNodesInProcess: Boolean): DriverParameters = copy(startNodesInProcess = startNodesInProcess) fun withWaitForAllNodesToFinish(waitForAllNodesToFinish: Boolean): DriverParameters = copy(waitForAllNodesToFinish = waitForAllNodesToFinish) fun withNotarySpecs(notarySpecs: List): DriverParameters = copy(notarySpecs = notarySpecs) + @Deprecated("extraCordappPackagesToScan does not preserve the original CorDapp's versioning and metadata, which may lead to " + "misleading results in tests. Use withCordappsForAllNodes instead.") fun withExtraCordappPackagesToScan(extraCordappPackagesToScan: List): DriverParameters = copy(extraCordappPackagesToScan = extraCordappPackagesToScan) @@ -428,7 +485,7 @@ data class DriverParameters( fun withCordappsForAllNodes(cordappsForAllNodes: Collection?): DriverParameters = copy(cordappsForAllNodes = cordappsForAllNodes) fun withDjvmBootstrapSource(djvmBootstrapSource: Path?): DriverParameters = copy(djvmBootstrapSource = djvmBootstrapSource) fun withDjvmCordaSource(djvmCordaSource: List): DriverParameters = copy(djvmCordaSource = djvmCordaSource) - fun withEnvironmentVariables(variables : Map): DriverParameters = copy(environmentVariables = variables) + fun withEnvironmentVariables(variables: Map): DriverParameters = copy(environmentVariables = variables) fun withAllowHibernateToManageAppSchema(value: Boolean): DriverParameters = copy(allowHibernateToManageAppSchema = value) fun copy( @@ -530,4 +587,48 @@ data class DriverParameters( djvmCordaSource = djvmCordaSource, environmentVariables = environmentVariables ) + + // Legacy copy() from v4.5 + @Suppress("LongParameterList") + fun copy(isDebug: Boolean, + driverDirectory: Path, + portAllocation: PortAllocation, + debugPortAllocation: PortAllocation, + systemProperties: Map, + useTestClock: Boolean, + startNodesInProcess: Boolean, + waitForAllNodesToFinish: Boolean, + notarySpecs: List, + extraCordappPackagesToScan: List, + @Suppress("DEPRECATION") jmxPolicy: JmxPolicy, + networkParameters: NetworkParameters, + notaryCustomOverrides: Map, + inMemoryDB: Boolean, + cordappsForAllNodes: Collection?, + djvmBootstrapSource: Path?, + djvmCordaSource: List, + environmentVariables: Map, + allowHibernateToManageAppSchema: Boolean + ) = this.copy( + isDebug = isDebug, + driverDirectory = driverDirectory, + portAllocation = portAllocation, + debugPortAllocation = debugPortAllocation, + systemProperties = systemProperties, + useTestClock = useTestClock, + startNodesInProcess = startNodesInProcess, + waitForAllNodesToFinish = waitForAllNodesToFinish, + notarySpecs = notarySpecs, + extraCordappPackagesToScan = extraCordappPackagesToScan, + jmxPolicy = jmxPolicy, + networkParameters = networkParameters, + notaryCustomOverrides = notaryCustomOverrides, + inMemoryDB = inMemoryDB, + cordappsForAllNodes = cordappsForAllNodes, + djvmBootstrapSource = djvmBootstrapSource, + djvmCordaSource = djvmCordaSource, + environmentVariables = environmentVariables, + allowHibernateToManageAppSchema = allowHibernateToManageAppSchema, + premigrateH2Database = true + ) } \ No newline at end of file diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/DatabaseSnapshot.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/DatabaseSnapshot.kt new file mode 100644 index 0000000000..bc1d7f7e90 --- /dev/null +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/DatabaseSnapshot.kt @@ -0,0 +1,23 @@ +package net.corda.testing.node + +import java.io.InputStream +import java.nio.file.Files +import java.nio.file.Path + +object DatabaseSnapshot { + private const val previousCordaVersion: String = "4.5.1" + private const val databaseName: String = "persistence.mv.db" + + private fun getDatabaseSnapshotStream(): InputStream { + val resourceUri = this::class.java.getResource("/databasesnapshots/${previousCordaVersion}/$databaseName") + return resourceUri.openStream() + } + + fun copyDatabaseSnapshot(baseDirectory: Path) { + getDatabaseSnapshotStream().use { stream -> + Files.createDirectories(baseDirectory) + val path = baseDirectory.resolve(databaseName) + Files.copy(stream, path) + } + } +} \ No newline at end of file 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 5d9751ae5d..625d4f18be 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 @@ -48,6 +48,7 @@ import net.corda.testing.internal.configureDatabase import net.corda.testing.node.internal.* import net.corda.testing.services.MockAttachmentStorage import java.io.ByteArrayOutputStream +import java.nio.file.Paths import java.security.KeyPair import java.sql.Connection import java.time.Clock @@ -99,9 +100,17 @@ open class MockServices private constructor( // TODO: Can we use an X509 principal generator here? @JvmStatic fun makeTestDataSourceProperties(nodeName: String = SecureHash.randomSHA256().toString()): Properties { + val dbDir = Paths.get("","build", "mocknetworktestdb", nodeName) + .toAbsolutePath() + val dbPath = dbDir.resolve("persistence") + try { + DatabaseSnapshot.copyDatabaseSnapshot(dbDir) + } catch (ex: java.nio.file.FileAlreadyExistsException) { + DriverDSLImpl.log.warn("Database already exists on disk, not attempting to pre-migrate database.") + } val props = Properties() props.setProperty("dataSourceClassName", "org.h2.jdbcx.JdbcDataSource") - props.setProperty("dataSource.url", "jdbc:h2:mem:${nodeName}_persistence;LOCK_TIMEOUT=10000;DB_CLOSE_ON_EXIT=FALSE") + props.setProperty("dataSource.url", "jdbc:h2:file:$dbPath;LOCK_TIMEOUT=10000;DB_CLOSE_ON_EXIT=FALSE") props.setProperty("dataSource.user", "sa") props.setProperty("dataSource.password", "") return props @@ -357,7 +366,6 @@ open class MockServices private constructor( constructor(cordappPackages: List, initialIdentityName: CordaX500Name, identityService: IdentityService, networkParameters: NetworkParameters) : this(cordappPackages, TestIdentity(initialIdentityName), identityService, networkParameters) - constructor(cordappPackages: List, initialIdentityName: CordaX500Name, identityService: IdentityService, networkParameters: NetworkParameters, key: KeyPair) : this(cordappPackages, TestIdentity(initialIdentityName, key), identityService, networkParameters) @@ -428,11 +436,12 @@ open class MockServices private constructor( private val mockCordappProvider: MockCordappProvider = MockCordappProvider(cordappLoader, attachments).also { it.start() } - override val transactionVerifierService: TransactionVerifierService get() = InMemoryTransactionVerifierService( - numberOfWorkers = 2, - cordappProvider = mockCordappProvider, - attachments = attachments - ) + override val transactionVerifierService: TransactionVerifierService + get() = InMemoryTransactionVerifierService( + numberOfWorkers = 2, + cordappProvider = mockCordappProvider, + attachments = attachments + ) override val cordappProvider: CordappProvider get() = mockCordappProvider override var networkParametersService: NetworkParametersService = MockNetworkParametersStorage(initialNetworkParameters) override val diagnosticsService: DiagnosticsService = NodeDiagnosticsService() diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/CustomCordapp.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/CustomCordapp.kt index 37590387b3..d461becbfc 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/CustomCordapp.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/CustomCordapp.kt @@ -50,12 +50,12 @@ data class CustomCordapp( @VisibleForTesting internal fun packageAsJar(file: Path) { val classGraph = ClassGraph() - if(packages.isNotEmpty()){ - classGraph.whitelistPaths(*packages.map { it.replace('.', '/') }.toTypedArray()) + if (packages.isNotEmpty()) { + classGraph.acceptPaths(*packages.map { it.replace('.', '/') }.toTypedArray()) } if (classes.isNotEmpty()) { classes.forEach { classGraph.addClassLoader(it.classLoader) } - classGraph.whitelistClasses(*classes.map { it.name }.toTypedArray()) + classGraph.acceptClasses(*classes.map { it.name }.toTypedArray()) } classGraph.enableClassInfo().pooledScan().use { scanResult -> diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/DriverDSLImpl.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/DriverDSLImpl.kt index f508ff2412..f11a717370 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/DriverDSLImpl.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/DriverDSLImpl.kt @@ -1,4 +1,5 @@ @file:Suppress("TooManyFunctions", "Deprecation") + package net.corda.testing.node.internal import co.paralleluniverse.fibers.instrument.JavaAgent @@ -52,6 +53,7 @@ import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.contextLogger import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.millis +import net.corda.core.utilities.toHexString import net.corda.coretesting.internal.stubs.CertificateStoreStubs import net.corda.node.NodeRegistrationOption import net.corda.node.VersionInfo @@ -96,6 +98,7 @@ import net.corda.testing.driver.internal.InProcessImpl import net.corda.testing.driver.internal.NodeHandleInternal import net.corda.testing.driver.internal.OutOfProcessImpl import net.corda.testing.node.ClusterSpec +import net.corda.testing.node.DatabaseSnapshot import net.corda.testing.node.NotarySpec import okhttp3.OkHttpClient import okhttp3.Request @@ -147,8 +150,9 @@ class DriverDSLImpl( val cordappsForAllNodes: Collection?, val djvmBootstrapSource: Path?, val djvmCordaSource: List, - val environmentVariables : Map, - val allowHibernateToManageAppSchema: Boolean = true + val environmentVariables: Map, + val allowHibernateToManageAppSchema: Boolean = true, + val premigrateH2Database: Boolean = true ) : InternalDriverDSL { private var _executorService: ScheduledExecutorService? = null @@ -156,8 +160,10 @@ class DriverDSLImpl( private var _shutdownManager: ShutdownManager? = null override val shutdownManager get() = _shutdownManager!! private lateinit var extraCustomCordapps: Set + // Map from a nodes legal name to an observable emitting the number of nodes in its network map. private val networkVisibilityController = NetworkVisibilityController() + /** * Future which completes when the network map infrastructure is available, whether a local one or one from the CZ. * This future acts as a gate to prevent nodes from starting too early. The value of the future is a [LocalNetworkMap] @@ -192,7 +198,7 @@ class DriverDSLImpl( } private fun NodeConfig.checkAndOverrideForInMemoryDB(): NodeConfig = this.run { - if (inMemoryDB && corda.dataSourceProperties.getProperty("dataSource.url").startsWith("jdbc:h2:")) { + if (inMemoryDB && isH2Database(corda)) { val jdbcUrl = "jdbc:h2:mem:persistence${inMemoryCounter.getAndIncrement()};DB_CLOSE_ON_EXIT=FALSE;LOCK_TIMEOUT=10000;WRITE_DELAY=100" corda.dataSourceProperties.setProperty("dataSource.url", jdbcUrl) NodeConfig(typesafe + mapOf("dataSourceProperties" to mapOf("dataSource.url" to jdbcUrl))) @@ -262,6 +268,15 @@ class DriverDSLImpl( val name = parameters.providedName ?: CordaX500Name("${oneOf(names).organisation}-${p2pAddress.port}", "London", "GB") val config = createConfig(name, parameters, p2pAddress) + if (premigrateH2Database && isH2Database(config)) { + if (!inMemoryDB) { + try { + DatabaseSnapshot.copyDatabaseSnapshot(config.corda.baseDirectory) + } catch (ex: java.nio.file.FileAlreadyExistsException) { + log.warn("Database already exists on disk, not attempting to pre-migrate database.") + } + } + } val registrationFuture = if (compatibilityZone?.rootCert != null) { // We don't need the network map to be available to be able to register the node createSchema(config, false).flatMap { startNodeRegistration(it, compatibilityZone.rootCert, compatibilityZone.config()) } @@ -270,7 +285,7 @@ class DriverDSLImpl( } return registrationFuture.flatMap { conf -> - networkMapAvailability.flatMap {networkMap -> + networkMapAvailability.flatMap { networkMap -> // But starting the node proper does require the network map startRegisteredNode(conf, networkMap, parameters, bytemanPort) } @@ -376,9 +391,9 @@ class DriverDSLImpl( startOutOfProcessMiniNode( config, arrayOf( - "initial-registration", - "--network-root-truststore=${rootTruststorePath.toAbsolutePath()}", - "--network-root-truststore-password=$rootTruststorePassword" + "initial-registration", + "--network-root-truststore=${rootTruststorePath.toAbsolutePath()}", + "--network-root-truststore-password=$rootTruststorePassword" ) ).map { config } } @@ -420,13 +435,13 @@ class DriverDSLImpl( _shutdownManager = ShutdownManager(executorService) val callerPackage = getCallerPackage().toMutableList() - if(callerPackage.firstOrNull()?.startsWith("net.corda.node") == true) callerPackage.add("net.corda.testing") + if (callerPackage.firstOrNull()?.startsWith("net.corda.node") == true) callerPackage.add("net.corda.testing") extraCustomCordapps = cordappsForPackages(extraCordappPackagesToScan + callerPackage) val notaryInfosFuture = if (compatibilityZone == null) { // If no CZ is specified then the driver does the generation of the network parameters and the copying of the // node info files. - startNotaryIdentityGeneration().map { notaryInfos -> Pair(notaryInfos, LocalNetworkMap(notaryInfos.map{it.second})) } + startNotaryIdentityGeneration().map { notaryInfos -> Pair(notaryInfos, LocalNetworkMap(notaryInfos.map { it.second })) } } else { // Otherwise it's the CZ's job to distribute thse via the HTTP network map, as that is what the nodes will be expecting. val notaryInfosFuture = if (compatibilityZone.rootCert == null) { @@ -437,7 +452,7 @@ class DriverDSLImpl( startAllNotaryRegistrations(compatibilityZone.rootCert, compatibilityZone) } notaryInfosFuture.map { notaryInfos -> - compatibilityZone.publishNotaries(notaryInfos.map{it.second}) + compatibilityZone.publishNotaries(notaryInfos.map { it.second }) Pair(notaryInfos, null) } } @@ -445,14 +460,15 @@ class DriverDSLImpl( networkMapAvailability = notaryInfosFuture.map { it.second } _notaries = notaryInfosFuture.map { (notaryInfos, localNetworkMap) -> - val listOfFutureNodeHandles = startNotaries(notaryInfos.map{it.first}, localNetworkMap, notaryCustomOverrides) + val listOfFutureNodeHandles = startNotaries(notaryInfos.map { it.first }, localNetworkMap, notaryCustomOverrides) notaryInfos.zip(listOfFutureNodeHandles) { (_, notaryInfo), nodeHandlesFuture -> NotaryHandle(notaryInfo.identity, notaryInfo.validating, nodeHandlesFuture) } } try { - _notaries.map { notary -> notary.map { handle -> handle.nodeHandles } }.getOrThrow(notaryHandleTimeout).forEach { future -> future.getOrThrow(notaryHandleTimeout) } - } catch(e: NodeListenProcessDeathException) { + _notaries.map { notary -> notary.map { handle -> handle.nodeHandles } }.getOrThrow(notaryHandleTimeout) + .forEach { future -> future.getOrThrow(notaryHandleTimeout) } + } catch (e: NodeListenProcessDeathException) { val message = if (e.causeFromStdError.isNotBlank()) { "Unable to start notaries. Failed with the following error: ${e.causeFromStdError}" } else { @@ -487,7 +503,7 @@ class DriverDSLImpl( } } - private fun startNotaryIdentityGeneration(): CordaFuture>> { + private fun startNotaryIdentityGeneration(): CordaFuture>> { return executorService.fork { notarySpecs.map { spec -> val notaryConfig = mapOf("notary" to mapOf("validating" to spec.validating)) @@ -537,32 +553,32 @@ class DriverDSLImpl( spec: NotarySpec, rootCert: X509Certificate, compatibilityZone: CompatibilityZoneParams - ): CordaFuture> { + ): CordaFuture> { val parameters = NodeParameters(rpcUsers = spec.rpcUsers, verifierType = spec.verifierType, customOverrides = notaryCustomOverrides, maximumHeapSize = spec.maximumHeapSize) return createSchema(createConfig(spec.name, parameters), false).flatMap { config -> - startNodeRegistration(config, rootCert, compatibilityZone.config())}.flatMap { config -> - // Node registration only gives us the node CA cert, not the identity cert. That is only created on first - // startup or when the node is told to just generate its node info file. We do that here. - if (startNodesInProcess) { - executorService.fork { - val nodeInfo = Node(config.corda, MOCK_VERSION_INFO, initialiseSerialization = false).generateAndSaveNodeInfo() - Pair(config.withNotaryDefinition(spec.validating), NotaryInfo(nodeInfo.legalIdentities[0], spec.validating)) - } - } else { - // TODO The config we use here is uses a hardocded p2p port which changes when the node is run proper - // This causes two node info files to be generated. - startOutOfProcessMiniNode(config, arrayOf("generate-node-info")).map { - // Once done we have to read the signed node info file that's been generated - val nodeInfoFile = config.corda.baseDirectory.list { paths -> - paths.filter { it.fileName.toString().startsWith(NodeInfoFilesCopier.NODE_INFO_FILE_NAME_PREFIX) }.findFirst() - .get() - } - val nodeInfo = nodeInfoFile.readObject().verified() - Pair(config.withNotaryDefinition(spec.validating), NotaryInfo(nodeInfo.legalIdentities[0], spec.validating)) + startNodeRegistration(config, rootCert, compatibilityZone.config()) + }.flatMap { config -> + // Node registration only gives us the node CA cert, not the identity cert. That is only created on first + // startup or when the node is told to just generate its node info file. We do that here. + if (startNodesInProcess) { + executorService.fork { + val nodeInfo = Node(config.corda, MOCK_VERSION_INFO, initialiseSerialization = false).generateAndSaveNodeInfo() + Pair(config.withNotaryDefinition(spec.validating), NotaryInfo(nodeInfo.legalIdentities[0], spec.validating)) + } + } else { + // TODO The config we use here is uses a hardocded p2p port which changes when the node is run proper + // This causes two node info files to be generated. + startOutOfProcessMiniNode(config, arrayOf("generate-node-info")).map { + // Once done we have to read the signed node info file that's been generated + val nodeInfoFile = config.corda.baseDirectory.list { paths -> + paths.filter { it.fileName.toString().startsWith(NodeInfoFilesCopier.NODE_INFO_FILE_NAME_PREFIX) }.findFirst() + .get() } + val nodeInfo = nodeInfoFile.readObject().verified() + Pair(config.withNotaryDefinition(spec.validating), NotaryInfo(nodeInfo.legalIdentities[0], spec.validating)) } } - + } } private fun generateNodeNames(spec: NotarySpec): List { @@ -571,7 +587,7 @@ class DriverDSLImpl( private fun startNotaries(configs: List, localNetworkMap: LocalNetworkMap?, customOverrides: Map): List>> { return notarySpecs.zip(configs).map { (spec, config) -> - when (spec.cluster) { + when (spec.cluster) { null -> startSingleNotary(config, spec, localNetworkMap, customOverrides) is ClusterSpec.Raft, // DummyCluster is used for testing the notary communication path, and it does not matter @@ -585,13 +601,13 @@ class DriverDSLImpl( private fun startSingleNotary(config: NodeConfig, spec: NotarySpec, localNetworkMap: LocalNetworkMap?, customOverrides: Map): CordaFuture> { val notaryConfig = mapOf("notary" to mapOf("validating" to spec.validating)) return startRegisteredNode( - config, - localNetworkMap, - NodeParameters(rpcUsers = spec.rpcUsers, - verifierType = spec.verifierType, - startInSameProcess = spec.startInProcess, - customOverrides = notaryConfig + customOverrides, - maximumHeapSize = spec.maximumHeapSize) + config, + localNetworkMap, + NodeParameters(rpcUsers = spec.rpcUsers, + verifierType = spec.verifierType, + startInSameProcess = spec.startInProcess, + customOverrides = notaryConfig + customOverrides, + maximumHeapSize = spec.maximumHeapSize) ).map { listOf(it) } } @@ -758,14 +774,14 @@ class DriverDSLImpl( } val effectiveP2PAddress = config.corda.messagingServerAddress ?: config.corda.p2pAddress val p2pReadyFuture = nodeMustBeStartedFuture( - executorService, - config.corda.baseDirectory / "net.corda.node.Corda.${identifier}.stdout.log", - process + executorService, + config.corda.baseDirectory / "net.corda.node.Corda.${identifier}.stdout.log", + process ) { NodeListenProcessDeathException( - effectiveP2PAddress, - process, - (config.corda.baseDirectory / "net.corda.node.Corda.$identifier.stderr.log").readText() + effectiveP2PAddress, + process, + (config.corda.baseDirectory / "net.corda.node.Corda.$identifier.stderr.log").readText() ) } @@ -804,6 +820,7 @@ class DriverDSLImpl( */ inner class LocalNetworkMap(notaryInfos: List) { val networkParametersCopier = NetworkParametersCopier(networkParameters.copy(notaries = notaryInfos)) + // TODO: this object will copy NodeInfo files from started nodes to other nodes additional-node-infos/ // This uses the FileSystem and adds a delay (~5 seconds) given by the time we wait before polling the file system. // Investigate whether we can avoid that. @@ -832,6 +849,7 @@ class DriverDSLImpl( private val notaryHandleTimeout = Duration.ofMinutes(1) private val defaultRpcUserList = listOf(InternalUser("default", "default", setOf("ALL")).toConfig().root().unwrapped()) private val names = arrayOf(ALICE_NAME, BOB_NAME, DUMMY_BANK_A_NAME) + /** * A sub-set of permissions that grant most of the essential operations used in the unit/integration tests as well as * in demo application like NodeExplorer. @@ -856,14 +874,14 @@ class DriverDSLImpl( private const val CORDA_TESTING_ATTRIBUTE = "Corda-Testing" private val CORDAPP_MANIFEST_ATTRIBUTES: List = unmodifiableList(listOf( - CORDAPP_CONTRACT_NAME, - CORDAPP_CONTRACT_LICENCE, - CORDAPP_CONTRACT_VENDOR, - CORDAPP_CONTRACT_VERSION, - CORDAPP_WORKFLOW_NAME, - CORDAPP_WORKFLOW_LICENCE, - CORDAPP_WORKFLOW_VENDOR, - CORDAPP_WORKFLOW_VERSION + CORDAPP_CONTRACT_NAME, + CORDAPP_CONTRACT_LICENCE, + CORDAPP_CONTRACT_VENDOR, + CORDAPP_CONTRACT_VERSION, + CORDAPP_WORKFLOW_NAME, + CORDAPP_WORKFLOW_LICENCE, + CORDAPP_WORKFLOW_VENDOR, + CORDAPP_WORKFLOW_VERSION )) /** @@ -938,7 +956,7 @@ class DriverDSLImpl( maximumHeapSize: String, logLevelOverride: String?, identifier: String, - environmentVariables : Map, + environmentVariables: Map, extraCmdLineFlag: Array = emptyArray() ): Process { log.info("Starting out-of-process Node ${config.corda.myLegalName.organisation}, " + @@ -979,7 +997,6 @@ class DriverDSLImpl( else -> "DEBUG" } - val arguments = mutableListOf( "--base-directory=${config.corda.baseDirectory}", "--logging-level=$loggingLevel", @@ -1003,11 +1020,11 @@ class DriverDSLImpl( // The following dependencies are excluded from the classpath of the created JVM, // so that the environment resembles a real one as close as possible. - val cp = ProcessUtilities.defaultClassPath.filter { cpEntry -> + val cp = ProcessUtilities.defaultClassPath.filter { cpEntry -> val cpPathEntry = Paths.get(cpEntry) cpPathEntry.isRegularFile() - && !isTestArtifact(cpPathEntry.fileName.toString()) - && !cpPathEntry.isExcludedJar + && !isTestArtifact(cpPathEntry.fileName.toString()) + && !cpPathEntry.isExcludedJar } return ProcessUtilities.startJavaProcess( @@ -1023,6 +1040,12 @@ class DriverDSLImpl( ) } + private fun isH2Database(config: NodeConfiguration) + = config.dataSourceProperties.getProperty("dataSource.url").startsWith("jdbc:h2:") + + private fun isH2Database(config: NodeConfig) + = isH2Database(config.corda) + // Obvious test artifacts. This is NOT intended to be an exhaustive list! // It is only intended to remove those FEW jars which BLATANTLY do not // belong inside a Corda Node. @@ -1046,12 +1069,13 @@ class DriverDSLImpl( || (manifest[TARGET_PLATFORM_VERSION] != null && manifest[MIN_PLATFORM_VERSION] != null) } - private val Path.isExcludedJar: Boolean get() { - return JarInputStream(Files.newInputStream(this).buffered()).use { jar -> - val manifest = jar.manifest ?: return false - isCordapp(manifest) || isTestArtifact(manifest) + private val Path.isExcludedJar: Boolean + get() { + return JarInputStream(Files.newInputStream(this).buffered()).use { jar -> + val manifest = jar.manifest ?: return false + isCordapp(manifest) || isTestArtifact(manifest) + } } - } private fun startWebserver(handle: NodeHandleInternal, debugPort: Int?, maximumHeapSize: String): Process { val className = "net.corda.webserver.WebServer" @@ -1220,8 +1244,8 @@ interface InternalDriverDSL : DriverDSL { fun shutdown() fun startNode( - parameters: NodeParameters = NodeParameters(), - bytemanPort: Int? = null + parameters: NodeParameters = NodeParameters(), + bytemanPort: Int? = null ): CordaFuture } @@ -1268,28 +1292,29 @@ fun genericDriver( ): A { setDriverSerialization().use { _ -> val driverDsl = driverDslWrapper( - DriverDSLImpl( - portAllocation = defaultParameters.portAllocation, - debugPortAllocation = defaultParameters.debugPortAllocation, - systemProperties = defaultParameters.systemProperties, - driverDirectory = defaultParameters.driverDirectory.toAbsolutePath(), - useTestClock = defaultParameters.useTestClock, - isDebug = defaultParameters.isDebug, - startNodesInProcess = defaultParameters.startNodesInProcess, - waitForAllNodesToFinish = defaultParameters.waitForAllNodesToFinish, - extraCordappPackagesToScan = @Suppress("DEPRECATION") defaultParameters.extraCordappPackagesToScan, - jmxPolicy = defaultParameters.jmxPolicy, - notarySpecs = defaultParameters.notarySpecs, - compatibilityZone = null, - networkParameters = defaultParameters.networkParameters, - notaryCustomOverrides = defaultParameters.notaryCustomOverrides, - inMemoryDB = defaultParameters.inMemoryDB, - cordappsForAllNodes = uncheckedCast(defaultParameters.cordappsForAllNodes), - djvmBootstrapSource = defaultParameters.djvmBootstrapSource, - djvmCordaSource = defaultParameters.djvmCordaSource, - environmentVariables = defaultParameters.environmentVariables, - allowHibernateToManageAppSchema = defaultParameters.allowHibernateToManageAppSchema - ) + DriverDSLImpl( + portAllocation = defaultParameters.portAllocation, + debugPortAllocation = defaultParameters.debugPortAllocation, + systemProperties = defaultParameters.systemProperties, + driverDirectory = defaultParameters.driverDirectory.toAbsolutePath(), + useTestClock = defaultParameters.useTestClock, + isDebug = defaultParameters.isDebug, + startNodesInProcess = defaultParameters.startNodesInProcess, + waitForAllNodesToFinish = defaultParameters.waitForAllNodesToFinish, + extraCordappPackagesToScan = @Suppress("DEPRECATION") defaultParameters.extraCordappPackagesToScan, + jmxPolicy = defaultParameters.jmxPolicy, + notarySpecs = defaultParameters.notarySpecs, + compatibilityZone = null, + networkParameters = defaultParameters.networkParameters, + notaryCustomOverrides = defaultParameters.notaryCustomOverrides, + inMemoryDB = defaultParameters.inMemoryDB, + cordappsForAllNodes = uncheckedCast(defaultParameters.cordappsForAllNodes), + djvmBootstrapSource = defaultParameters.djvmBootstrapSource, + djvmCordaSource = defaultParameters.djvmCordaSource, + environmentVariables = defaultParameters.environmentVariables, + allowHibernateToManageAppSchema = defaultParameters.allowHibernateToManageAppSchema, + premigrateH2Database = defaultParameters.premigrateH2Database + ) ) val shutdownHook = addShutdownHook(driverDsl::shutdown) try { @@ -1324,7 +1349,7 @@ sealed class CompatibilityZoneParams( ) { abstract fun networkMapURL(): URL abstract fun doormanURL(): URL - abstract fun config() : NetworkServicesConfig + abstract fun config(): NetworkServicesConfig } /** @@ -1332,18 +1357,18 @@ sealed class CompatibilityZoneParams( */ class SharedCompatibilityZoneParams( private val url: URL, - private val pnm : UUID?, + private val pnm: UUID?, publishNotaries: (List) -> Unit, rootCert: X509Certificate? = null ) : CompatibilityZoneParams(publishNotaries, rootCert) { - val config : NetworkServicesConfig by lazy { + val config: NetworkServicesConfig by lazy { NetworkServicesConfig(url, url, pnm, false) } override fun doormanURL() = url override fun networkMapURL() = url - override fun config() : NetworkServicesConfig = config + override fun config(): NetworkServicesConfig = config } /** @@ -1352,17 +1377,17 @@ class SharedCompatibilityZoneParams( class SplitCompatibilityZoneParams( private val doormanURL: URL, private val networkMapURL: URL, - private val pnm : UUID?, + private val pnm: UUID?, publishNotaries: (List) -> Unit, rootCert: X509Certificate? = null ) : CompatibilityZoneParams(publishNotaries, rootCert) { - val config : NetworkServicesConfig by lazy { + val config: NetworkServicesConfig by lazy { NetworkServicesConfig(doormanURL, networkMapURL, pnm, false) } override fun doormanURL() = doormanURL override fun networkMapURL() = networkMapURL - override fun config() : NetworkServicesConfig = config + override fun config(): NetworkServicesConfig = config } @Suppress("LongParameterList") @@ -1387,6 +1412,7 @@ fun internalDriver( djvmCordaSource: List = emptyList(), environmentVariables: Map = emptyMap(), allowHibernateToManageAppSchema: Boolean = true, + premigrateH2Database: Boolean = true, dsl: DriverDSLImpl.() -> A ): A { return genericDriver( @@ -1410,15 +1436,21 @@ fun internalDriver( djvmBootstrapSource = djvmBootstrapSource, djvmCordaSource = djvmCordaSource, environmentVariables = environmentVariables, - allowHibernateToManageAppSchema = allowHibernateToManageAppSchema + allowHibernateToManageAppSchema = allowHibernateToManageAppSchema, + premigrateH2Database = premigrateH2Database ), coerce = { it }, dsl = dsl ) } +val DIRECTORY_TIMESTAMP_FORMAT: DateTimeFormatter = DateTimeFormatter.ofPattern("yyyyMMdd-HHmmss.SSS").withZone(UTC) +private val directoryRandom = Random() fun getTimestampAsDirectoryName(): String { - return DateTimeFormatter.ofPattern("yyyyMMdd-HHmmss.SSS").withZone(UTC).format(Instant.now()) + val base = DIRECTORY_TIMESTAMP_FORMAT.format(Instant.now()) + // Introduce some randomness so starting two nodes in the same ms doesn't use the same path + val random = directoryRandom.nextLong().toBigInteger().toByteArray().toHexString() + return "$base-$random" } fun writeConfig(path: Path, filename: String, config: Config) { diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/MockNetworkParametersService.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/MockNetworkParametersService.kt index 8f906b58cd..2155343d27 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/MockNetworkParametersService.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/MockNetworkParametersService.kt @@ -9,7 +9,7 @@ import net.corda.core.node.NetworkParameters import net.corda.core.node.NotaryInfo import net.corda.core.serialization.serialize import net.corda.nodeapi.internal.network.SignedNetworkParameters -import net.corda.nodeapi.internal.network.verifiedNetworkMapCert +import net.corda.nodeapi.internal.network.verifiedNetworkParametersCert import net.corda.testing.common.internal.testNetworkParameters import net.corda.testing.core.ALICE_NAME import net.corda.testing.core.TestIdentity @@ -30,7 +30,7 @@ class MockNetworkParametersStorage(private var currentParameters: NetworkParamet } override fun setCurrentParameters(currentSignedParameters: SignedDataWithCert, trustRoot: X509Certificate) { - setCurrentParametersUnverified(currentSignedParameters.verifiedNetworkMapCert(trustRoot)) + setCurrentParametersUnverified(currentSignedParameters.verifiedNetworkParametersCert(trustRoot)) } override fun lookupSigned(hash: SecureHash): SignedDataWithCert? { diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/TestCordappImpl.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/TestCordappImpl.kt index 40cc21ec93..68b37b5247 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/TestCordappImpl.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/TestCordappImpl.kt @@ -56,7 +56,7 @@ data class TestCordappImpl(val scanPackage: String, override val config: Map { return packageToRootPaths.computeIfAbsent(scanPackage) { - val classGraph = ClassGraph().whitelistPaths(scanPackage.replace('.', '/')) + val classGraph = ClassGraph().acceptPaths(scanPackage.replace('.', '/')) classGraph.pooledScan().use { scanResult -> scanResult.allResources .asSequence() diff --git a/testing/node-driver/src/main/resources/databasesnapshots/4.5.1/persistence.mv.db b/testing/node-driver/src/main/resources/databasesnapshots/4.5.1/persistence.mv.db new file mode 100644 index 0000000000..d5f6cdd09f Binary files /dev/null and b/testing/node-driver/src/main/resources/databasesnapshots/4.5.1/persistence.mv.db differ diff --git a/testing/test-utils/src/main/kotlin/net/corda/testing/internal/InternalTestUtils.kt b/testing/test-utils/src/main/kotlin/net/corda/testing/internal/InternalTestUtils.kt index 8e37ceaeba..813125bcf7 100644 --- a/testing/test-utils/src/main/kotlin/net/corda/testing/internal/InternalTestUtils.kt +++ b/testing/test-utils/src/main/kotlin/net/corda/testing/internal/InternalTestUtils.kt @@ -252,4 +252,6 @@ fun isLocalPortBound(port: Int): Boolean { // Failed to open server socket means that it is already bound by someone true } -} \ No newline at end of file +} + +val IS_OPENJ9 = System.getProperty("java.vm.name").toLowerCase().contains("openj9") \ No newline at end of file diff --git a/tools/shell/src/test/java/net/corda/tools/shell/InteractiveShellJavaTest.java b/tools/shell/src/test/java/net/corda/tools/shell/InteractiveShellJavaTest.java index 7304eaac6b..7eb54681c2 100644 --- a/tools/shell/src/test/java/net/corda/tools/shell/InteractiveShellJavaTest.java +++ b/tools/shell/src/test/java/net/corda/tools/shell/InteractiveShellJavaTest.java @@ -38,6 +38,8 @@ public class InteractiveShellJavaTest { // should guarantee that FlowA will have synthetic method to access this field private static final String synthetic = "synth"; + private static final boolean IS_OPENJ9 = System.getProperty("java.vm.name").toLowerCase().contains("openj9"); + abstract static class StringFlow extends FlowLogic { abstract String getA(); } @@ -183,8 +185,10 @@ public class InteractiveShellJavaTest { @Test public void flowStartSimple() throws InteractiveShell.NoApplicableConstructor { check("a: Hi there", "Hi there", FlowA.class); - check("b: 12", "12", FlowA.class); - check("b: 12, c: Yo", "12Yo", FlowA.class); + if (!IS_OPENJ9) { + check("b: 12", "12", FlowA.class); + check("b: 12, c: Yo", "12Yo", FlowA.class); + } } @Test @@ -210,11 +214,13 @@ public class InteractiveShellJavaTest { @Test public void flowStartWithArrayType() throws InteractiveShell.NoApplicableConstructor { - check( - "b: [ One, Two, Three, Four ]", - "One+Two+Three+Four", - FlowA.class - ); + if (!IS_OPENJ9) { + check( + "b: [ One, Two, Three, Four ]", + "One+Two+Three+Four", + FlowA.class + ); + } } @Test