Merge pull request #6254 from corda/adel/merge-from-4.5

Adel/merge from 4.5
This commit is contained in:
Rick Parker 2020-05-15 15:38:46 +01:00 committed by GitHub
commit ac03ba63e8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
28 changed files with 177 additions and 77 deletions

View File

@ -1,13 +1,9 @@
@Library('corda-shared-build-pipeline-steps')
import static com.r3.build.BuildControl.killAllExistingBuildsForJob
killAllExistingBuildsForJob(env.JOB_NAME, env.BUILD_NUMBER.toInteger())
pipeline { pipeline {
agent { label 'k8s' } agent { label 'k8s' }
options { options {
timestamps() timestamps()
buildDiscarder(logRotator(daysToKeepStr: '7', artifactDaysToKeepStr: '7')) buildDiscarder(logRotator(daysToKeepStr: '7', artifactDaysToKeepStr: '7'))
disableConcurrentBuilds()
timeout(time: 3, unit: 'HOURS') timeout(time: 3, unit: 'HOURS')
} }

View File

@ -152,6 +152,7 @@ buildscript {
"unknown" "unknown"
} }
}() }()
ext.corda_docs_link = "https://docs.corda.net/docs/corda-os/$baseVersion"
repositories { repositories {
mavenLocal() mavenLocal()
mavenCentral() mavenCentral()
@ -309,6 +310,7 @@ allprojects {
attributes('Corda-Revision': corda_revision) attributes('Corda-Revision': corda_revision)
attributes('Corda-Vendor': 'Corda Open Source') attributes('Corda-Vendor': 'Corda Open Source')
attributes('Automatic-Module-Name': "net.corda.${task.project.name.replaceAll('-', '.')}") attributes('Automatic-Module-Name': "net.corda.${task.project.name.replaceAll('-', '.')}")
attributes('Corda-Docs-Link': corda_docs_link)
} }
} }

View File

@ -13,6 +13,7 @@ class CordaVersion {
val revision: String by lazy { manifestValue("Corda-Revision") ?: UNKNOWN } val revision: String by lazy { manifestValue("Corda-Revision") ?: UNKNOWN }
val vendor: String by lazy { manifestValue("Corda-Vendor") ?: UNKNOWN } val vendor: String by lazy { manifestValue("Corda-Vendor") ?: UNKNOWN }
val platformVersion: Int by lazy { manifestValue("Corda-Platform-Version")?.toInt() ?: 1 } val platformVersion: Int by lazy { manifestValue("Corda-Platform-Version")?.toInt() ?: 1 }
val docsLink: String by lazy { manifestValue("Corda-Docs-Link") ?: UNKNOWN }
internal val semanticVersion: String by lazy { if(releaseVersion == UNKNOWN) CURRENT_MAJOR_RELEASE else releaseVersion } internal val semanticVersion: String by lazy { if(releaseVersion == UNKNOWN) CURRENT_MAJOR_RELEASE else releaseVersion }
} }

View File

@ -11,21 +11,10 @@ import java.util.*
class CordaErrorContextProvider : ErrorContextProvider { class CordaErrorContextProvider : ErrorContextProvider {
companion object { companion object {
private const val BASE_URL = "https://docs.corda.net/docs"
private const val OS_PAGES = "corda-os"
private const val ENTERPRISE_PAGES = "corda-enterprise"
private const val ERROR_CODE_PAGE = "error-codes.html" private const val ERROR_CODE_PAGE = "error-codes.html"
} }
override fun getURL(locale: Locale): String { override fun getURL(locale: Locale): String {
val versionNumber = CordaVersion.releaseVersion return "${CordaVersion.docsLink}/$ERROR_CODE_PAGE"
// This slightly strange block here allows the code to be merged across to Enterprise with no changes.
val productVersion = if (CordaVersion.platformEditionCode == "OS") {
OS_PAGES
} else {
ENTERPRISE_PAGES
}
return "$BASE_URL/$productVersion/$versionNumber/$ERROR_CODE_PAGE"
} }
} }

View File

@ -25,7 +25,7 @@ internal class ErrorReporterImpl(private val resourceLocation: String,
val resource = "$resourceLocation/$ERROR_INFO_RESOURCE" val resource = "$resourceLocation/$ERROR_INFO_RESOURCE"
val codeMessage = fetchAndFormat(resource, ERROR_CODE_MESSAGE, arrayOf(error.formatCode())) val codeMessage = fetchAndFormat(resource, ERROR_CODE_MESSAGE, arrayOf(error.formatCode()))
val urlMessage = fetchAndFormat(resource, ERROR_CODE_URL, arrayOf(errorContextProvider.getURL(locale))) val urlMessage = fetchAndFormat(resource, ERROR_CODE_URL, arrayOf(errorContextProvider.getURL(locale)))
return "[$codeMessage, $urlMessage]" return "[$codeMessage $urlMessage]"
} }
override fun report(error: ErrorCode<*>, logger: Logger) { override fun report(error: ErrorCode<*>, logger: Logger) {

View File

@ -11,7 +11,8 @@ class CordaErrorContextProviderTest {
@Test(timeout = 300_000) @Test(timeout = 300_000)
fun `check that correct URL is returned from context provider`() { fun `check that correct URL is returned from context provider`() {
val context = CordaErrorContextProvider() val context = CordaErrorContextProvider()
val expectedURL = "https://docs.corda.net/docs/corda-os/${CordaVersion.releaseVersion}/error-codes.html" val version = CordaVersion.releaseVersion.substringBefore("-") // Remove SNAPSHOT if present
val expectedURL = "https://docs.corda.net/docs/corda-os/$version/error-codes.html"
// In this first release, there is only one localisation and the URL structure for future localisations is currently unknown. As // In this first release, there is only one localisation and the URL structure for future localisations is currently unknown. As
// a result, the same URL is expected for all locales. // a result, the same URL is expected for all locales.
assertEquals(expectedURL, context.getURL(Locale.getDefault())) assertEquals(expectedURL, context.getURL(Locale.getDefault()))

View File

@ -74,7 +74,7 @@ class ErrorReporterImplTest {
val error = TEST_ERROR_1 val error = TEST_ERROR_1
val testReporter = createReporterImpl("en-US") val testReporter = createReporterImpl("en-US")
testReporter.report(error, loggerMock) testReporter.report(error, loggerMock)
assertEquals(listOf("This is a test message [Code: test-case1, URL: $TEST_URL/en-US]"), logs) assertEquals(listOf("This is a test message [Code: test-case1 URL: $TEST_URL/en-US]"), logs)
} }
@Test(timeout = 300_00) @Test(timeout = 300_00)
@ -84,7 +84,7 @@ class ErrorReporterImplTest {
val testReporter = createReporterImpl("en-US") val testReporter = createReporterImpl("en-US")
testReporter.report(error, loggerMock) testReporter.report(error, loggerMock)
val format = DateFormat.getDateInstance(DateFormat.LONG, Locale.forLanguageTag("en-US")) val format = DateFormat.getDateInstance(DateFormat.LONG, Locale.forLanguageTag("en-US"))
assertEquals(listOf("This is the second case with string foo, number 1, date ${format.format(currentDate)} [Code: test-case2, URL: $TEST_URL/en-US]"), logs) assertEquals(listOf("This is the second case with string foo, number 1, date ${format.format(currentDate)} [Code: test-case2 URL: $TEST_URL/en-US]"), logs)
} }
@Test(timeout = 300_000) @Test(timeout = 300_000)
@ -92,7 +92,7 @@ class ErrorReporterImplTest {
val error = TEST_ERROR_1 val error = TEST_ERROR_1
val testReporter = createReporterImpl("fr-FR") val testReporter = createReporterImpl("fr-FR")
testReporter.report(error, loggerMock) testReporter.report(error, loggerMock)
assertEquals(listOf("This is a test message [Code: test-case1, URL: $TEST_URL/fr-FR]"), logs) assertEquals(listOf("This is a test message [Code: test-case1 URL: $TEST_URL/fr-FR]"), logs)
} }
@Test(timeout = 300_000) @Test(timeout = 300_000)
@ -100,7 +100,7 @@ class ErrorReporterImplTest {
val error = TEST_ERROR_1 val error = TEST_ERROR_1
val testReporter = createReporterImpl("ga-IE") val testReporter = createReporterImpl("ga-IE")
testReporter.report(error, loggerMock) testReporter.report(error, loggerMock)
assertEquals(listOf("Is teachtaireacht earráide é seo [Code: test-case1, URL: $TEST_URL/ga-IE]"), logs) assertEquals(listOf("Is teachtaireacht earráide é seo [Code: test-case1 URL: $TEST_URL/ga-IE]"), logs)
} }
@Test(timeout = 300_000) @Test(timeout = 300_000)
@ -108,7 +108,7 @@ class ErrorReporterImplTest {
val error = TEST_ERROR_1 val error = TEST_ERROR_1
val testReporter = createReporterImpl("es-ES") val testReporter = createReporterImpl("es-ES")
testReporter.report(error, loggerMock) testReporter.report(error, loggerMock)
assertEquals(listOf("This is a test message [Code: test-case1, URL: $TEST_URL/es-ES]"), logs) assertEquals(listOf("This is a test message [Code: test-case1 URL: $TEST_URL/es-ES]"), logs)
} }
@Test(timeout = 300_000) @Test(timeout = 300_000)
@ -116,6 +116,6 @@ class ErrorReporterImplTest {
val error = TEST_ERROR_3 val error = TEST_ERROR_3
val testReporter = createReporterImpl("en-US") val testReporter = createReporterImpl("en-US")
testReporter.report(error, loggerMock) testReporter.report(error, loggerMock)
assertEquals(listOf("This is the third test message [Code: test-case-3, URL: $TEST_URL/en-US]"), logs) assertEquals(listOf("This is the third test message [Code: test-case-3 URL: $TEST_URL/en-US]"), logs)
} }
} }

View File

@ -21,7 +21,7 @@ quasarVersion11=0.8.0_r3
jdkClassifier11=jdk11 jdkClassifier11=jdk11
proguardVersion=6.1.1 proguardVersion=6.1.1
bouncycastleVersion=1.60 bouncycastleVersion=1.60
classgraphVersion=4.8.71 classgraphVersion=4.8.78
disruptorVersion=3.4.2 disruptorVersion=3.4.2
typesafeConfigVersion=1.3.4 typesafeConfigVersion=1.3.4
jsr305Version=3.0.2 jsr305Version=3.0.2

View File

@ -12,7 +12,6 @@ import org.bouncycastle.asn1.x509.GeneralSubtree
import org.bouncycastle.asn1.x509.NameConstraints import org.bouncycastle.asn1.x509.NameConstraints
import org.bouncycastle.jce.provider.BouncyCastleProvider import org.bouncycastle.jce.provider.BouncyCastleProvider
import org.junit.Test import org.junit.Test
import java.security.Security
import java.security.UnrecoverableKeyException import java.security.UnrecoverableKeyException
import java.security.cert.CertPathValidator import java.security.cert.CertPathValidator
import java.security.cert.CertPathValidatorException import java.security.cert.CertPathValidatorException
@ -95,7 +94,8 @@ class X509NameConstraintsTest {
@Test(timeout=300_000) @Test(timeout=300_000)
fun `x500 name with correct cn and extra attribute`() { fun `x500 name with correct cn and extra attribute`() {
Security.addProvider(BouncyCastleProvider()) // Do not use Security.addProvider(BouncyCastleProvider()) to avoid EdDSA signature disruption in other tests.
Crypto.findProvider(BouncyCastleProvider.PROVIDER_NAME)
val acceptableNames = listOf("CN=Bank A TLS, UID=", "O=Bank A") val acceptableNames = listOf("CN=Bank A TLS, UID=", "O=Bank A")
.map { GeneralSubtree(GeneralName(X500Name(it))) }.toTypedArray() .map { GeneralSubtree(GeneralName(X500Name(it))) }.toTypedArray()

View File

@ -229,8 +229,6 @@ abstract class TransactionVerificationException(val txId: SecureHash, message: S
""" """
State of class ${state.data ::class.java.typeName} belongs to contract $requiredContractClassName, but State of class ${state.data ::class.java.typeName} belongs to contract $requiredContractClassName, but
is bundled in TransactionState with ${state.contract}. is bundled in TransactionState with ${state.contract}.
For details see: https://docs.corda.net/api-contract-constraints.html#contract-state-agreement
""".trimIndent().replace('\n', ' ')) """.trimIndent().replace('\n', ' '))
} }
@ -243,8 +241,6 @@ abstract class TransactionVerificationException(val txId: SecureHash, message: S
State of class ${state.data::class.java.typeName} does not have a specified owning contract. State of class ${state.data::class.java.typeName} does not have a specified owning contract.
Add the @BelongsToContract annotation to this class to ensure that it can only be bundled in a TransactionState Add the @BelongsToContract annotation to this class to ensure that it can only be bundled in a TransactionState
with the correct contract. with the correct contract.
For details see: https://docs.corda.net/api-contract-constraints.html#contract-state-agreement
""".trimIndent()) """.trimIndent())
} }
@ -331,8 +327,7 @@ abstract class TransactionVerificationException(val txId: SecureHash, message: S
*/ */
class PackageOwnershipException(txId: SecureHash, @Suppress("unused") val attachmentHash: AttachmentId, @Suppress("unused") val invalidClassName: String, val packageName: String) : TransactionVerificationException(txId, class PackageOwnershipException(txId: SecureHash, @Suppress("unused") val attachmentHash: AttachmentId, @Suppress("unused") val invalidClassName: String, val packageName: String) : TransactionVerificationException(txId,
"""The attachment JAR: $attachmentHash containing the class: $invalidClassName is not signed by the owner of package $packageName specified in the network parameters. """The attachment JAR: $attachmentHash containing the class: $invalidClassName is not signed by the owner of package $packageName specified in the network parameters.
Please check the source of this attachment and if it is malicious contact your zone operator to report this incident. Please check the source of this attachment and if it is malicious contact your zone operator to report this incident.""".trimIndent(), null)
For details see: https://docs.corda.net/network-map.html#network-parameters""".trimIndent(), null)
class InvalidAttachmentException(txId: SecureHash, @Suppress("unused") val attachmentHash: AttachmentId) : TransactionVerificationException(txId, class InvalidAttachmentException(txId: SecureHash, @Suppress("unused") val attachmentHash: AttachmentId) : TransactionVerificationException(txId,
"The attachment $attachmentHash is not a valid ZIP or JAR file.".trimIndent(), null) "The attachment $attachmentHash is not a valid ZIP or JAR file.".trimIndent(), null)
@ -345,8 +340,7 @@ abstract class TransactionVerificationException(val txId: SecureHash, message: S
class UntrustedAttachmentsException(val txId: SecureHash, val ids: List<SecureHash>) : class UntrustedAttachmentsException(val txId: SecureHash, val ids: List<SecureHash>) :
CordaException("Attempting to load untrusted transaction attachments: $ids. " + CordaException("Attempting to load untrusted transaction attachments: $ids. " +
"At this time these are not loadable because the DJVM sandbox has not yet been integrated. " + "At this time these are not loadable because the DJVM sandbox has not yet been integrated. " +
"You will need to manually install the CorDapp to whitelist it for use. " + "You will need to manually install the CorDapp to whitelist it for use.")
"Please follow the operational steps outlined in https://docs.corda.net/cordapp-build-systems.html#cordapp-contract-attachments to learn more and continue.")
/* /*
If you add a new class extending [TransactionVerificationException], please add a test in `TransactionVerificationExceptionSerializationTests` If you add a new class extending [TransactionVerificationException], please add a test in `TransactionVerificationExceptionSerializationTests`

View File

@ -52,8 +52,6 @@ object JarSignatureCollector {
""" """
Mismatch between signers ${firstSignerSet.toOrderedPublicKeys()} for file $firstFile Mismatch between signers ${firstSignerSet.toOrderedPublicKeys()} for file $firstFile
and signers ${otherSignerSet.toOrderedPublicKeys()} for file ${otherFile}. and signers ${otherSignerSet.toOrderedPublicKeys()} for file ${otherFile}.
See https://docs.corda.net/api-contract-constraints.html#signature-constraints for details of the
constraints applied to attachment signatures.
""".trimIndent().replace('\n', ' ')) """.trimIndent().replace('\n', ' '))
} }
return firstSignerSet return firstSignerSet

View File

@ -265,8 +265,6 @@ abstract class Verifier(val ltx: LedgerTransaction, protected val transactionCla
logger.warnOnce(""" logger.warnOnce("""
State of class ${state.data::class.java.typeName} belongs to contract $requiredContractClassName, but State of class ${state.data::class.java.typeName} belongs to contract $requiredContractClassName, but
is bundled in TransactionState with ${state.contract}. is bundled in TransactionState with ${state.contract}.
For details see: https://docs.corda.net/api-contract-constraints.html#contract-state-agreement
""".trimIndent().replace('\n', ' ')) """.trimIndent().replace('\n', ' '))
} }
} }

View File

@ -33,8 +33,6 @@ object StateContractValidationEnforcementRule {
Unable to determine JAR location for contract state class ${state::class.java.name}, Unable to determine JAR location for contract state class ${state::class.java.name},
and consequently unable to determine target platform version. and consequently unable to determine target platform version.
Enforcing state/contract agreement validation by default. Enforcing state/contract agreement validation by default.
For details see: https://docs.corda.net/api-contract-constraints.html#contract-state-agreement
""".trimIndent().replace("\n", " ")) """.trimIndent().replace("\n", " "))
return true return true
} }

View File

@ -116,8 +116,7 @@ class AttachmentsClassLoader(attachments: List<Attachment>,
if (untrusted.isNotEmpty()) { if (untrusted.isNotEmpty()) {
log.warn("Cannot verify transaction $sampleTxId as the following attachment IDs are untrusted: $untrusted." + log.warn("Cannot verify transaction $sampleTxId as the following attachment IDs are untrusted: $untrusted." +
"You will need to manually install the CorDapp to whitelist it for use. " + "You will need to manually install the CorDapp to whitelist it for use.")
"Please follow the operational steps outlined in https://docs.corda.net/cordapp-build-systems.html#cordapp-contract-attachments to learn more and continue.")
throw TransactionVerificationException.UntrustedAttachmentsException(sampleTxId, untrusted) throw TransactionVerificationException.UntrustedAttachmentsException(sampleTxId, untrusted)
} }

View File

@ -18,5 +18,4 @@ class MissingContractAttachments
@JvmOverloads @JvmOverloads
constructor(val states: List<TransactionState<ContractState>>, contractsClassName: String? = null, minimumRequiredContractClassVersion: Version? = null) : FlowException( constructor(val states: List<TransactionState<ContractState>>, contractsClassName: String? = null, minimumRequiredContractClassVersion: Version? = null) : FlowException(
"Cannot find contract attachments for " + "Cannot find contract attachments for " +
"${contractsClassName ?: states.map { it.contract }.distinct()}${minimumRequiredContractClassVersion?.let { ", minimum required contract class version $minimumRequiredContractClassVersion"}}. " + "${contractsClassName ?: states.map { it.contract }.distinct()}${minimumRequiredContractClassVersion?.let { ", minimum required contract class version $minimumRequiredContractClassVersion"}}.")
"See https://docs.corda.net/api-contract-constraints.html#debugging")

View File

@ -265,8 +265,7 @@ class OutstandingDatabaseChangesException(@Suppress("MemberVisibilityCanBePrivat
class CheckpointsException : DatabaseMigrationException("Attempting to update the database while there are flows in flight. " + class CheckpointsException : DatabaseMigrationException("Attempting to update the database while there are flows in flight. " +
"This is dangerous because the node might not be able to restore the flows correctly and could consequently fail. " + "This is dangerous because the node might not be able to restore the flows correctly and could consequently fail. " +
"Updating the database would make reverting to the previous version more difficult. " + "Updating the database would make reverting to the previous version more difficult.")
"Please drain your node first. See: https://docs.corda.net/upgrading-cordapps.html#flow-drains")
class DatabaseIncompatibleException(@Suppress("MemberVisibilityCanBePrivate") private val reason: String) : DatabaseMigrationException(errorMessageFor(reason)) { class DatabaseIncompatibleException(@Suppress("MemberVisibilityCanBePrivate") private val reason: String) : DatabaseMigrationException(errorMessageFor(reason)) {
internal companion object { internal companion object {

View File

@ -75,7 +75,8 @@ class RpcReconnectTests {
* This test runs flows in a loop and in the background kills the node or restarts it. * This test runs flows in a loop and in the background kills the node or restarts it.
* Also the RPC connection is made through a proxy that introduces random latencies and is also periodically killed. * Also the RPC connection is made through a proxy that introduces random latencies and is also periodically killed.
*/ */
@Test(timeout=300_000) @Suppress("ComplexMethod")
@Test(timeout=420_000)
fun `test that the RPC client is able to reconnect and proceed after node failure, restart, or connection reset`() { fun `test that the RPC client is able to reconnect and proceed after node failure, restart, or connection reset`() {
val nodeRunningTime = { Random().nextInt(12000) + 8000 } val nodeRunningTime = { Random().nextInt(12000) + 8000 }

View File

@ -58,7 +58,6 @@ import java.math.BigInteger
import java.net.InetSocketAddress import java.net.InetSocketAddress
import java.security.KeyPair import java.security.KeyPair
import java.security.PrivateKey import java.security.PrivateKey
import java.security.Security
import java.security.cert.X509CRL import java.security.cert.X509CRL
import java.security.cert.X509Certificate import java.security.cert.X509Certificate
import java.util.* import java.util.*
@ -117,7 +116,8 @@ class CertificateRevocationListNodeTests {
@Before @Before
fun setUp() { fun setUp() {
Security.addProvider(BouncyCastleProvider()) // Do not use Security.addProvider(BouncyCastleProvider()) to avoid EdDSA signature disruption in other tests.
Crypto.findProvider(BouncyCastleProvider.PROVIDER_NAME)
revokedNodeCerts.clear() revokedNodeCerts.clear()
server = CrlServer(NetworkHostAndPort("localhost", 0)) server = CrlServer(NetworkHostAndPort("localhost", 0))
server.start() server.start()

View File

@ -32,6 +32,7 @@ import net.corda.testing.driver.driver
import org.hibernate.exception.ConstraintViolationException import org.hibernate.exception.ConstraintViolationException
import org.junit.Before import org.junit.Before
import org.junit.Test import org.junit.Test
import java.lang.RuntimeException
import java.sql.Connection import java.sql.Connection
import java.util.concurrent.ExecutorService import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors import java.util.concurrent.Executors
@ -39,6 +40,7 @@ import java.util.concurrent.Semaphore
import javax.persistence.PersistenceException import javax.persistence.PersistenceException
import kotlin.test.assertEquals import kotlin.test.assertEquals
@Suppress("TooGenericExceptionCaught", "TooGenericExceptionThrown")
class FlowEntityManagerTest : AbstractFlowEntityManagerTest() { class FlowEntityManagerTest : AbstractFlowEntityManagerTest() {
@Before @Before
@ -364,6 +366,62 @@ class FlowEntityManagerTest : AbstractFlowEntityManagerTest() {
} }
} }
@Test(timeout = 300_000)
fun `non database error caught outside entity manager does not save entities`() {
var counter = 0
StaffedFlowHospital.onFlowDischarged.add { _, _ -> ++counter }
driver(DriverParameters(notarySpecs = emptyList(), startNodesInProcess = true)) {
val alice = startNode(providedName = ALICE_NAME).getOrThrow()
alice.rpc.startFlow(::EntityManagerSaveAndThrowNonDatabaseErrorFlow)
.returnValue.getOrThrow(30.seconds)
assertEquals(0, counter)
val entities = alice.rpc.startFlow(::GetCustomEntities).returnValue.getOrThrow()
assertEquals(0, entities.size)
}
}
@Test(timeout = 300_000)
fun `non database error caught outside entity manager after flush occurs does save entities`() {
var counter = 0
StaffedFlowHospital.onFlowDischarged.add { _, _ -> ++counter }
driver(DriverParameters(notarySpecs = emptyList(), startNodesInProcess = true)) {
val alice = startNode(providedName = ALICE_NAME).getOrThrow()
alice.rpc.startFlow(::EntityManagerSaveFlushAndThrowNonDatabaseErrorFlow)
.returnValue.getOrThrow(30.seconds)
assertEquals(0, counter)
val entities = alice.rpc.startFlow(::GetCustomEntities).returnValue.getOrThrow()
assertEquals(3, entities.size)
}
}
@Test(timeout = 300_000)
fun `database error caught inside entity manager non database exception thrown and caught outside entity manager should not save entities`() {
var counter = 0
StaffedFlowHospital.onFlowDischarged.add { _, _ -> ++counter }
driver(DriverParameters(notarySpecs = emptyList(), startNodesInProcess = true)) {
val alice = startNode(providedName = ALICE_NAME).getOrThrow()
alice.rpc.expectFlowSuccessAndAssertCreatedEntities(
flow = ::EntityManagerCatchDatabaseErrorInsideEntityManagerThrowNonDatabaseErrorAndCatchOutsideFlow,
commitStatus = CommitStatus.NO_INTERMEDIATE_COMMIT,
numberOfDischarges = 0,
numberOfExpectedEntities = 1
)
alice.rpc.expectFlowSuccessAndAssertCreatedEntities(
flow = ::EntityManagerCatchDatabaseErrorInsideEntityManagerThrowNonDatabaseErrorAndCatchOutsideFlow,
commitStatus = CommitStatus.INTERMEDIATE_COMMIT,
numberOfDischarges = 0,
numberOfExpectedEntities = 1
)
}
}
@StartableByRPC @StartableByRPC
class EntityManagerSaveEntitiesWithoutAFlushFlow : FlowLogic<Unit>() { class EntityManagerSaveEntitiesWithoutAFlushFlow : FlowLogic<Unit>() {
@ -706,6 +764,74 @@ class FlowEntityManagerTest : AbstractFlowEntityManagerTest() {
} }
} }
@StartableByRPC
class EntityManagerSaveAndThrowNonDatabaseErrorFlow : FlowLogic<Unit>() {
@Suspendable
override fun call() {
try {
serviceHub.withEntityManager {
persist(entityWithIdOne)
persist(entityWithIdTwo)
persist(entityWithIdThree)
throw RuntimeException("die")
}
} catch (e: RuntimeException) {
logger.info("Caught error")
}
sleep(1.millis)
}
}
@StartableByRPC
class EntityManagerSaveFlushAndThrowNonDatabaseErrorFlow : FlowLogic<Unit>() {
@Suspendable
override fun call() {
try {
serviceHub.withEntityManager {
persist(entityWithIdOne)
persist(entityWithIdTwo)
persist(entityWithIdThree)
flush()
throw RuntimeException("die")
}
} catch (e: RuntimeException) {
logger.info("Caught error")
}
sleep(1.millis)
}
}
@StartableByRPC
class EntityManagerCatchDatabaseErrorInsideEntityManagerThrowNonDatabaseErrorAndCatchOutsideFlow(private val commitStatus: CommitStatus) :
FlowLogic<Unit>() {
@Suspendable
override fun call() {
serviceHub.withEntityManager {
persist(entityWithIdOne)
}
if (commitStatus == CommitStatus.INTERMEDIATE_COMMIT) {
sleep(1.millis)
}
try {
serviceHub.withEntityManager {
persist(anotherEntityWithIdOne)
try {
flush()
} catch (e: PersistenceException) {
logger.info("Caught the exception!")
}
throw RuntimeException("die")
}
} catch (e: RuntimeException) {
logger.info("Caught error")
}
sleep(1.millis)
}
}
@CordaService @CordaService
class MyService(private val services: AppServiceHub) : SingletonSerializeAsToken() { class MyService(private val services: AppServiceHub) : SingletonSerializeAsToken() {

View File

@ -183,7 +183,7 @@ import java.sql.Savepoint
import java.time.Clock import java.time.Clock
import java.time.Duration import java.time.Duration
import java.time.format.DateTimeParseException import java.time.format.DateTimeParseException
import java.util.* import java.util.Properties
import java.util.concurrent.ExecutorService import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors import java.util.concurrent.Executors
import java.util.concurrent.LinkedBlockingQueue import java.util.concurrent.LinkedBlockingQueue
@ -193,8 +193,6 @@ import java.util.concurrent.TimeUnit.MINUTES
import java.util.concurrent.TimeUnit.SECONDS import java.util.concurrent.TimeUnit.SECONDS
import java.util.function.Consumer import java.util.function.Consumer
import javax.persistence.EntityManager import javax.persistence.EntityManager
import javax.persistence.PersistenceException
import kotlin.collections.ArrayList
/** /**
* A base node implementation that can be customised either for production (with real implementations that do real * A base node implementation that can be customised either for production (with real implementations that do real
@ -912,8 +910,7 @@ abstract class AbstractNode<S>(val configuration: NodeConfiguration,
requireNotNull(getCertificateStores()) { requireNotNull(getCertificateStores()) {
"One or more keyStores (identity or TLS) or trustStore not found. " + "One or more keyStores (identity or TLS) or trustStore not found. " +
"Please either copy your existing keys and certificates from another node, " + "Please either copy your existing keys and certificates from another node, " +
"or if you don't have one yet, fill out the config file and run corda.jar initial-registration. " + "or if you don't have one yet, fill out the config file and run corda.jar initial-registration."
"Read more at: https://docs.corda.net/permissioning.html"
} }
} catch (e: KeyStoreException) { } catch (e: KeyStoreException) {
throw IllegalArgumentException("At least one of the keystores or truststore passwords does not match configuration.") throw IllegalArgumentException("At least one of the keystores or truststore passwords does not match configuration.")
@ -1196,6 +1193,7 @@ abstract class AbstractNode<S>(val configuration: NodeConfiguration,
*/ */
override fun jdbcSession(): Connection = RestrictedConnection(database.createSession()) override fun jdbcSession(): Connection = RestrictedConnection(database.createSession())
@Suppress("TooGenericExceptionCaught")
override fun <T : Any?> withEntityManager(block: EntityManager.() -> T): T { override fun <T : Any?> withEntityManager(block: EntityManager.() -> T): T {
return database.transaction(useErrorHandler = false) { return database.transaction(useErrorHandler = false) {
session.flush() session.flush()
@ -1210,7 +1208,7 @@ abstract class AbstractNode<S>(val configuration: NodeConfiguration,
connection.rollback(savepoint) connection.rollback(savepoint)
} }
} }
} catch (e: PersistenceException) { } catch (e: Exception) {
if (manager.transaction.rollbackOnly) { if (manager.transaction.rollbackOnly) {
connection.rollback(savepoint) connection.rollback(savepoint)
} }
@ -1359,7 +1357,7 @@ fun CordaPersistence.startHikariPool(hikariProperties: Properties, databaseConfi
NodeDatabaseErrors.COULD_NOT_CONNECT, NodeDatabaseErrors.COULD_NOT_CONNECT,
cause = ex) cause = ex)
ex.cause is ClassNotFoundException -> throw CouldNotCreateDataSourceException( ex.cause is ClassNotFoundException -> throw CouldNotCreateDataSourceException(
"Could not find the database driver class. Please add it to the 'drivers' folder. See: https://docs.corda.net/corda-configuration-file.html", "Could not find the database driver class. Please add it to the 'drivers' folder.",
NodeDatabaseErrors.MISSING_DRIVER) NodeDatabaseErrors.MISSING_DRIVER)
ex is OutstandingDatabaseChangesException -> throw (DatabaseIncompatibleException(ex.message)) ex is OutstandingDatabaseChangesException -> throw (DatabaseIncompatibleException(ex.message))
else -> else ->

View File

@ -84,19 +84,18 @@ object CheckpointVerifier {
sealed class CheckpointIncompatibleException(override val message: String) : Exception() { sealed class CheckpointIncompatibleException(override val message: String) : Exception() {
class CannotBeDeserialisedException(val e: Exception) : CheckpointIncompatibleException( class CannotBeDeserialisedException(val e: Exception) : CheckpointIncompatibleException(
"Found checkpoint that cannot be deserialised using the current Corda version. Please revert to the previous version of Corda, " + "Found checkpoint that cannot be deserialised using the current Corda version. Please revert to the previous version of Corda, " +
"drain your node (see https://docs.corda.net/upgrading-cordapps.html#flow-drains), and try again. Cause: ${e.message}") "drain your node, and try again. Cause: ${e.message}")
class SubFlowCoreVersionIncompatibleException(val flowClass: Class<out FlowLogic<*>>, oldVersion: Int) : CheckpointIncompatibleException( class SubFlowCoreVersionIncompatibleException(val flowClass: Class<out FlowLogic<*>>, oldVersion: Int) : CheckpointIncompatibleException(
"Found checkpoint for flow: $flowClass that is incompatible with the current Corda platform. Please revert to the previous " + "Found checkpoint for flow: $flowClass that is incompatible with the current Corda platform. Please revert to the previous " +
"version of Corda (version $oldVersion), drain your node (see https://docs.corda.net/upgrading-cordapps.html#flow-drains), and try again.") "version of Corda (version $oldVersion), drain your node, and try again.")
class FlowVersionIncompatibleException(val flowClass: Class<out FlowLogic<*>>, val cordapp: Cordapp, oldHash: SecureHash) : CheckpointIncompatibleException( class FlowVersionIncompatibleException(val flowClass: Class<out FlowLogic<*>>, val cordapp: Cordapp, oldHash: SecureHash) : CheckpointIncompatibleException(
"Found checkpoint for flow: $flowClass that is incompatible with the current installed version of ${cordapp.name}. " + "Found checkpoint for flow: $flowClass that is incompatible with the current installed version of ${cordapp.name}. " +
"Please reinstall the previous version of the CorDapp (with hash: $oldHash), drain your node " + "Please reinstall the previous version of the CorDapp (with hash: $oldHash), drain your node, and try again.")
"(see https://docs.corda.net/upgrading-cordapps.html#flow-drains), and try again.")
class CordappNotInstalledException(classNotFound: String) : CheckpointIncompatibleException( class CordappNotInstalledException(classNotFound: String) : CheckpointIncompatibleException(
"Found checkpoint for CorDapp that is no longer installed. Specifically, could not find class $classNotFound. Please install the " + "Found checkpoint for CorDapp that is no longer installed. Specifically, could not find class $classNotFound. Please install the " +
"missing CorDapp, drain your node (see https://docs.corda.net/upgrading-cordapps.html#flow-drains), and try again.") "missing CorDapp, drain your node, and try again.")
} }

View File

@ -334,7 +334,6 @@ open class NodeStartup : NodeStartupLogging {
if (!certDirectory.isDirectory()) { if (!certDirectory.isDirectory()) {
printError("Unable to access certificates directory ${certDirectory}. This could be because the node has not been registered with the Identity Operator.") printError("Unable to access certificates directory ${certDirectory}. This could be because the node has not been registered with the Identity Operator.")
printError("Please see https://docs.corda.net/joining-a-compatibility-zone.html for more information.")
printError("Node will now shutdown.") printError("Node will now shutdown.")
return false return false
} }
@ -350,9 +349,7 @@ open class NodeStartup : NodeStartupLogging {
// //
// Also see https://bugs.openjdk.java.net/browse/JDK-8143378 // Also see https://bugs.openjdk.java.net/browse/JDK-8143378
val messages = listOf( val messages = listOf(
"Your computer took over a second to resolve localhost due an incorrect configuration. Corda will work but start very slowly until this is fixed. ", "Your computer took over a second to resolve localhost due an incorrect configuration. Corda will work but start very slowly until this is fixed."
"Please see https://docs.corda.net/troubleshooting.html#slow-localhost-resolution for information on how to fix this. ",
"It will only take a few seconds for you to resolve."
) )
logger.warn(messages.joinToString("")) logger.warn(messages.joinToString(""))
Emoji.renderIfSupported { Emoji.renderIfSupported {

View File

@ -36,6 +36,7 @@ object ConfigHelper {
private const val UPPERCASE_PROPERTY_PREFIX = "CORDA." private const val UPPERCASE_PROPERTY_PREFIX = "CORDA."
private val log = LoggerFactory.getLogger(javaClass) private val log = LoggerFactory.getLogger(javaClass)
@Suppress("LongParameterList")
fun loadConfig(baseDirectory: Path, fun loadConfig(baseDirectory: Path,
configFile: Path = baseDirectory / "node.conf", configFile: Path = baseDirectory / "node.conf",
allowMissingConfig: Boolean = false, allowMissingConfig: Boolean = false,
@ -88,7 +89,15 @@ object ConfigHelper {
return ConfigFactory.parseMap( return ConfigFactory.parseMap(
toProperties() toProperties()
.mapKeys { .mapKeys {
var newKey = (it.key as String) val original = it.key as String
// Reject environment variable that are in all caps
// since these cannot be properties.
if (original == original.toUpperCase()){
return@mapKeys original
}
var newKey = original
.replace('_', '.') .replace('_', '.')
.replace(UPPERCASE_PROPERTY_PREFIX, CORDA_PROPERTY_PREFIX) .replace(UPPERCASE_PROPERTY_PREFIX, CORDA_PROPERTY_PREFIX)

View File

@ -563,7 +563,7 @@ val Class<out FlowLogic<*>>.flowVersionAndInitiatingClass: Pair<Int, Class<out F
current = current.superclass current = current.superclass
?: return found ?: return found
?: throw IllegalArgumentException("$name, as a flow that initiates other flows, must be annotated with " + ?: throw IllegalArgumentException("$name, as a flow that initiates other flows, must be annotated with " +
"${InitiatingFlow::class.java.name}. See https://docs.corda.net/api-flows.html#flowlogic-annotations.") "${InitiatingFlow::class.java.name}.")
} }
} }

View File

@ -339,8 +339,7 @@ class SingleThreadedStateMachineManager(
private fun checkQuasarJavaAgentPresence() { private fun checkQuasarJavaAgentPresence() {
check(JavaAgent.isActive()) { check(JavaAgent.isActive()) {
"""Missing the '-javaagent' JVM argument. Make sure you run the tests with the Quasar java agent attached to your JVM. "Missing the '-javaagent' JVM argument. Make sure you run the tests with the Quasar java agent attached to your JVM."
#See https://docs.corda.net/head/testing.html#running-tests-in-intellij - 'Fiber classes not instrumented' for more details.""".trimMargin("#")
} }
} }

View File

@ -626,8 +626,7 @@ class HibernateQueryCriteriaParser(val contractStateType: Class<out ContractStat
e.message?.let { message -> e.message?.let { message ->
if (message.contains("Not an entity")) if (message.contains("Not an entity"))
throw VaultQueryException(""" throw VaultQueryException("""
Please register the entity '${entityStateClass.name}' Please register the entity '${entityStateClass.name}'.""")
See https://docs.corda.net/api-persistence.html#custom-schema-registration for more information""")
} }
throw VaultQueryException("Parsing error: ${e.message}") throw VaultQueryException("Parsing error: ${e.message}")
} }

View File

@ -110,7 +110,7 @@ object ContractJarTestUtils {
val fileManager = compiler.getStandardFileManager(null, null, null) val fileManager = compiler.getStandardFileManager(null, null, null)
fileManager.setLocation(StandardLocation.CLASS_OUTPUT, listOf(workingDir.toFile())) fileManager.setLocation(StandardLocation.CLASS_OUTPUT, listOf(workingDir.toFile()))
compiler.getTask(System.out.writer(), fileManager, null, null, null, listOf(source)).call() compiler.getTask(System.out.writer(), fileManager, null, listOf("-source", "8", "-target", "8"), null, listOf(source)).call()
val outFile = fileManager.getFileForInput(StandardLocation.CLASS_OUTPUT, packages.joinToString("."), "$className.class") val outFile = fileManager.getFileForInput(StandardLocation.CLASS_OUTPUT, packages.joinToString("."), "$className.class")
return Paths.get(outFile.name) return Paths.get(outFile.name)
} }

View File

@ -112,8 +112,6 @@ class JarSignatureCollectorTest {
""" """
Mismatch between signers [O=Alice Corp, L=Madrid, C=ES, O=Bob Plc, L=Rome, C=IT] for file _signable1 Mismatch between signers [O=Alice Corp, L=Madrid, C=ES, O=Bob Plc, L=Rome, C=IT] for file _signable1
and signers [O=Bob Plc, L=Rome, C=IT] for file _signable2. and signers [O=Bob Plc, L=Rome, C=IT] for file _signable2.
See https://docs.corda.net/api-contract-constraints.html#signature-constraints for details of the
constraints applied to attachment signatures.
""".trimIndent().replace('\n', ' ') """.trimIndent().replace('\n', ' ')
) { dir.getJarSigners(FILENAME) } ) { dir.getJarSigners(FILENAME) }
} }