diff --git a/.idea/compiler.xml b/.idea/compiler.xml
index 9844574b57..03aafa00c9 100644
--- a/.idea/compiler.xml
+++ b/.idea/compiler.xml
@@ -98,6 +98,8 @@
+
+
diff --git a/client/jfx/src/main/kotlin/net/corda/client/jfx/model/NodeMonitorModel.kt b/client/jfx/src/main/kotlin/net/corda/client/jfx/model/NodeMonitorModel.kt
index 12673dfb3c..0ab4ed96df 100644
--- a/client/jfx/src/main/kotlin/net/corda/client/jfx/model/NodeMonitorModel.kt
+++ b/client/jfx/src/main/kotlin/net/corda/client/jfx/model/NodeMonitorModel.kt
@@ -31,7 +31,7 @@ import net.corda.core.transactions.SignedTransaction
import net.corda.core.utilities.NetworkHostAndPort
import net.corda.core.utilities.contextLogger
import net.corda.core.utilities.seconds
-import org.apache.activemq.artemis.api.core.ActiveMQSecurityException
+import org.apache.activemq.artemis.api.core.ActiveMQException
import rx.Observable
import rx.Subscription
import rx.subjects.PublishSubject
@@ -202,8 +202,11 @@ class NodeMonitorModel {
val nodeInfo = _connection.proxy.nodeInfo()
require(nodeInfo.legalIdentitiesAndCerts.isNotEmpty())
_connection
- } catch(secEx: ActiveMQSecurityException) {
- // Happens when incorrect credentials provided - no point to retry connecting.
+ } catch(secEx: ActiveMQException) {
+ // Happens when:
+ // * incorrect credentials provided;
+ // * incorrect endpoint specified;
+ // - no point to retry connecting.
throw secEx
}
catch(th: Throwable) {
diff --git a/config/dev/log4j2.xml b/config/dev/log4j2.xml
index d71b403691..c3da2f3636 100644
--- a/config/dev/log4j2.xml
+++ b/config/dev/log4j2.xml
@@ -23,7 +23,22 @@
-
+
+
+
+
+
+
+
diff --git a/docs/source/api-testing.rst b/docs/source/api-testing.rst
index 5169f7b3b5..6c6ee9ddf5 100644
--- a/docs/source/api-testing.rst
+++ b/docs/source/api-testing.rst
@@ -252,26 +252,6 @@ The network must then be manually run before retrieving the future's value:
Accessing ``StartedMockNode`` internals
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-Creating a node database transaction
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Whenever you query a node's database (e.g. to extract information from the node's vault), you must wrap the query in
-a database transaction, as follows:
-
-.. container:: codeset
-
- .. sourcecode:: kotlin
-
- nodeA.database.transaction {
- // Perform query here.
- }
-
- .. sourcecode:: java
-
- node.getDatabase().transaction(tx -> {
- // Perform query here.
- }
-
Querying a node's vault
~~~~~~~~~~~~~~~~~~~~~~~
@@ -281,15 +261,11 @@ Recorded states can be retrieved from the vault of a ``StartedMockNode`` using:
.. sourcecode:: kotlin
- nodeA.database.transaction {
- val myStates = nodeA.services.vaultService.queryBy().states
- }
+ val myStates = nodeA.services.vaultService.queryBy().states
.. sourcecode:: java
- node.getDatabase().transaction(tx -> {
- List myStates = node.getServices().getVaultService().queryBy(MyStateType.class).getStates();
- }
+ List myStates = node.getServices().getVaultService().queryBy(MyStateType.class).getStates();
This allows you to check whether a given state has (or has not) been stored, and whether it has the correct attributes.
diff --git a/docs/source/changelog.rst b/docs/source/changelog.rst
index 9bef076789..31831a7f17 100644
--- a/docs/source/changelog.rst
+++ b/docs/source/changelog.rst
@@ -8,6 +8,8 @@ Unreleased
==========
* Introduced a hierarchy of ``DatabaseMigrationException``s, allowing ``NodeStartup`` to gracefully inform users of problems related to database migrations before exiting with a non-zero code.
+* ``ServiceHub`` and ``CordaRPCOps`` can now safely be used from multiple threads without incurring in database transaction problems.
+
* Doorman and NetworkMap url's can now be configured individually rather than being assumed to be
the same server. Current ``compatibilityZoneURL`` configurations remain valid. See both :doc:`corda-configuration-file`
and :doc:`permissioning` for details.
diff --git a/docs/source/getting-set-up.rst b/docs/source/getting-set-up.rst
index f40c62af5f..a81d9b1110 100644
--- a/docs/source/getting-set-up.rst
+++ b/docs/source/getting-set-up.rst
@@ -6,7 +6,7 @@ Software requirements
Corda uses industry-standard tools:
* **Oracle JDK 8 JVM** - minimum supported version **8u131**
-* **IntelliJ IDEA** - supported versions **2017.1**, **2017.2** and **2017.3**
+* **IntelliJ IDEA** - supported versions **2017.x** and **2018.x**
* **Git**
We also use Gradle and Kotlin, but you do not need to install them. A standalone Gradle wrapper is provided, and it
@@ -80,7 +80,7 @@ Download a sample project
^^^^^^^^^^^^^^^^^^^^^^^^^
1. Open a command prompt
2. Clone the CorDapp example repo by running ``git clone https://github.com/corda/cordapp-example``
-3. Move into the cordapp-example folder by running ``cd cordapp-example``
+3. Move into the ``cordapp-example`` folder by running ``cd cordapp-example``
Run from the command prompt
^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -92,16 +92,22 @@ Run from the command prompt
Run from IntelliJ
^^^^^^^^^^^^^^^^^
1. Open IntelliJ Community Edition
-2. On the splash screen, click "Open" (do NOT click "Import Project") and select the cordapp-example folder
+2. On the splash screen, click ``Open`` (do **not** click ``Import Project``) and select the ``cordapp-example`` folder
-.. warning:: If you click "Import Project" instead of "Open", the project's run configurations will be erased!
+.. warning:: If you click ``Import Project`` instead of ``Open``, the project's run configurations will be erased!
-3. Once the project is open, click "File > Project Structure". Under "Project SDK:", set the project SDK by clicking "New...", clicking "JDK", and navigating to C:\\Program Files\\Java\\jdk1.8.0_XXX (where "XXX" is the latest minor version number). Click "OK".
-4. Click "View > Tool Windows > Event Log", and click "Import Gradle project", then "OK". Wait, and click "OK" again when the "Gradle Project Data To Import" window appears
-5. Wait for indexing to finish (a progress bar will display at the bottom-right of the IntelliJ window until indexing is complete)
-6. At the top-right of the screen, to the left of the green "play" arrow, you should see a dropdown. In that dropdown, select "Run Example Cordapp - Kotlin" and click the green "play" arrow.
-7. Wait until the run windows displays the message "Webserver started up in XX.X sec"
-8. Test the CorDapp is running correctly by visiting the front end at http://localhost:10007/web/example/
+3. Once the project is open, click ``File``, then ``Project Structure``. Under ``Project SDK:``, set the project SDK by
+ clicking ``New...``, clicking ``JDK``, and navigating to ``C:\\Program Files\\Java\\jdk1.8.0_XXX`` (where ``XXX`` is
+ the latest minor version number). Click "OK"
+4. Again under ``File`` then ``Project Structure``, select ``Modules``. Click ``+``, then ``Import Module``, then select
+ the ``cordapp-example`` folder and click ``Open``. Choose to ``Import module from external model``, select
+ ``Gradle``, click ``Next`` then ``Finish`` (leaving the defaults) and ``OK``
+5. Wait for the indexing to finish (a progress bar will display at the bottom-right of the IntelliJ window until indexing
+ is complete)
+6. At the top-right of the screen, to the left of the green ``play`` arrow, you should see a dropdown. In that
+ dropdown, select ``Run Example Cordapp - Kotlin`` and click the green ``play`` arrow.
+7. Wait until the run windows displays the message ``Webserver started up in XX.X sec``
+8. Test the CorDapp is running correctly by visiting the front end at `http://localhost:10007/web/example/
.. _mac-label:
@@ -128,7 +134,7 @@ Download a sample project
^^^^^^^^^^^^^^^^^^^^^^^^^
1. Open a terminal
2. Clone the CorDapp example repo by running ``git clone https://github.com/corda/cordapp-example``
-3. Move into the cordapp-example folder by running ``cd cordapp-example``
+3. Move into the ``cordapp-example`` folder by running ``cd cordapp-example``
Run from the terminal
^^^^^^^^^^^^^^^^^^^^^
@@ -140,13 +146,22 @@ Run from the terminal
Run from IntelliJ
^^^^^^^^^^^^^^^^^
1. Open IntelliJ Community Edition
-2. On the splash screen, click "Open" (do NOT click "Import Project") and select the cordapp-example folder
-3. Once the project is open, click "File > Project Structure". Under "Project SDK:", set the project SDK by clicking "New...", clicking "JDK", and navigating to /Library/Java/JavaVirtualMachines/jdk1.8.0_XXX (where "XXX" is the latest minor version number). Click "OK".
-4. Click "View > Tool Windows > Event Log", and click "Import Gradle project", then "OK". Wait, and click "OK" again when the "Gradle Project Data To Import" window appears
-5. Wait for indexing to finish (a progress bar will display at the bottom-right of the IntelliJ window until indexing is complete)
-6. At the top-right of the screen, to the left of the green "play" arrow, you should see a dropdown. In that dropdown, select "Run Example Cordapp - Kotlin" and click the green "play" arrow.
-7. Wait until the run windows displays the message "Webserver started up in XX.X sec"
-8. Test the CorDapp is running correctly by visiting the front end at http://localhost:10007/web/example/
+2. On the splash screen, click ``Open`` (do **not** click ``Import Project``) and select the ``cordapp-example`` folder
+
+.. warning:: If you click ``Import Project`` instead of ``Open``, the project's run configurations will be erased!
+
+3. Once the project is open, click ``File``, then ``Project Structure``. Under ``Project SDK:``, set the project SDK by
+ clicking ``New...``, clicking ``JDK``, and navigating to ``C:\\Program Files\\Java\\jdk1.8.0_XXX`` (where ``XXX`` is
+ the latest minor version number). Click "OK"
+4. Again under ``File`` then ``Project Structure``, select ``Modules``. Click ``+``, then ``Import Module``, then select
+ the ``cordapp-example`` folder and click ``Open``. Choose to ``Import module from external model``, select
+ ``Gradle``, click ``Next`` then ``Finish`` (leaving the defaults) and ``OK``
+5. Wait for the indexing to finish (a progress bar will display at the bottom-right of the IntelliJ window until indexing
+ is complete)
+6. At the top-right of the screen, to the left of the green ``play`` arrow, you should see a dropdown. In that
+ dropdown, select ``Run Example Cordapp - Kotlin`` and click the green ``play`` arrow.
+7. Wait until the run windows displays the message ``Webserver started up in XX.X sec``
+8. Test the CorDapp is running correctly by visiting the front end at `http://localhost:10007/web/example/
Corda source code
-----------------
diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/persistence/CordaPersistence.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/persistence/CordaPersistence.kt
index 5a6cc539f9..969e89a721 100644
--- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/persistence/CordaPersistence.kt
+++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/persistence/CordaPersistence.kt
@@ -54,7 +54,7 @@ enum class TransactionIsolationLevel {
val jdbcValue: Int = java.sql.Connection::class.java.getField(jdbcString).get(null) as Int
}
-private val _contextDatabase = ThreadLocal()
+private val _contextDatabase = InheritableThreadLocal()
var contextDatabase: CordaPersistence
get() = _contextDatabase.get() ?: error("Was expecting to find CordaPersistence set on current thread: ${Strand.currentStrand()}")
set(database) = _contextDatabase.set(database)
diff --git a/node/src/integration-test/kotlin/net/corda/node/services/BFTNotaryServiceTests.kt b/node/src/integration-test/kotlin/net/corda/node/services/BFTNotaryServiceTests.kt
index 26ed5e119b..80ef21c815 100644
--- a/node/src/integration-test/kotlin/net/corda/node/services/BFTNotaryServiceTests.kt
+++ b/node/src/integration-test/kotlin/net/corda/node/services/BFTNotaryServiceTests.kt
@@ -16,7 +16,11 @@ import net.corda.core.contracts.AlwaysAcceptAttachmentConstraint
import net.corda.core.contracts.ContractState
import net.corda.core.contracts.StateRef
import net.corda.core.contracts.TimeWindow
-import net.corda.core.crypto.*
+import net.corda.core.crypto.CompositeKey
+import net.corda.core.crypto.SecureHash
+import net.corda.core.crypto.TransactionSignature
+import net.corda.core.crypto.isFulfilledBy
+import net.corda.core.crypto.sha256
import net.corda.core.flows.NotaryError
import net.corda.core.flows.NotaryException
import net.corda.core.flows.NotaryFlow
@@ -50,12 +54,26 @@ import net.corda.testing.node.internal.InternalMockNetwork.MockNode
import net.corda.testing.node.internal.InternalMockNodeParameters
import net.corda.testing.node.internal.startFlow
import org.hamcrest.Matchers.instanceOf
-import org.junit.*
+import org.junit.AfterClass
import org.junit.Assert.assertThat
+import org.junit.BeforeClass
+import org.junit.Test
import java.nio.file.Paths
import java.time.Duration
import java.time.Instant
import java.util.concurrent.ExecutionException
+import kotlin.collections.List
+import kotlin.collections.component1
+import kotlin.collections.component2
+import kotlin.collections.distinct
+import kotlin.collections.forEach
+import kotlin.collections.last
+import kotlin.collections.listOf
+import kotlin.collections.map
+import kotlin.collections.mapIndexedNotNull
+import kotlin.collections.plus
+import kotlin.collections.single
+import kotlin.collections.zip
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith
import kotlin.test.assertTrue
@@ -127,9 +145,7 @@ class BFTNotaryServiceTests {
val issueTx = signInitialTransaction(notary) {
addOutputState(DummyContract.SingleOwnerState(owner = info.singleIdentity()), DummyContract.PROGRAM_ID, AlwaysAcceptAttachmentConstraint)
}
- database.transaction {
- services.recordTransactions(issueTx)
- }
+ services.recordTransactions(issueTx)
val spendTxs = (1..10).map {
signInitialTransaction(notary) {
addInputState(issueTx.tx.outRef(0))
@@ -171,9 +187,7 @@ class BFTNotaryServiceTests {
val issueTx = signInitialTransaction(notary) {
addOutputState(DummyContract.SingleOwnerState(owner = info.singleIdentity()), DummyContract.PROGRAM_ID, AlwaysAcceptAttachmentConstraint)
}
- database.transaction {
- services.recordTransactions(issueTx)
- }
+ services.recordTransactions(issueTx)
val spendTx = signInitialTransaction(notary) {
addInputState(issueTx.tx.outRef(0))
setTimeWindow(TimeWindow.fromOnly(Instant.MAX))
@@ -188,7 +202,7 @@ class BFTNotaryServiceTests {
}
}
- @Test
+ @Test
fun `notarise issue tx with time-window`() {
node.run {
val issueTx = signInitialTransaction(notary) {
@@ -209,9 +223,7 @@ class BFTNotaryServiceTests {
val issueTx = signInitialTransaction(notary) {
addOutputState(DummyContract.SingleOwnerState(owner = info.singleIdentity()), DummyContract.PROGRAM_ID, AlwaysAcceptAttachmentConstraint)
}
- database.transaction {
- services.recordTransactions(issueTx)
- }
+ services.recordTransactions(issueTx)
val spendTx = signInitialTransaction(notary) {
addInputState(issueTx.tx.outRef(0))
setTimeWindow(TimeWindow.untilOnly(Instant.now() + Duration.ofHours(1)))
diff --git a/node/src/integration-test/kotlin/net/corda/node/services/RaftNotaryServiceTests.kt b/node/src/integration-test/kotlin/net/corda/node/services/RaftNotaryServiceTests.kt
index e6d98c3e20..3bfa4ed2bb 100644
--- a/node/src/integration-test/kotlin/net/corda/node/services/RaftNotaryServiceTests.kt
+++ b/node/src/integration-test/kotlin/net/corda/node/services/RaftNotaryServiceTests.kt
@@ -21,10 +21,10 @@ import net.corda.core.internal.concurrent.map
import net.corda.core.transactions.TransactionBuilder
import net.corda.core.utilities.getOrThrow
import net.corda.core.utilities.seconds
-import net.corda.testing.core.DUMMY_BANK_A_NAME
-import net.corda.testing.core.singleIdentity
import net.corda.testing.contracts.DummyContract
+import net.corda.testing.core.DUMMY_BANK_A_NAME
import net.corda.testing.core.dummyCommand
+import net.corda.testing.core.singleIdentity
import net.corda.testing.driver.DriverParameters
import net.corda.testing.driver.driver
import net.corda.testing.driver.internal.InProcessImpl
@@ -89,22 +89,17 @@ class RaftNotaryServiceTests : IntegrationTest() {
notarySpecs = listOf(NotarySpec(notaryName, cluster = ClusterSpec.Raft(clusterSize = 3)))
)) {
val bankA = startNode(providedName = DUMMY_BANK_A_NAME).map { (it as InProcessImpl) }.getOrThrow()
- val issueTx = bankA.database.transaction {
- val builder = DummyContract.generateInitial(Random().nextInt(), defaultNotaryIdentity, bankA.services.myInfo.singleIdentity().ref(0))
- .setTimeWindow(bankA.services.clock.instant(), 30.seconds)
- bankA.services.signInitialTransaction(builder)
- }
+ val builder = DummyContract.generateInitial(Random().nextInt(), defaultNotaryIdentity, bankA.services.myInfo.singleIdentity().ref(0))
+ .setTimeWindow(bankA.services.clock.instant(), 30.seconds)
+ val issueTx = bankA.services.signInitialTransaction(builder)
bankA.startFlow(NotaryFlow.Client(issueTx)).getOrThrow()
- }
+ }
}
private fun issueState(nodeHandle: InProcessImpl, notary: Party): StateAndRef<*> {
- return nodeHandle.database.transaction {
-
- val builder = DummyContract.generateInitial(Random().nextInt(), notary, nodeHandle.services.myInfo.singleIdentity().ref(0))
- val stx = nodeHandle.services.signInitialTransaction(builder)
- nodeHandle.services.recordTransactions(stx)
- StateAndRef(builder.outputStates().first(), StateRef(stx.id, 0))
- }
+ val builder = DummyContract.generateInitial(Random().nextInt(), notary, nodeHandle.services.myInfo.singleIdentity().ref(0))
+ val stx = nodeHandle.services.signInitialTransaction(builder)
+ nodeHandle.services.recordTransactions(stx)
+ return StateAndRef(builder.outputStates().first(), StateRef(stx.id, 0))
}
}
diff --git a/node/src/integration-test/kotlin/net/corda/node/services/network/PersistentNetworkMapCacheTest.kt b/node/src/integration-test/kotlin/net/corda/node/services/network/PersistentNetworkMapCacheTest.kt
index 67a13063fd..c26cb269bb 100644
--- a/node/src/integration-test/kotlin/net/corda/node/services/network/PersistentNetworkMapCacheTest.kt
+++ b/node/src/integration-test/kotlin/net/corda/node/services/network/PersistentNetworkMapCacheTest.kt
@@ -19,7 +19,13 @@ import net.corda.node.internal.Node
import net.corda.node.internal.StartedNode
import net.corda.testing.internal.IntegrationTestSchemas
import net.corda.testing.internal.toDatabaseSchemaName
-import net.corda.testing.core.*
+import net.corda.testing.core.ALICE_NAME
+import net.corda.testing.core.BOB_NAME
+import net.corda.testing.core.CHARLIE_NAME
+import net.corda.testing.core.DUMMY_NOTARY_NAME
+import net.corda.testing.core.TestIdentity
+import net.corda.testing.core.getTestPartyAndCertificate
+import net.corda.testing.core.singleIdentity
import net.corda.testing.node.internal.NodeBasedTest
import org.assertj.core.api.Assertions.assertThat
import org.assertj.core.api.Assertions.assertThatExceptionOfType
@@ -58,12 +64,10 @@ class PersistentNetworkMapCacheTest : NodeBasedTest() {
fun `unknown legal name`() {
val alice = startNodesWithPort(listOf(ALICE))[0]
val netMapCache = alice.services.networkMapCache
- alice.database.transaction {
- assertThat(netMapCache.getNodesByLegalName(DUMMY_NOTARY_NAME)).isEmpty()
- assertThat(netMapCache.getNodeByLegalName(DUMMY_NOTARY_NAME)).isNull()
- assertThat(netMapCache.getPeerByLegalName(DUMMY_NOTARY_NAME)).isNull()
- assertThat(netMapCache.getPeerCertificateByLegalName(DUMMY_NOTARY_NAME)).isNull()
- }
+ assertThat(netMapCache.getNodesByLegalName(DUMMY_NOTARY_NAME)).isEmpty()
+ assertThat(netMapCache.getNodeByLegalName(DUMMY_NOTARY_NAME)).isNull()
+ assertThat(netMapCache.getPeerByLegalName(DUMMY_NOTARY_NAME)).isNull()
+ assertThat(netMapCache.getPeerCertificateByLegalName(DUMMY_NOTARY_NAME)).isNull()
}
@Test
@@ -71,48 +75,40 @@ class PersistentNetworkMapCacheTest : NodeBasedTest() {
val alice = startNodesWithPort(listOf(ALICE))[0]
val netMapCache = alice.services.networkMapCache
- val distServiceNodeInfos = alice.database.transaction {
- val distributedIdentity = TestIdentity(DUMMY_NOTARY_NAME).identity
- (1..2).map {
- val nodeInfo = NodeInfo(
- addresses = listOf(NetworkHostAndPort("localhost", 1000 + it)),
- legalIdentitiesAndCerts = listOf(TestIdentity.fresh("Org-$it").identity, distributedIdentity),
- platformVersion = 3,
- serial = 1
- )
- netMapCache.addNode(nodeInfo)
- nodeInfo
- }
+ val distributedIdentity = TestIdentity(DUMMY_NOTARY_NAME).identity
+ val distServiceNodeInfos = (1..2).map {
+ val nodeInfo = NodeInfo(
+ addresses = listOf(NetworkHostAndPort("localhost", 1000 + it)),
+ legalIdentitiesAndCerts = listOf(TestIdentity.fresh("Org-$it").identity, distributedIdentity),
+ platformVersion = 3,
+ serial = 1
+ )
+ netMapCache.addNode(nodeInfo)
+ nodeInfo
}
- alice.database.transaction {
- assertThat(netMapCache.getNodesByLegalName(DUMMY_NOTARY_NAME)).containsOnlyElementsOf(distServiceNodeInfos)
- assertThatExceptionOfType(IllegalArgumentException::class.java)
- .isThrownBy { netMapCache.getNodeByLegalName(DUMMY_NOTARY_NAME) }
- .withMessageContaining(DUMMY_NOTARY_NAME.toString())
- }
+ assertThat(netMapCache.getNodesByLegalName(DUMMY_NOTARY_NAME)).containsOnlyElementsOf(distServiceNodeInfos)
+ assertThatExceptionOfType(IllegalArgumentException::class.java)
+ .isThrownBy { netMapCache.getNodeByLegalName(DUMMY_NOTARY_NAME) }
+ .withMessageContaining(DUMMY_NOTARY_NAME.toString())
}
@Test
fun `get nodes by owning key and by name`() {
val alice = startNodesWithPort(listOf(ALICE))[0]
val netCache = alice.services.networkMapCache
- alice.database.transaction {
- val res = netCache.getNodeByLegalIdentity(alice.info.singleIdentity())
- assertEquals(alice.info, res)
- val res2 = netCache.getNodeByLegalName(DUMMY_REGULATOR.name)
- assertEquals(infos.singleOrNull { DUMMY_REGULATOR.name in it.legalIdentities.map { it.name } }, res2)
- }
+ val res = netCache.getNodeByLegalIdentity(alice.info.singleIdentity())
+ assertEquals(alice.info, res)
+ val res2 = netCache.getNodeByLegalName(DUMMY_REGULATOR.name)
+ assertEquals(infos.singleOrNull { DUMMY_REGULATOR.name in it.legalIdentities.map { it.name } }, res2)
}
@Test
fun `get nodes by address`() {
val alice = startNodesWithPort(listOf(ALICE))[0]
val netCache = alice.services.networkMapCache
- alice.database.transaction {
- val res = netCache.getNodeByAddress(alice.info.addresses[0])
- assertEquals(alice.info, res)
- }
+ val res = netCache.getNodeByAddress(alice.info.addresses[0])
+ assertEquals(alice.info, res)
}
// This test has to be done as normal node not mock, because MockNodes don't have addresses.
@@ -122,9 +118,7 @@ class PersistentNetworkMapCacheTest : NodeBasedTest() {
val charliePartyCert = getTestPartyAndCertificate(CHARLIE_NAME, generateKeyPair().public)
val aliceCache = aliceNode.services.networkMapCache
aliceCache.addNode(aliceNode.info.copy(legalIdentitiesAndCerts = listOf(charliePartyCert)))
- val res = aliceNode.database.transaction {
- aliceCache.allNodes.filter { aliceNode.info.addresses[0] in it.addresses }
- }
+ val res = aliceCache.allNodes.filter { aliceNode.info.addresses[0] in it.addresses }
assertEquals(2, res.size)
}
diff --git a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt
index b3629b1c95..5b7172bf4e 100644
--- a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt
+++ b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt
@@ -30,6 +30,7 @@ import net.corda.core.flows.InitiatingFlow
import net.corda.core.flows.NotaryChangeFlow
import net.corda.core.flows.NotaryFlow
import net.corda.core.flows.StartableByService
+import net.corda.core.identity.AbstractParty
import net.corda.core.identity.CordaX500Name
import net.corda.core.identity.Party
import net.corda.core.identity.PartyAndCertificate
@@ -51,7 +52,6 @@ import net.corda.core.node.NetworkParameters
import net.corda.core.node.NodeInfo
import net.corda.core.node.ServiceHub
import net.corda.core.node.ServicesForResolution
-import net.corda.core.node.StatesToRecord
import net.corda.core.node.services.AttachmentStorage
import net.corda.core.node.services.CordaService
import net.corda.core.node.services.IdentityService
@@ -61,7 +61,6 @@ import net.corda.core.serialization.SerializationWhitelist
import net.corda.core.serialization.SerializeAsToken
import net.corda.core.serialization.SingletonSerializeAsToken
import net.corda.core.serialization.serialize
-import net.corda.core.transactions.SignedTransaction
import net.corda.core.utilities.NetworkHostAndPort
import net.corda.core.utilities.days
import net.corda.core.utilities.debug
@@ -177,6 +176,8 @@ import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors
import java.util.concurrent.TimeUnit
+import java.util.concurrent.TimeUnit.SECONDS
+import java.util.concurrent.atomic.AtomicReference
import kotlin.collections.set
import kotlin.reflect.KClass
import net.corda.core.crypto.generateKeyPair as cryptoGenerateKeyPair
@@ -250,9 +251,9 @@ abstract class AbstractNode(val configuration: NodeConfiguration,
@Volatile private var _started: StartedNode? = null
/** The implementation of the [CordaRPCOps] interface used by this node. */
- open fun makeRPCOps(flowStarter: FlowStarter, database: CordaPersistence, smm: StateMachineManager): CordaRPCOps {
+ open fun makeRPCOps(flowStarter: FlowStarter, smm: StateMachineManager): CordaRPCOps {
- val ops: CordaRPCOps = CordaRPCOpsImpl(services, smm, database, flowStarter, { shutdownExecutor.submit { stop() } })
+ val ops: CordaRPCOps = CordaRPCOpsImpl(services, smm, flowStarter, { shutdownExecutor.submit { stop() } })
val proxies = mutableListOf<(CordaRPCOps) -> CordaRPCOps>()
// Mind that order is relevant here.
proxies += ::AuthenticatedRpcOpsProxy
@@ -279,7 +280,12 @@ abstract class AbstractNode(val configuration: NodeConfiguration,
initCertificate()
val schemaService = NodeSchemaService(cordappLoader.cordappSchemas)
val (identity, identityKeyPair) = obtainIdentity(notaryConfig = null)
- return initialiseDatabasePersistence(schemaService, makeIdentityService(identity.certificate)).use {
+ // Wrapped in an atomic reference just to allow setting it before the closure below gets invoked.
+ val identityServiceRef = AtomicReference()
+ val database = initialiseDatabasePersistence(schemaService, { name -> identityServiceRef.get().wellKnownPartyFromX500Name(name) }, { party -> identityServiceRef.get().wellKnownPartyFromAnonymous(party) })
+ val identityService = makeIdentityService(identity.certificate, database)
+ identityServiceRef.set(identityService)
+ return database.use {
it.transaction {
// TODO The fact that we need to specify an empty list of notaries just to generate our node info looks
// like a design smell.
@@ -302,8 +308,16 @@ abstract class AbstractNode(val configuration: NodeConfiguration,
initialiseJVMAgents()
val schemaService = NodeSchemaService(cordappLoader.cordappSchemas, configuration.notary != null)
val (identity, identityKeyPair) = obtainIdentity(notaryConfig = null)
- val identityService = makeIdentityService(identity.certificate)
+ // Wrapped in an atomic reference just to allow setting it before the closure below gets invoked.
+ val identityServiceRef = AtomicReference()
+
+ // Do all of this in a database transaction so anything that might need a connection has one.
+ val database = initialiseDatabasePersistence(
+ schemaService,
+ { name -> identityServiceRef.get().wellKnownPartyFromX500Name(name) },
+ { party -> identityServiceRef.get().wellKnownPartyFromAnonymous(party) })
+ val identityService = makeIdentityService(identity.certificate, database).also(identityServiceRef::set)
networkMapClient = configuration.networkServices?.let { NetworkMapClient(it.networkMapURL, identityService.trustRoot) }
val networkParameteresReader = NetworkParametersReader(identityService.trustRoot, networkMapClient, configuration.baseDirectory)
val networkParameters = networkParameteresReader.networkParameters
@@ -311,15 +325,14 @@ abstract class AbstractNode(val configuration: NodeConfiguration,
"Node's platform version is lower than network's required minimumPlatformVersion"
}
- // Do all of this in a database transaction so anything that might need a connection has one.
- val (startedImpl, schedulerService) = initialiseDatabasePersistence(schemaService, identityService).transaction {
- val networkMapCache = NetworkMapCacheImpl(PersistentNetworkMapCache(database, networkParameters.notaries).start(), identityService)
+ val (startedImpl, schedulerService) = database.transaction {
+ val networkMapCache = NetworkMapCacheImpl(PersistentNetworkMapCache(database, networkParameters.notaries).start(), identityService, database)
val (keyPairs, nodeInfo) = updateNodeInfo(networkMapCache, networkMapClient, identity, identityKeyPair)
identityService.loadIdentities(nodeInfo.legalIdentitiesAndCerts)
val metrics = MetricRegistry()
val transactionStorage = makeTransactionStorage(database, configuration.transactionCacheSizeBytes)
log.debug("Transaction storage created")
- attachments = NodeAttachmentService(metrics, configuration.attachmentContentCacheSizeBytes, configuration.attachmentCacheBound)
+ attachments = NodeAttachmentService(metrics, configuration.attachmentContentCacheSizeBytes, configuration.attachmentCacheBound, database)
log.debug("Attachment service created")
val cordappProvider = CordappProviderImpl(cordappLoader, CordappConfigFileProvider(), attachments, networkParameters.whitelistedContractImplementations)
log.debug("Cordapp provider created")
@@ -371,7 +384,7 @@ abstract class AbstractNode(val configuration: NodeConfiguration,
}
makeVaultObservers(schedulerService, database.hibernateConfig, schemaService, flowLogicRefFactory)
- val rpcOps = makeRPCOps(flowStarter, database, smm)
+ val rpcOps = makeRPCOps(flowStarter, smm)
startMessagingService(rpcOps)
installCoreFlows()
val cordaServices = installCordaServices(flowStarter)
@@ -735,7 +748,7 @@ abstract class AbstractNode(val configuration: NodeConfiguration,
networkParameters: NetworkParameters): MutableList {
checkpointStorage = DBCheckpointStorage()
- val keyManagementService = makeKeyManagementService(identityService, keyPairs)
+ val keyManagementService = makeKeyManagementService(identityService, keyPairs, database)
_services = ServiceHubInternalImpl(
identityService,
keyManagementService,
@@ -757,7 +770,7 @@ abstract class AbstractNode(val configuration: NodeConfiguration,
services, cordappProvider, this)
}
- protected open fun makeTransactionStorage(database: CordaPersistence, transactionCacheSizeBytes: Long): WritableTransactionStorage = DBTransactionStorage(transactionCacheSizeBytes)
+ protected open fun makeTransactionStorage(database: CordaPersistence, transactionCacheSizeBytes: Long): WritableTransactionStorage = DBTransactionStorage(transactionCacheSizeBytes, database)
private fun makeVaultObservers(schedulerService: SchedulerService, hibernateConfig: HibernateConfiguration, schemaService: SchemaService, flowLogicRefFactory: FlowLogicRefFactory) {
ScheduledActivityObserver.install(services.vaultService, schedulerService, flowLogicRefFactory)
HibernateObserver.install(services.vaultService.rawUpdates, hibernateConfig, schemaService)
@@ -798,7 +811,9 @@ abstract class AbstractNode(val configuration: NodeConfiguration,
// Specific class so that MockNode can catch it.
class DatabaseConfigurationException(msg: String) : CordaException(msg)
- protected open fun initialiseDatabasePersistence(schemaService: SchemaService, identityService: IdentityService): CordaPersistence {
+ protected open fun initialiseDatabasePersistence(schemaService: SchemaService,
+ wellKnownPartyFromX500Name: (CordaX500Name) -> Party?,
+ wellKnownPartyFromAnonymous: (AbstractParty) -> Party?): CordaPersistence {
log.debug {
val driverClasses = DriverManager.getDrivers().asSequence().map { it.javaClass.name }
"Available JDBC drivers: $driverClasses"
@@ -806,7 +821,7 @@ abstract class AbstractNode(val configuration: NodeConfiguration,
val props = configuration.dataSourceProperties
if (props.isEmpty) throw DatabaseConfigurationException("There must be a database configured.")
- val database = configureDatabase(props, configuration.database, identityService, schemaService)
+ val database = configureDatabase(props, configuration.database, wellKnownPartyFromX500Name, wellKnownPartyFromAnonymous, schemaService)
// Now log the vendor string as this will also cause a connection to be tested eagerly.
logVendorString(database, log)
runOnStop += database::close
@@ -832,8 +847,8 @@ abstract class AbstractNode(val configuration: NodeConfiguration,
}
}
- protected open fun makeKeyManagementService(identityService: IdentityService, keyPairs: Set): KeyManagementService {
- return PersistentKeyManagementService(identityService, keyPairs)
+ protected open fun makeKeyManagementService(identityService: IdentityService, keyPairs: Set, database: CordaPersistence): KeyManagementService {
+ return PersistentKeyManagementService(identityService, keyPairs, database)
}
private fun makeCoreNotaryService(notaryConfig: NotaryConfig, database: CordaPersistence): NotaryService {
@@ -864,10 +879,10 @@ abstract class AbstractNode(val configuration: NodeConfiguration,
}
}
- private fun makeIdentityService(identityCert: X509Certificate): PersistentIdentityService {
+ private fun makeIdentityService(identityCert: X509Certificate, database: CordaPersistence): PersistentIdentityService {
val trustRoot = configuration.loadTrustStore().getCertificate(X509Utilities.CORDA_ROOT_CA)
val nodeCa = configuration.loadNodeKeyStore().getCertificate(X509Utilities.CORDA_CLIENT_CA)
- return PersistentIdentityService(trustRoot, listOf(identityCert, nodeCa))
+ return PersistentIdentityService(trustRoot, database, listOf(identityCert, nodeCa))
}
protected abstract fun makeTransactionVerifierService(): TransactionVerifierService
@@ -945,8 +960,8 @@ abstract class AbstractNode(val configuration: NodeConfiguration,
}
protected open fun generateKeyPair() = cryptoGenerateKeyPair()
- protected open fun makeVaultService(keyManagementService: KeyManagementService, services: ServicesForResolution, hibernateConfig: HibernateConfiguration): VaultServiceInternal {
- return NodeVaultService(platformClock, keyManagementService, services, hibernateConfig)
+ protected open fun makeVaultService(keyManagementService: KeyManagementService, services: ServicesForResolution, hibernateConfig: HibernateConfiguration, database: CordaPersistence): VaultServiceInternal {
+ return NodeVaultService(platformClock, keyManagementService, services, hibernateConfig, database)
}
/** Load configured JVM agents */
@@ -983,10 +998,10 @@ abstract class AbstractNode(val configuration: NodeConfiguration,
private val servicesForResolution: ServicesForResolution
) : SingletonSerializeAsToken(), ServiceHubInternal, ServicesForResolution by servicesForResolution {
override val rpcFlows = ArrayList>>()
- override val stateMachineRecordedTransactionMapping = DBTransactionMappingStorage()
+ override val stateMachineRecordedTransactionMapping = DBTransactionMappingStorage(database)
override val auditService = DummyAuditService()
override val transactionVerifierService by lazy { makeTransactionVerifierService() }
- override val vaultService by lazy { makeVaultService(keyManagementService, servicesForResolution, database.hibernateConfig) }
+ override val vaultService by lazy { makeVaultService(keyManagementService, servicesForResolution, database.hibernateConfig, database) }
override val contractUpgradeService by lazy { ContractUpgradeServiceImpl() }
override val attachments: AttachmentStorage get() = this@AbstractNode.attachments
override val networkService: MessagingService get() = network
@@ -1002,12 +1017,6 @@ abstract class AbstractNode(val configuration: NodeConfiguration,
return flowFactories[initiatingFlowClass]
}
- override fun recordTransactions(statesToRecord: StatesToRecord, txs: Iterable) {
- database.transaction {
- super.recordTransactions(statesToRecord, txs)
- }
- }
-
override fun jdbcSession(): Connection = database.createSession()
// allows services to register handlers to be informed when the node stop method is called
@@ -1077,15 +1086,16 @@ internal class NetworkMapCacheEmptyException : Exception()
fun configureDatabase(hikariProperties: Properties,
databaseConfig: DatabaseConfig,
- identityService: IdentityService,
+ wellKnownPartyFromX500Name: (CordaX500Name) -> Party?,
+ wellKnownPartyFromAnonymous: (AbstractParty) -> Party?,
schemaService: SchemaService = NodeSchemaService()): CordaPersistence {
// Register the AbstractPartyDescriptor so Hibernate doesn't warn when encountering AbstractParty. Unfortunately
// Hibernate warns about not being able to find a descriptor if we don't provide one, but won't use it by default
// so we end up providing both descriptor and converter. We should re-examine this in later versions to see if
// either Hibernate can be convinced to stop warning, use the descriptor by default, or something else.
- JavaTypeDescriptorRegistry.INSTANCE.addDescriptor(AbstractPartyDescriptor(identityService))
+ JavaTypeDescriptorRegistry.INSTANCE.addDescriptor(AbstractPartyDescriptor(wellKnownPartyFromX500Name, wellKnownPartyFromAnonymous))
val dataSource = DataSourceFactory.createDataSource(hikariProperties)
- val attributeConverters = listOf(AbstractPartyToX500NameAsStringConverter(identityService))
+ val attributeConverters = listOf(AbstractPartyToX500NameAsStringConverter(wellKnownPartyFromX500Name, wellKnownPartyFromAnonymous))
val jdbcUrl = hikariProperties.getProperty("dataSource.url", "")
SchemaMigration(
schemaService.schemaOptions.keys,
diff --git a/node/src/main/kotlin/net/corda/node/internal/CordaRPCOpsImpl.kt b/node/src/main/kotlin/net/corda/node/internal/CordaRPCOpsImpl.kt
index 339fc7b184..b686c2fcaa 100644
--- a/node/src/main/kotlin/net/corda/node/internal/CordaRPCOpsImpl.kt
+++ b/node/src/main/kotlin/net/corda/node/internal/CordaRPCOpsImpl.kt
@@ -27,12 +27,26 @@ import net.corda.core.internal.FlowStateMachine
import net.corda.core.internal.RPC_UPLOADER
import net.corda.core.internal.STRUCTURAL_STEP_PREFIX
import net.corda.core.internal.sign
-import net.corda.core.messaging.*
+import net.corda.core.messaging.CordaRPCOps
+import net.corda.core.messaging.DataFeed
+import net.corda.core.messaging.FlowHandle
+import net.corda.core.messaging.FlowHandleImpl
+import net.corda.core.messaging.FlowProgressHandle
+import net.corda.core.messaging.FlowProgressHandleImpl
+import net.corda.core.messaging.ParametersUpdateInfo
+import net.corda.core.messaging.RPCReturnsObservables
+import net.corda.core.messaging.StateMachineInfo
+import net.corda.core.messaging.StateMachineTransactionMapping
+import net.corda.core.messaging.StateMachineUpdate
import net.corda.core.node.NodeInfo
import net.corda.core.node.services.AttachmentId
import net.corda.core.node.services.NetworkMapCache
import net.corda.core.node.services.Vault
-import net.corda.core.node.services.vault.*
+import net.corda.core.node.services.vault.AttachmentQueryCriteria
+import net.corda.core.node.services.vault.AttachmentSort
+import net.corda.core.node.services.vault.PageSpecification
+import net.corda.core.node.services.vault.QueryCriteria
+import net.corda.core.node.services.vault.Sort
import net.corda.core.serialization.serialize
import net.corda.core.transactions.SignedTransaction
import net.corda.core.utilities.getOrThrow
@@ -42,7 +56,6 @@ import net.corda.node.services.messaging.context
import net.corda.node.services.statemachine.StateMachineManager
import net.corda.nodeapi.exceptions.NonRpcFlowException
import net.corda.nodeapi.exceptions.RejectedCommandException
-import net.corda.nodeapi.internal.persistence.CordaPersistence
import rx.Observable
import java.io.InputStream
import java.security.PublicKey
@@ -55,7 +68,6 @@ import java.time.Instant
internal class CordaRPCOpsImpl(
private val services: ServiceHubInternal,
private val smm: StateMachineManager,
- private val database: CordaPersistence,
private val flowStarter: FlowStarter,
private val shutdownNode: () -> Unit
) : CordaRPCOps {
@@ -78,18 +90,15 @@ internal class CordaRPCOpsImpl(
}
override fun networkMapFeed(): DataFeed, NetworkMapCache.MapChange> {
- return database.transaction {
- services.networkMapCache.track()
- }
+ return services.networkMapCache.track()
}
override fun vaultQueryBy(criteria: QueryCriteria,
paging: PageSpecification,
sorting: Sort,
contractStateType: Class): Vault.Page {
- return database.transaction {
- services.vaultService._queryBy(criteria, paging, sorting, contractStateType)
- }
+ contractStateType.checkIsA()
+ return services.vaultService._queryBy(criteria, paging, sorting, contractStateType)
}
@RPCReturnsObservables
@@ -97,9 +106,8 @@ internal class CordaRPCOpsImpl(
paging: PageSpecification,
sorting: Sort,
contractStateType: Class): DataFeed, Vault.Update> {
- return database.transaction {
- services.vaultService._trackBy(criteria, paging, sorting, contractStateType)
- }
+ contractStateType.checkIsA()
+ return services.vaultService._trackBy(criteria, paging, sorting, contractStateType)
}
@Suppress("OverridingDeprecatedMember")
@@ -111,9 +119,7 @@ internal class CordaRPCOpsImpl(
@Suppress("OverridingDeprecatedMember")
override fun internalVerifiedTransactionsFeed(): DataFeed, SignedTransaction> {
- return database.transaction {
- services.validatedTransactions.track()
- }
+ return services.validatedTransactions.track()
}
override fun stateMachinesSnapshot(): List {
@@ -125,13 +131,11 @@ internal class CordaRPCOpsImpl(
override fun killFlow(id: StateMachineRunId) = smm.killFlow(id)
override fun stateMachinesFeed(): DataFeed, StateMachineUpdate> {
- return database.transaction {
- val (allStateMachines, changes) = smm.track()
- DataFeed(
- allStateMachines.map { stateMachineInfoFromFlowLogic(it) },
- changes.map { stateMachineUpdateFromStateMachineChange(it) }
- )
- }
+ val (allStateMachines, changes) = smm.track()
+ return DataFeed(
+ allStateMachines.map { stateMachineInfoFromFlowLogic(it) },
+ changes.map { stateMachineUpdateFromStateMachineChange(it) }
+ )
}
override fun stateMachineRecordedTransactionMappingSnapshot(): List {
@@ -141,9 +145,7 @@ internal class CordaRPCOpsImpl(
}
override fun stateMachineRecordedTransactionMappingFeed(): DataFeed, StateMachineTransactionMapping> {
- return database.transaction {
- services.stateMachineRecordedTransactionMapping.track()
- }
+ return services.stateMachineRecordedTransactionMapping.track()
}
override fun nodeInfo(): NodeInfo {
@@ -155,15 +157,11 @@ internal class CordaRPCOpsImpl(
}
override fun addVaultTransactionNote(txnId: SecureHash, txnNote: String) {
- return database.transaction {
- services.vaultService.addNoteToTransaction(txnId, txnNote)
- }
+ services.vaultService.addNoteToTransaction(txnId, txnNote)
}
override fun getVaultTransactionNotes(txnId: SecureHash): Iterable {
- return database.transaction {
- services.vaultService.getTransactionNotes(txnId)
- }
+ return services.vaultService.getTransactionNotes(txnId)
}
override fun startTrackedFlowDynamic(logicType: Class>, vararg args: Any?): FlowProgressHandle {
@@ -191,38 +189,23 @@ internal class CordaRPCOpsImpl(
}
override fun attachmentExists(id: SecureHash): Boolean {
- // TODO: this operation should not require an explicit transaction
- return database.transaction {
- services.attachments.openAttachment(id) != null
- }
+ return services.attachments.openAttachment(id) != null
}
override fun openAttachment(id: SecureHash): InputStream {
- // TODO: this operation should not require an explicit transaction
- return database.transaction {
- services.attachments.openAttachment(id)!!.open()
- }
+ return services.attachments.openAttachment(id)!!.open()
}
override fun uploadAttachment(jar: InputStream): SecureHash {
- // TODO: this operation should not require an explicit transaction
- return database.transaction {
- services.attachments.importAttachment(jar, RPC_UPLOADER, null)
- }
+ return services.attachments.importAttachment(jar, RPC_UPLOADER, null)
}
- override fun uploadAttachmentWithMetadata(jar: InputStream, uploader:String, filename:String): SecureHash {
- // TODO: this operation should not require an explicit transaction
- return database.transaction {
- services.attachments.importAttachment(jar, uploader, filename)
- }
+ override fun uploadAttachmentWithMetadata(jar: InputStream, uploader: String, filename: String): SecureHash {
+ return services.attachments.importAttachment(jar, uploader, filename)
}
override fun queryAttachments(query: AttachmentQueryCriteria, sorting: AttachmentSort?): List {
- // TODO: this operation should not require an explicit transaction
- return database.transaction {
- services.attachments.queryAttachments(query, sorting)
- }
+ return services.attachments.queryAttachments(query, sorting)
}
override fun currentNodeTime(): Instant = Instant.now(services.clock)
@@ -230,43 +213,31 @@ internal class CordaRPCOpsImpl(
override fun waitUntilNetworkReady(): CordaFuture = services.networkMapCache.nodeReady
override fun wellKnownPartyFromAnonymous(party: AbstractParty): Party? {
- return database.transaction {
- services.identityService.wellKnownPartyFromAnonymous(party)
- }
+ return services.identityService.wellKnownPartyFromAnonymous(party)
}
override fun partyFromKey(key: PublicKey): Party? {
- return database.transaction {
- services.identityService.partyFromKey(key)
- }
+ return services.identityService.partyFromKey(key)
}
override fun wellKnownPartyFromX500Name(x500Name: CordaX500Name): Party? {
- return database.transaction {
- services.identityService.wellKnownPartyFromX500Name(x500Name)
- }
+ return services.identityService.wellKnownPartyFromX500Name(x500Name)
}
override fun notaryPartyFromX500Name(x500Name: CordaX500Name): Party? = services.networkMapCache.getNotary(x500Name)
override fun partiesFromName(query: String, exactMatch: Boolean): Set {
- return database.transaction {
- services.identityService.partiesFromName(query, exactMatch)
- }
+ return services.identityService.partiesFromName(query, exactMatch)
}
override fun nodeInfoFromParty(party: AbstractParty): NodeInfo? {
- return database.transaction {
- services.networkMapCache.getNodeByLegalIdentity(party)
- }
+ return services.networkMapCache.getNodeByLegalIdentity(party)
}
override fun registeredFlows(): List = services.rpcFlows.map { it.name }.sorted()
override fun clearNetworkMapCache() {
- database.transaction {
- services.networkMapCache.clearNetworkMapCache()
- }
+ services.networkMapCache.clearNetworkMapCache()
}
override fun vaultQuery(contractStateType: Class): Vault.Page {
@@ -325,14 +296,25 @@ internal class CordaRPCOpsImpl(
}
private fun InvocationContext.toFlowInitiator(): FlowInitiator {
-
val principal = origin.principal().name
return when (origin) {
is InvocationOrigin.RPC -> FlowInitiator.RPC(principal)
- is InvocationOrigin.Peer -> services.identityService.wellKnownPartyFromX500Name((origin as InvocationOrigin.Peer).party)?.let { FlowInitiator.Peer(it) } ?: throw IllegalStateException("Unknown peer with name ${(origin as InvocationOrigin.Peer).party}.")
+ is InvocationOrigin.Peer -> {
+ val wellKnownParty = services.identityService.wellKnownPartyFromX500Name((origin as InvocationOrigin.Peer).party)
+ wellKnownParty?.let { FlowInitiator.Peer(it) }
+ ?: throw IllegalStateException("Unknown peer with name ${(origin as InvocationOrigin.Peer).party}.")
+ }
is InvocationOrigin.Service -> FlowInitiator.Service(principal)
InvocationOrigin.Shell -> FlowInitiator.Shell
is InvocationOrigin.Scheduled -> FlowInitiator.Scheduled((origin as InvocationOrigin.Scheduled).scheduledState)
}
}
+
+ /**
+ * RPC can be invoked from the shell where the type parameter of any [Class] parameter is lost, so we must
+ * explicitly check that the provided [Class] is the one we want.
+ */
+ private inline fun Class<*>.checkIsA() {
+ require(TARGET::class.java.isAssignableFrom(this)) { "$name is not a ${TARGET::class.java.name}" }
+ }
}
\ No newline at end of file
diff --git a/node/src/main/kotlin/net/corda/node/internal/Node.kt b/node/src/main/kotlin/net/corda/node/internal/Node.kt
index bf60b130c5..9970060018 100644
--- a/node/src/main/kotlin/net/corda/node/internal/Node.kt
+++ b/node/src/main/kotlin/net/corda/node/internal/Node.kt
@@ -13,7 +13,9 @@ package net.corda.node.internal
import com.codahale.metrics.JmxReporter
import net.corda.client.rpc.internal.serialization.amqp.AMQPClientSerializationScheme
import net.corda.core.concurrent.CordaFuture
+import net.corda.core.identity.AbstractParty
import net.corda.core.identity.CordaX500Name
+import net.corda.core.identity.Party
import net.corda.core.internal.Emoji
import net.corda.core.internal.concurrent.openFuture
import net.corda.core.internal.concurrent.thenMatch
@@ -23,7 +25,6 @@ import net.corda.core.messaging.RPCOps
import net.corda.core.node.NetworkParameters
import net.corda.core.node.NodeInfo
import net.corda.core.node.ServiceHub
-import net.corda.core.node.services.IdentityService
import net.corda.core.node.services.TransactionVerifierService
import net.corda.core.serialization.internal.SerializationEnvironmentImpl
import net.corda.core.serialization.internal.nodeSerializationEnv
@@ -343,7 +344,9 @@ open class Node(configuration: NodeConfiguration,
* This is not using the H2 "automatic mixed mode" directly but leans on many of the underpinnings. For more details
* on H2 URLs and configuration see: http://www.h2database.com/html/features.html#database_url
*/
- override fun initialiseDatabasePersistence(schemaService: SchemaService, identityService: IdentityService): CordaPersistence {
+ override fun initialiseDatabasePersistence(schemaService: SchemaService,
+ wellKnownPartyFromX500Name: (CordaX500Name) -> Party?,
+ wellKnownPartyFromAnonymous: (AbstractParty) -> Party?): CordaPersistence {
val databaseUrl = configuration.dataSourceProperties.getProperty("dataSource.url")
val h2Prefix = "jdbc:h2:file:"
if (databaseUrl != null && databaseUrl.startsWith(h2Prefix)) {
@@ -363,7 +366,7 @@ open class Node(configuration: NodeConfiguration,
else if (databaseUrl != null) {
printBasicNodeInfo("Database connection url is", databaseUrl)
}
- return super.initialiseDatabasePersistence(schemaService, identityService)
+ return super.initialiseDatabasePersistence(schemaService, wellKnownPartyFromX500Name, wellKnownPartyFromAnonymous)
}
private val _startupComplete = openFuture()
diff --git a/node/src/main/kotlin/net/corda/node/services/api/ServiceHubInternal.kt b/node/src/main/kotlin/net/corda/node/services/api/ServiceHubInternal.kt
index 8239c4b36d..5035c6024f 100644
--- a/node/src/main/kotlin/net/corda/node/services/api/ServiceHubInternal.kt
+++ b/node/src/main/kotlin/net/corda/node/services/api/ServiceHubInternal.kt
@@ -34,6 +34,7 @@ import net.corda.node.services.network.NetworkMapUpdater
import net.corda.node.services.statemachine.ExternalEvent
import net.corda.node.services.statemachine.FlowStateMachineImpl
import net.corda.nodeapi.internal.persistence.CordaPersistence
+import net.corda.nodeapi.internal.persistence.contextDatabase
interface NetworkMapCacheInternal : NetworkMapCache, NetworkMapCacheBaseInternal
interface NetworkMapCacheBaseInternal : NetworkMapCacheBase {
@@ -62,55 +63,58 @@ interface ServiceHubInternal : ServiceHub {
fun recordTransactions(statesToRecord: StatesToRecord, txs: Iterable,
validatedTransactions: WritableTransactionStorage,
stateMachineRecordedTransactionMapping: StateMachineRecordedTransactionMappingStorage,
- vaultService: VaultServiceInternal) {
+ vaultService: VaultServiceInternal,
+ database: CordaPersistence) {
- require(txs.any()) { "No transactions passed in for recording" }
- val recordedTransactions = txs.filter { validatedTransactions.addTransaction(it) }
- val stateMachineRunId = FlowStateMachineImpl.currentStateMachine()?.id
- if (stateMachineRunId != null) {
- recordedTransactions.forEach {
- stateMachineRecordedTransactionMapping.addMapping(stateMachineRunId, it.id)
+ database.transaction {
+ require(txs.any()) { "No transactions passed in for recording" }
+ val recordedTransactions = txs.filter { validatedTransactions.addTransaction(it) }
+ val stateMachineRunId = FlowStateMachineImpl.currentStateMachine()?.id
+ if (stateMachineRunId != null) {
+ recordedTransactions.forEach {
+ stateMachineRecordedTransactionMapping.addMapping(stateMachineRunId, it.id)
+ }
+ } else {
+ log.warn("Transactions recorded from outside of a state machine")
}
- } else {
- log.warn("Transactions recorded from outside of a state machine")
- }
- if (statesToRecord != StatesToRecord.NONE) {
- // When the user has requested StatesToRecord.ALL we may end up recording and relationally mapping states
- // that do not involve us and that we cannot sign for. This will break coin selection and thus a warning
- // is present in the documentation for this feature (see the "Observer nodes" tutorial on docs.corda.net).
- //
- // The reason for this is three-fold:
- //
- // 1) We are putting in place the observer mode feature relatively quickly to meet specific customer
- // launch target dates.
- //
- // 2) The right design for vaults which mix observations and relevant states isn't entirely clear yet.
- //
- // 3) If we get the design wrong it could create security problems and business confusions.
- //
- // Back in the bitcoinj days I did add support for "watching addresses" to the wallet code, which is the
- // Bitcoin equivalent of observer nodes:
- //
- // https://bitcoinj.github.io/working-with-the-wallet#watching-wallets
- //
- // The ability to have a wallet containing both irrelevant and relevant states complicated everything quite
- // dramatically, even methods as basic as the getBalance() API which required additional modes to let you
- // query "balance I can spend" vs "balance I am observing". In the end it might have been better to just
- // require the user to create an entirely separate wallet for observing with.
- //
- // In Corda we don't support a single node having multiple vaults (at the time of writing), and it's not
- // clear that's the right way to go: perhaps adding an "origin" column to the VAULT_STATES table is a better
- // solution. Then you could select subsets of states depending on where the report came from.
- //
- // The risk of doing this is that apps/developers may use 'canned SQL queries' not written by us that forget
- // to add a WHERE clause for the origin column. Those queries will seem to work most of the time until
- // they're run on an observer node and mix in irrelevant data. In the worst case this may result in
- // erroneous data being reported to the user, which could cause security problems.
- //
- // Because the primary use case for recording irrelevant states is observer/regulator nodes, who are unlikely
- // to make writes to the ledger very often or at all, we choose to punt this issue for the time being.
- vaultService.notifyAll(statesToRecord, recordedTransactions.map { it.coreTransaction })
+ if (statesToRecord != StatesToRecord.NONE) {
+ // When the user has requested StatesToRecord.ALL we may end up recording and relationally mapping states
+ // that do not involve us and that we cannot sign for. This will break coin selection and thus a warning
+ // is present in the documentation for this feature (see the "Observer nodes" tutorial on docs.corda.net).
+ //
+ // The reason for this is three-fold:
+ //
+ // 1) We are putting in place the observer mode feature relatively quickly to meet specific customer
+ // launch target dates.
+ //
+ // 2) The right design for vaults which mix observations and relevant states isn't entirely clear yet.
+ //
+ // 3) If we get the design wrong it could create security problems and business confusions.
+ //
+ // Back in the bitcoinj days I did add support for "watching addresses" to the wallet code, which is the
+ // Bitcoin equivalent of observer nodes:
+ //
+ // https://bitcoinj.github.io/working-with-the-wallet#watching-wallets
+ //
+ // The ability to have a wallet containing both irrelevant and relevant states complicated everything quite
+ // dramatically, even methods as basic as the getBalance() API which required additional modes to let you
+ // query "balance I can spend" vs "balance I am observing". In the end it might have been better to just
+ // require the user to create an entirely separate wallet for observing with.
+ //
+ // In Corda we don't support a single node having multiple vaults (at the time of writing), and it's not
+ // clear that's the right way to go: perhaps adding an "origin" column to the VAULT_STATES table is a better
+ // solution. Then you could select subsets of states depending on where the report came from.
+ //
+ // The risk of doing this is that apps/developers may use 'canned SQL queries' not written by us that forget
+ // to add a WHERE clause for the origin column. Those queries will seem to work most of the time until
+ // they're run on an observer node and mix in irrelevant data. In the worst case this may result in
+ // erroneous data being reported to the user, which could cause security problems.
+ //
+ // Because the primary use case for recording irrelevant states is observer/regulator nodes, who are unlikely
+ // to make writes to the ledger very often or at all, we choose to punt this issue for the time being.
+ vaultService.notifyAll(statesToRecord, recordedTransactions.map { it.coreTransaction })
+ }
}
}
}
@@ -135,7 +139,7 @@ interface ServiceHubInternal : ServiceHub {
val networkMapUpdater: NetworkMapUpdater
override val cordappProvider: CordappProviderInternal
override fun recordTransactions(statesToRecord: StatesToRecord, txs: Iterable) {
- recordTransactions(statesToRecord, txs, validatedTransactions, stateMachineRecordedTransactionMapping, vaultService)
+ recordTransactions(statesToRecord, txs, validatedTransactions, stateMachineRecordedTransactionMapping, vaultService, database)
}
fun getFlowFactory(initiatingFlowClass: Class>): InitiatedFlowFactory<*>?
diff --git a/node/src/main/kotlin/net/corda/node/services/identity/PersistentIdentityService.kt b/node/src/main/kotlin/net/corda/node/services/identity/PersistentIdentityService.kt
index 9490751842..f74e890c04 100644
--- a/node/src/main/kotlin/net/corda/node/services/identity/PersistentIdentityService.kt
+++ b/node/src/main/kotlin/net/corda/node/services/identity/PersistentIdentityService.kt
@@ -26,6 +26,7 @@ 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.crypto.x509Certificates
+import net.corda.nodeapi.internal.persistence.CordaPersistence
import net.corda.nodeapi.internal.persistence.NODE_DATABASE_PREFIX
import org.apache.commons.lang.ArrayUtils.EMPTY_BYTE_ARRAY
import java.io.Serializable
@@ -48,6 +49,7 @@ import javax.persistence.Lob
// TODO There is duplicated logic between this and InMemoryIdentityService
@ThreadSafe
class PersistentIdentityService(override val trustRoot: X509Certificate,
+ private val database: CordaPersistence,
caCertificates: List = emptyList()) : SingletonSerializeAsToken(), IdentityServiceInternal {
companion object {
@@ -120,72 +122,82 @@ class PersistentIdentityService(override val trustRoot: X509Certificate,
/** Requires a database transaction. */
fun loadIdentities(identities: Iterable = emptySet(), confidentialIdentities: Iterable = emptySet()) {
- identities.forEach {
- val key = mapToKey(it)
- keyToParties.addWithDuplicatesAllowed(key, it, false)
- principalToParties.addWithDuplicatesAllowed(it.name, key, false)
+ database.transaction {
+ identities.forEach {
+ val key = mapToKey(it)
+ keyToParties.addWithDuplicatesAllowed(key, it, false)
+ principalToParties.addWithDuplicatesAllowed(it.name, key, false)
+ }
+ confidentialIdentities.forEach {
+ principalToParties.addWithDuplicatesAllowed(it.name, mapToKey(it), false)
+ }
+ log.debug("Identities loaded")
}
- confidentialIdentities.forEach {
- principalToParties.addWithDuplicatesAllowed(it.name, mapToKey(it), false)
- }
- log.debug("Identities loaded")
}
@Throws(CertificateExpiredException::class, CertificateNotYetValidException::class, InvalidAlgorithmParameterException::class)
override fun verifyAndRegisterIdentity(identity: PartyAndCertificate): PartyAndCertificate? {
- // Validate the chain first, before we do anything clever with it
- val identityCertChain = identity.certPath.x509Certificates
- try {
- identity.verify(trustAnchor)
- } catch (e: CertPathValidatorException) {
- log.warn(e.localizedMessage)
- log.warn("Path = ")
- identityCertChain.reversed().forEach {
- log.warn(it.subjectX500Principal.toString())
+ return database.transaction {
+
+ // Validate the chain first, before we do anything clever with it
+ val identityCertChain = identity.certPath.x509Certificates
+ try {
+ identity.verify(trustAnchor)
+ } catch (e: CertPathValidatorException) {
+ log.warn(e.localizedMessage)
+ log.warn("Path = ")
+ identityCertChain.reversed().forEach {
+ log.warn(it.subjectX500Principal.toString())
+ }
+ throw e
}
- throw e
- }
- // Ensure we record the first identity of the same name, first
- val wellKnownCert = identityCertChain.single { CertRole.extract(it)?.isWellKnown ?: false }
- if (wellKnownCert != identity.certificate) {
- val idx = identityCertChain.lastIndexOf(wellKnownCert)
- val firstPath = X509Utilities.buildCertPath(identityCertChain.slice(idx until identityCertChain.size))
- verifyAndRegisterIdentity(PartyAndCertificate(firstPath))
- }
+ // Ensure we record the first identity of the same name, first
+ val wellKnownCert = identityCertChain.single { CertRole.extract(it)?.isWellKnown ?: false }
+ if (wellKnownCert != identity.certificate) {
+ val idx = identityCertChain.lastIndexOf(wellKnownCert)
+ val firstPath = X509Utilities.buildCertPath(identityCertChain.slice(idx until identityCertChain.size))
+ verifyAndRegisterIdentity(PartyAndCertificate(firstPath))
+ }
- log.debug { "Registering identity $identity" }
- val key = mapToKey(identity)
- keyToParties.addWithDuplicatesAllowed(key, identity, false)
- // Always keep the first party we registered, as that's the well known identity
- principalToParties.addWithDuplicatesAllowed(identity.name, key, false)
- val parentId = mapToKey(identityCertChain[1].publicKey)
- return keyToParties[parentId]
+ log.debug { "Registering identity $identity" }
+ val key = mapToKey(identity)
+ keyToParties.addWithDuplicatesAllowed(key, identity, false)
+ // Always keep the first party we registered, as that's the well known identity
+ principalToParties.addWithDuplicatesAllowed(identity.name, key, false)
+ val parentId = mapToKey(identityCertChain[1].publicKey)
+ keyToParties[parentId]
+ }
}
- override fun certificateFromKey(owningKey: PublicKey): PartyAndCertificate? = keyToParties[mapToKey(owningKey)]
+ override fun certificateFromKey(owningKey: PublicKey): PartyAndCertificate? = database.transaction { keyToParties[mapToKey(owningKey)] }
+
private fun certificateFromCordaX500Name(name: CordaX500Name): PartyAndCertificate? {
- val partyId = principalToParties[name]
- return if (partyId != null) {
- keyToParties[partyId]
- } else null
+ return database.transaction {
+ val partyId = principalToParties[name]
+ if (partyId != null) {
+ keyToParties[partyId]
+ } else null
+ }
}
// We give the caller a copy of the data set to avoid any locking problems
- override fun getAllIdentities(): Iterable = keyToParties.allPersisted().map { it.second }.asIterable()
+ override fun getAllIdentities(): Iterable = database.transaction { keyToParties.allPersisted().map { it.second }.asIterable() }
override fun partyFromKey(key: PublicKey): Party? = certificateFromKey(key)?.party
override fun wellKnownPartyFromX500Name(name: CordaX500Name): Party? = certificateFromCordaX500Name(name)?.party
override fun wellKnownPartyFromAnonymous(party: AbstractParty): Party? {
- // The original version of this would return the party as-is if it was a Party (rather than AnonymousParty),
- // however that means that we don't verify that we know who owns the key. As such as now enforce turning the key
- // into a party, and from there figure out the well known party.
- val candidate = partyFromKey(party.owningKey)
- // TODO: This should be done via the network map cache, which is the authoritative source of well known identities
- return if (candidate != null) {
- wellKnownPartyFromX500Name(candidate.name)
- } else {
- null
+ return database.transaction {
+ // The original version of this would return the party as-is if it was a Party (rather than AnonymousParty),
+ // however that means that we don't verify that we know who owns the key. As such as now enforce turning the key
+ // into a party, and from there figure out the well known party.
+ val candidate = partyFromKey(party.owningKey)
+ // TODO: This should be done via the network map cache, which is the authoritative source of well known identities
+ if (candidate != null) {
+ wellKnownPartyFromX500Name(candidate.name)
+ } else {
+ null
+ }
}
}
@@ -195,20 +207,23 @@ class PersistentIdentityService(override val trustRoot: X509Certificate,
}
override fun partiesFromName(query: String, exactMatch: Boolean): Set {
- val results = LinkedHashSet()
- for ((x500name, partyId) in principalToParties.allPersisted()) {
- partiesFromName(query, exactMatch, x500name, results, keyToParties[partyId]!!.party)
+ return database.transaction {
+ val results = LinkedHashSet()
+ for ((x500name, partyId) in principalToParties.allPersisted()) {
+ partiesFromName(query, exactMatch, x500name, results, keyToParties[partyId]!!.party)
+ }
+ results
}
- return results
}
@Throws(UnknownAnonymousPartyException::class)
override fun assertOwnership(party: Party, anonymousParty: AnonymousParty) {
- val anonymousIdentity = certificateFromKey(anonymousParty.owningKey) ?:
- throw UnknownAnonymousPartyException("Unknown $anonymousParty")
- val issuingCert = anonymousIdentity.certPath.certificates[1]
- require(issuingCert.publicKey == party.owningKey) {
- "Issuing certificate's public key must match the party key ${party.owningKey.toStringShort()}."
+ database.transaction {
+ val anonymousIdentity = certificateFromKey(anonymousParty.owningKey) ?: throw UnknownAnonymousPartyException("Unknown $anonymousParty")
+ val issuingCert = anonymousIdentity.certPath.certificates[1]
+ require(issuingCert.publicKey == party.owningKey) {
+ "Issuing certificate's public key must match the party key ${party.owningKey.toStringShort()}."
+ }
}
}
}
diff --git a/node/src/main/kotlin/net/corda/node/services/keys/PersistentKeyManagementService.kt b/node/src/main/kotlin/net/corda/node/services/keys/PersistentKeyManagementService.kt
index 3d06fef7e7..a39ff6140b 100644
--- a/node/src/main/kotlin/net/corda/node/services/keys/PersistentKeyManagementService.kt
+++ b/node/src/main/kotlin/net/corda/node/services/keys/PersistentKeyManagementService.kt
@@ -17,6 +17,7 @@ import net.corda.core.node.services.KeyManagementService
import net.corda.core.serialization.SingletonSerializeAsToken
import net.corda.core.utilities.MAX_HASH_HEX_SIZE
import net.corda.node.utilities.AppendOnlyPersistentMap
+import net.corda.nodeapi.internal.persistence.CordaPersistence
import net.corda.nodeapi.internal.persistence.NODE_DATABASE_PREFIX
import org.apache.commons.lang.ArrayUtils.EMPTY_BYTE_ARRAY
import org.bouncycastle.operator.ContentSigner
@@ -37,7 +38,8 @@ import javax.persistence.Lob
* This class needs database transactions to be in-flight during method calls and init.
*/
class PersistentKeyManagementService(val identityService: IdentityService,
- initialKeys: Set) : SingletonSerializeAsToken(), KeyManagementService {
+ initialKeys: Set,
+ private val database: CordaPersistence) : SingletonSerializeAsToken(), KeyManagementService {
@Entity
@javax.persistence.Table(name = "${NODE_DATABASE_PREFIX}our_key_pairs")
@@ -76,17 +78,23 @@ class PersistentKeyManagementService(val identityService: IdentityService,
val keysMap = createKeyMap()
init {
- initialKeys.forEach({ it -> keysMap.addWithDuplicatesAllowed(it.public, it.private) })
+ // TODO this should be in a start function, not in an init block.
+ database.transaction {
+ initialKeys.forEach({ it -> keysMap.addWithDuplicatesAllowed(it.public, it.private) })
+ }
}
- override val keys: Set get() = keysMap.allPersisted().map { it.first }.toSet()
+ override val keys: Set get() = database.transaction { keysMap.allPersisted().map { it.first }.toSet() }
- override fun filterMyKeys(candidateKeys: Iterable): Iterable =
- candidateKeys.filter { keysMap[it] != null }
+ override fun filterMyKeys(candidateKeys: Iterable): Iterable = database.transaction {
+ candidateKeys.filter { keysMap[it] != null }
+ }
override fun freshKey(): PublicKey {
val keyPair = generateKeyPair()
- keysMap[keyPair.public] = keyPair.private
+ database.transaction {
+ keysMap[keyPair.public] = keyPair.private
+ }
return keyPair.public
}
@@ -97,8 +105,10 @@ class PersistentKeyManagementService(val identityService: IdentityService,
//It looks for the PublicKey in the (potentially) CompositeKey that is ours, and then returns the associated PrivateKey to use in signing
private fun getSigningKeyPair(publicKey: PublicKey): KeyPair {
- val pk = publicKey.keys.first { keysMap[it] != null } //TODO here for us to re-write this using an actual query if publicKey.keys.size > 1
- return KeyPair(pk, keysMap[pk]!!)
+ return database.transaction {
+ val pk = publicKey.keys.first { keysMap[it] != null } //TODO here for us to re-write this using an actual query if publicKey.keys.size > 1
+ KeyPair(pk, keysMap[pk]!!)
+ }
}
override fun sign(bytes: ByteArray, publicKey: PublicKey): DigitalSignature.WithKey {
diff --git a/node/src/main/kotlin/net/corda/node/services/messaging/P2PMessagingClient.kt b/node/src/main/kotlin/net/corda/node/services/messaging/P2PMessagingClient.kt
index a9e7e4885d..7ddba49ca3 100644
--- a/node/src/main/kotlin/net/corda/node/services/messaging/P2PMessagingClient.kt
+++ b/node/src/main/kotlin/net/corda/node/services/messaging/P2PMessagingClient.kt
@@ -163,9 +163,7 @@ class P2PMessagingClient(val config: NodeConfiguration,
fun sendMessage(address: String, message: ClientMessage) = producer!!.send(address, message)
}
- private val messagesToRedeliver = database.transaction {
- createMessageToRedeliver()
- }
+ private val messagesToRedeliver = createMessageToRedeliver()
private val scheduledMessageRedeliveries = ConcurrentHashMap>()
diff --git a/node/src/main/kotlin/net/corda/node/services/network/PersistentNetworkMapCache.kt b/node/src/main/kotlin/net/corda/node/services/network/PersistentNetworkMapCache.kt
index edffa8fbe9..e80e998d6f 100644
--- a/node/src/main/kotlin/net/corda/node/services/network/PersistentNetworkMapCache.kt
+++ b/node/src/main/kotlin/net/corda/node/services/network/PersistentNetworkMapCache.kt
@@ -48,8 +48,9 @@ import kotlin.collections.HashSet
class NetworkMapCacheImpl(
networkMapCacheBase: NetworkMapCacheBaseInternal,
- private val identityService: IdentityService
-) : NetworkMapCacheBaseInternal by networkMapCacheBase, NetworkMapCacheInternal {
+ private val identityService: IdentityService,
+ private val database: CordaPersistence
+) : NetworkMapCacheBaseInternal by networkMapCacheBase, NetworkMapCacheInternal, SingletonSerializeAsToken() {
companion object {
private val logger = loggerFor()
}
@@ -72,9 +73,11 @@ class NetworkMapCacheImpl(
}
override fun getNodeByLegalIdentity(party: AbstractParty): NodeInfo? {
- val wellKnownParty = identityService.wellKnownPartyFromAnonymous(party)
- return wellKnownParty?.let {
- getNodesByLegalIdentityKey(it.owningKey).firstOrNull()
+ return database.transaction {
+ val wellKnownParty = identityService.wellKnownPartyFromAnonymous(party)
+ wellKnownParty?.let {
+ getNodesByLegalIdentityKey(it.owningKey).firstOrNull()
+ }
}
}
}
@@ -193,7 +196,7 @@ open class PersistentNetworkMapCache(
override fun track(): DataFeed, MapChange> {
synchronized(_changed) {
- val allInfos = database.transaction { getAllInfos(session) }.map { it.toNodeInfo() }
+ val allInfos = database.transaction { getAllInfos(session).map { it.toNodeInfo() } }
return DataFeed(allInfos, _changed.bufferUntilSubscribed().wrapWithDatabaseTransaction())
}
}
diff --git a/node/src/main/kotlin/net/corda/node/services/persistence/AbstractPartyDescriptor.kt b/node/src/main/kotlin/net/corda/node/services/persistence/AbstractPartyDescriptor.kt
index a32d21505d..96c2bbde32 100644
--- a/node/src/main/kotlin/net/corda/node/services/persistence/AbstractPartyDescriptor.kt
+++ b/node/src/main/kotlin/net/corda/node/services/persistence/AbstractPartyDescriptor.kt
@@ -12,22 +12,23 @@ package net.corda.node.services.persistence
import net.corda.core.identity.AbstractParty
import net.corda.core.identity.CordaX500Name
+import net.corda.core.identity.Party
import net.corda.core.internal.uncheckedCast
-import net.corda.core.node.services.IdentityService
import net.corda.core.utilities.contextLogger
import org.hibernate.type.descriptor.WrapperOptions
import org.hibernate.type.descriptor.java.AbstractTypeDescriptor
import org.hibernate.type.descriptor.java.ImmutableMutabilityPlan
import org.hibernate.type.descriptor.java.MutabilityPlan
-class AbstractPartyDescriptor(private val identityService: IdentityService) : AbstractTypeDescriptor(AbstractParty::class.java) {
+class AbstractPartyDescriptor(private val wellKnownPartyFromX500Name: (CordaX500Name) -> Party?,
+ private val wellKnownPartyFromAnonymous: (AbstractParty) -> Party?) : AbstractTypeDescriptor(AbstractParty::class.java) {
companion object {
private val log = contextLogger()
}
override fun fromString(dbData: String?): AbstractParty? {
return if (dbData != null) {
- val party = identityService.wellKnownPartyFromX500Name(CordaX500Name.parse(dbData))
+ val party = wellKnownPartyFromX500Name(CordaX500Name.parse(dbData))
if (party == null) log.warn("Identity service unable to resolve X500name: $dbData")
party
} else {
@@ -39,7 +40,7 @@ class AbstractPartyDescriptor(private val identityService: IdentityService) : Ab
override fun toString(party: AbstractParty?): String? {
return if (party != null) {
- val partyName = party.nameOrNull() ?: identityService.wellKnownPartyFromAnonymous(party)?.name
+ val partyName = party.nameOrNull() ?: wellKnownPartyFromAnonymous(party)?.name
if (partyName == null) log.warn("Identity service unable to resolve AbstractParty: $party")
partyName.toString()
} else {
diff --git a/node/src/main/kotlin/net/corda/node/services/persistence/AbstractPartyToX500NameAsStringConverter.kt b/node/src/main/kotlin/net/corda/node/services/persistence/AbstractPartyToX500NameAsStringConverter.kt
index 54511e037c..9ac1b4bc43 100644
--- a/node/src/main/kotlin/net/corda/node/services/persistence/AbstractPartyToX500NameAsStringConverter.kt
+++ b/node/src/main/kotlin/net/corda/node/services/persistence/AbstractPartyToX500NameAsStringConverter.kt
@@ -12,7 +12,7 @@ package net.corda.node.services.persistence
import net.corda.core.identity.AbstractParty
import net.corda.core.identity.CordaX500Name
-import net.corda.core.node.services.IdentityService
+import net.corda.core.identity.Party
import net.corda.core.utilities.contextLogger
import javax.persistence.AttributeConverter
import javax.persistence.Converter
@@ -22,14 +22,15 @@ import javax.persistence.Converter
* Completely anonymous parties are stored as null (to preserve privacy).
*/
@Converter(autoApply = true)
-class AbstractPartyToX500NameAsStringConverter(private val identityService: IdentityService) : AttributeConverter {
+class AbstractPartyToX500NameAsStringConverter(private val wellKnownPartyFromX500Name: (CordaX500Name) -> Party?,
+ private val wellKnownPartyFromAnonymous: (AbstractParty) -> Party?) : AttributeConverter {
companion object {
private val log = contextLogger()
}
override fun convertToDatabaseColumn(party: AbstractParty?): String? {
if (party != null) {
- val partyName = identityService.wellKnownPartyFromAnonymous(party)?.toString()
+ val partyName = wellKnownPartyFromAnonymous(party)?.toString()
if (partyName != null) return partyName
log.warn("Identity service unable to resolve AbstractParty: $party")
}
@@ -38,7 +39,7 @@ class AbstractPartyToX500NameAsStringConverter(private val identityService: Iden
override fun convertToEntityAttribute(dbData: String?): AbstractParty? {
if (dbData != null) {
- val party = identityService.wellKnownPartyFromX500Name(CordaX500Name.parse(dbData))
+ val party = wellKnownPartyFromX500Name(CordaX500Name.parse(dbData))
if (party != null) return party
}
return null // non resolvable anonymous parties are stored as nulls
diff --git a/node/src/main/kotlin/net/corda/node/services/persistence/DBTransactionMappingStorage.kt b/node/src/main/kotlin/net/corda/node/services/persistence/DBTransactionMappingStorage.kt
index 55110aa88d..3ac3bd8d52 100644
--- a/node/src/main/kotlin/net/corda/node/services/persistence/DBTransactionMappingStorage.kt
+++ b/node/src/main/kotlin/net/corda/node/services/persistence/DBTransactionMappingStorage.kt
@@ -18,6 +18,7 @@ import net.corda.core.messaging.DataFeed
import net.corda.core.messaging.StateMachineTransactionMapping
import net.corda.node.services.api.StateMachineRecordedTransactionMappingStorage
import net.corda.node.utilities.AppendOnlyPersistentMap
+import net.corda.nodeapi.internal.persistence.CordaPersistence
import net.corda.nodeapi.internal.persistence.NODE_DATABASE_PREFIX
import net.corda.nodeapi.internal.persistence.bufferUntilDatabaseCommit
import net.corda.nodeapi.internal.persistence.wrapWithDatabaseTransaction
@@ -36,7 +37,7 @@ import javax.persistence.Id
* RPC API to correlate transaction creation with flows.
*/
@ThreadSafe
-class DBTransactionMappingStorage : StateMachineRecordedTransactionMappingStorage {
+class DBTransactionMappingStorage(private val database: CordaPersistence) : StateMachineRecordedTransactionMappingStorage {
@Entity
@javax.persistence.Table(name = "${NODE_DATABASE_PREFIX}transaction_mappings")
@@ -73,16 +74,20 @@ class DBTransactionMappingStorage : StateMachineRecordedTransactionMappingStorag
private val concurrentBox = ConcurrentBox(InnerState())
override fun addMapping(stateMachineRunId: StateMachineRunId, transactionId: SecureHash) {
- concurrentBox.concurrent {
- stateMachineTransactionMap.addWithDuplicatesAllowed(transactionId, stateMachineRunId)
- updates.bufferUntilDatabaseCommit().onNext(StateMachineTransactionMapping(stateMachineRunId, transactionId))
+ database.transaction {
+ concurrentBox.concurrent {
+ stateMachineTransactionMap.addWithDuplicatesAllowed(transactionId, stateMachineRunId)
+ updates.bufferUntilDatabaseCommit().onNext(StateMachineTransactionMapping(stateMachineRunId, transactionId))
+ }
}
}
override fun track(): DataFeed, StateMachineTransactionMapping> {
- return concurrentBox.exclusive {
- DataFeed(stateMachineTransactionMap.allPersisted().map { StateMachineTransactionMapping(it.second, it.first) }.toList(),
- updates.bufferUntilSubscribed().wrapWithDatabaseTransaction())
+ return database.transaction {
+ concurrentBox.exclusive {
+ DataFeed(stateMachineTransactionMap.allPersisted().map { StateMachineTransactionMapping(it.second, it.first) }.toList(),
+ updates.bufferUntilSubscribed().wrapWithDatabaseTransaction())
+ }
}
}
}
diff --git a/node/src/main/kotlin/net/corda/node/services/persistence/DBTransactionStorage.kt b/node/src/main/kotlin/net/corda/node/services/persistence/DBTransactionStorage.kt
index cf8a94439d..77e668228f 100644
--- a/node/src/main/kotlin/net/corda/node/services/persistence/DBTransactionStorage.kt
+++ b/node/src/main/kotlin/net/corda/node/services/persistence/DBTransactionStorage.kt
@@ -18,13 +18,18 @@ import net.corda.core.internal.VisibleForTesting
import net.corda.core.internal.bufferUntilSubscribed
import net.corda.core.internal.concurrent.doneFuture
import net.corda.core.messaging.DataFeed
-import net.corda.core.serialization.*
+import net.corda.core.serialization.SerializationDefaults
+import net.corda.core.serialization.SerializedBytes
+import net.corda.core.serialization.SingletonSerializeAsToken
+import net.corda.core.serialization.deserialize
+import net.corda.core.serialization.serialize
import net.corda.core.toFuture
import net.corda.core.transactions.CoreTransaction
import net.corda.core.transactions.SignedTransaction
import net.corda.node.services.api.WritableTransactionStorage
import net.corda.node.utilities.AppendOnlyPersistentMapBase
import net.corda.node.utilities.WeightBasedAppendOnlyPersistentMap
+import net.corda.nodeapi.internal.persistence.CordaPersistence
import net.corda.nodeapi.internal.persistence.NODE_DATABASE_PREFIX
import net.corda.nodeapi.internal.persistence.bufferUntilDatabaseCommit
import net.corda.nodeapi.internal.persistence.wrapWithDatabaseTransaction
@@ -33,14 +38,19 @@ import org.apache.commons.lang.ArrayUtils.EMPTY_BYTE_ARRAY
import rx.Observable
import rx.subjects.PublishSubject
import java.io.Serializable
-import javax.persistence.*
+import javax.persistence.Column
+import javax.persistence.Entity
+import javax.persistence.Id
+import javax.persistence.Lob
+import javax.persistence.Table
// cache value type to just store the immutable bits of a signed transaction plus conversion helpers
typealias TxCacheValue = Pair, List>
+
fun TxCacheValue.toSignedTx() = SignedTransaction(this.first, this.second)
fun SignedTransaction.toTxCacheValue() = TxCacheValue(this.txBits, this.sigs)
-class DBTransactionStorage(cacheSizeBytes: Long) : WritableTransactionStorage, SingletonSerializeAsToken() {
+class DBTransactionStorage(cacheSizeBytes: Long, private val database: CordaPersistence) : WritableTransactionStorage, SingletonSerializeAsToken() {
@Entity
@Table(name = "${NODE_DATABASE_PREFIX}transactions")
@@ -93,35 +103,41 @@ class DBTransactionStorage(cacheSizeBytes: Long) : WritableTransactionStorage, S
private val txStorage = ConcurrentBox(createTransactionsMap(cacheSizeBytes))
- override fun addTransaction(transaction: SignedTransaction): Boolean =
- txStorage.concurrent {
- addWithDuplicatesAllowed(transaction.id, transaction.toTxCacheValue()).apply {
- updatesPublisher.bufferUntilDatabaseCommit().onNext(transaction)
- }
+ override fun addTransaction(transaction: SignedTransaction): Boolean = database.transaction {
+ txStorage.concurrent {
+ addWithDuplicatesAllowed(transaction.id, transaction.toTxCacheValue()).apply {
+ updatesPublisher.bufferUntilDatabaseCommit().onNext(transaction)
}
+ }
+ }
- override fun getTransaction(id: SecureHash): SignedTransaction? = txStorage.content[id]?.toSignedTx()
+ override fun getTransaction(id: SecureHash): SignedTransaction? = database.transaction { txStorage.content[id]?.toSignedTx() }
private val updatesPublisher = PublishSubject.create().toSerialized()
override val updates: Observable = updatesPublisher.wrapWithDatabaseTransaction()
override fun track(): DataFeed, SignedTransaction> {
- return txStorage.exclusive {
- DataFeed(allPersisted().map { it.second.toSignedTx() }.toList(), updates.bufferUntilSubscribed())
+ return database.transaction {
+ txStorage.exclusive {
+ DataFeed(allPersisted().map { it.second.toSignedTx() }.toList(), updates.bufferUntilSubscribed())
+ }
}
}
override fun trackTransaction(id: SecureHash): CordaFuture {
- return txStorage.exclusive {
- val existingTransaction = get(id)
- if (existingTransaction == null) {
- updates.filter { it.id == id }.toFuture()
- } else {
- doneFuture(existingTransaction.toSignedTx())
+ return database.transaction {
+ txStorage.exclusive {
+ val existingTransaction = get(id)
+ if (existingTransaction == null) {
+ updates.filter { it.id == id }.toFuture()
+ } else {
+ doneFuture(existingTransaction.toSignedTx())
+ }
}
}
}
@VisibleForTesting
- val transactions: Iterable get() = txStorage.content.allPersisted().map { it.second.toSignedTx() }.toList()
+ val transactions: Iterable
+ get() = database.transaction { txStorage.content.allPersisted().map { it.second.toSignedTx() }.toList() }
}
diff --git a/node/src/main/kotlin/net/corda/node/services/persistence/NodeAttachmentService.kt b/node/src/main/kotlin/net/corda/node/services/persistence/NodeAttachmentService.kt
index f6abc1a060..0ad2814b31 100644
--- a/node/src/main/kotlin/net/corda/node/services/persistence/NodeAttachmentService.kt
+++ b/node/src/main/kotlin/net/corda/node/services/persistence/NodeAttachmentService.kt
@@ -30,13 +30,18 @@ import net.corda.core.node.services.AttachmentId
import net.corda.core.node.services.AttachmentStorage
import net.corda.core.node.services.vault.AttachmentQueryCriteria
import net.corda.core.node.services.vault.AttachmentSort
-import net.corda.core.serialization.*
+import net.corda.core.serialization.CordaSerializable
+import net.corda.core.serialization.SerializationToken
+import net.corda.core.serialization.SerializeAsToken
+import net.corda.core.serialization.SerializeAsTokenContext
+import net.corda.core.serialization.SingletonSerializeAsToken
import net.corda.core.utilities.contextLogger
import net.corda.node.services.config.NodeConfiguration
import net.corda.node.services.vault.HibernateAttachmentQueryCriteriaParser
import net.corda.node.utilities.NonInvalidatingCache
import net.corda.node.utilities.NonInvalidatingWeightBasedCache
import net.corda.nodeapi.exceptions.DuplicateAttachmentException
+import net.corda.nodeapi.internal.persistence.CordaPersistence
import net.corda.nodeapi.internal.persistence.NODE_DATABASE_PREFIX
import net.corda.nodeapi.internal.persistence.currentDBSession
import net.corda.nodeapi.internal.withContractsInJar
@@ -49,7 +54,16 @@ import java.time.Instant
import java.util.*
import java.util.jar.JarInputStream
import javax.annotation.concurrent.ThreadSafe
-import javax.persistence.*
+import javax.persistence.CollectionTable
+import javax.persistence.Column
+import javax.persistence.ElementCollection
+import javax.persistence.Entity
+import javax.persistence.ForeignKey
+import javax.persistence.Id
+import javax.persistence.Index
+import javax.persistence.JoinColumn
+import javax.persistence.Lob
+import javax.persistence.Table
/**
* Stores attachments using Hibernate to database.
@@ -58,7 +72,8 @@ import javax.persistence.*
class NodeAttachmentService(
metrics: MetricRegistry,
attachmentContentCacheSize: Long = NodeConfiguration.defaultAttachmentContentCacheSize,
- attachmentCacheBound: Long = NodeConfiguration.defaultAttachmentCacheBound
+ attachmentCacheBound: Long = NodeConfiguration.defaultAttachmentCacheBound,
+ private val database: CordaPersistence
) : AttachmentStorage, SingletonSerializeAsToken(
) {
@@ -117,13 +132,15 @@ class NodeAttachmentService(
private val attachmentCount = metrics.counter("Attachments")
- init {
- val session = currentDBSession()
- val criteriaBuilder = session.criteriaBuilder
- val criteriaQuery = criteriaBuilder.createQuery(Long::class.java)
- criteriaQuery.select(criteriaBuilder.count(criteriaQuery.from(NodeAttachmentService.DBAttachment::class.java)))
- val count = session.createQuery(criteriaQuery).singleResult
- attachmentCount.inc(count)
+ fun start() {
+ database.transaction {
+ val session = currentDBSession()
+ val criteriaBuilder = session.criteriaBuilder
+ val criteriaQuery = criteriaBuilder.createQuery(Long::class.java)
+ criteriaQuery.select(criteriaBuilder.count(criteriaQuery.from(NodeAttachmentService.DBAttachment::class.java)))
+ val count = session.createQuery(criteriaQuery).singleResult
+ attachmentCount.inc(count)
+ }
}
@CordaSerializable
@@ -204,10 +221,8 @@ class NodeAttachmentService(
}
override fun toToken(context: SerializeAsTokenContext) = Token(id, checkOnLoad)
-
}
-
// slightly complex 2 level approach to attachment caching:
// On the first level we cache attachment contents loaded from the DB by their key. This is a weight based
// cache (we don't want to waste too much memory on this) and could be evicted quite aggressively. If we fail
@@ -227,17 +242,18 @@ class NodeAttachmentService(
)
private fun loadAttachmentContent(id: SecureHash): Pair? {
- val attachment = currentDBSession().get(NodeAttachmentService.DBAttachment::class.java, id.toString())
- ?: return null
- val attachmentImpl = AttachmentImpl(id, { attachment.content }, checkAttachmentsOnLoad).let {
- val contracts = attachment.contractClassNames
- if (contracts != null && contracts.isNotEmpty()) {
- ContractAttachment(it, contracts.first(), contracts.drop(1).toSet(), attachment.uploader)
- } else {
- it
+ return database.transaction {
+ val attachment = currentDBSession().get(NodeAttachmentService.DBAttachment::class.java, id.toString()) ?: return@transaction null
+ val attachmentImpl = AttachmentImpl(id, { attachment.content }, checkAttachmentsOnLoad).let {
+ val contracts = attachment.contractClassNames
+ if (contracts != null && contracts.isNotEmpty()) {
+ ContractAttachment(it, contracts.first(), contracts.drop(1).toSet(), attachment.uploader)
+ } else {
+ it
+ }
}
+ Pair(attachmentImpl, attachment.content)
}
- return Pair(attachmentImpl, attachment.content)
}
private val attachmentCache = NonInvalidatingCache>(
@@ -273,32 +289,35 @@ class NodeAttachmentService(
return import(jar, uploader, filename)
}
- override fun hasAttachment(attachmentId: AttachmentId): Boolean =
- currentDBSession().find(NodeAttachmentService.DBAttachment::class.java, attachmentId.toString()) != null
+ override fun hasAttachment(attachmentId: AttachmentId): Boolean = database.transaction {
+ currentDBSession().find(NodeAttachmentService.DBAttachment::class.java, attachmentId.toString()) != null
+ }
// TODO: PLT-147: The attachment should be randomised to prevent brute force guessing and thus privacy leaks.
private fun import(jar: InputStream, uploader: String?, filename: String?): AttachmentId {
- return withContractsInJar(jar) { contractClassNames, inputStream ->
- require(inputStream !is JarInputStream)
+ return database.transaction {
+ withContractsInJar(jar) { contractClassNames, inputStream ->
+ require(inputStream !is JarInputStream)
- // Read the file into RAM and then calculate its hash. The attachment must fit into memory.
- // TODO: Switch to a two-phase insert so we can handle attachments larger than RAM.
- // To do this we must pipe stream into the database without knowing its hash, which we will learn only once
- // the insert/upload is complete. We can then query to see if it's a duplicate and if so, erase, and if not
- // set the hash field of the new attachment record.
+ // Read the file into RAM and then calculate its hash. The attachment must fit into memory.
+ // TODO: Switch to a two-phase insert so we can handle attachments larger than RAM.
+ // To do this we must pipe stream into the database without knowing its hash, which we will learn only once
+ // the insert/upload is complete. We can then query to see if it's a duplicate and if so, erase, and if not
+ // set the hash field of the new attachment record.
- val bytes = inputStream.readFully()
- val id = bytes.sha256()
- if (!hasAttachment(id)) {
- checkIsAValidJAR(bytes.inputStream())
- val session = currentDBSession()
- val attachment = NodeAttachmentService.DBAttachment(attId = id.toString(), content = bytes, uploader = uploader, filename = filename, contractClassNames = contractClassNames)
- session.save(attachment)
- attachmentCount.inc()
- log.info("Stored new attachment $id")
- id
- } else {
- throw DuplicateAttachmentException(id.toString())
+ val bytes = inputStream.readFully()
+ val id = bytes.sha256()
+ if (!hasAttachment(id)) {
+ checkIsAValidJAR(bytes.inputStream())
+ val session = currentDBSession()
+ val attachment = NodeAttachmentService.DBAttachment(attId = id.toString(), content = bytes, uploader = uploader, filename = filename, contractClassNames = contractClassNames)
+ session.save(attachment)
+ attachmentCount.inc()
+ log.info("Stored new attachment $id")
+ id
+ } else {
+ throw DuplicateAttachmentException(id.toString())
+ }
}
}
}
@@ -312,24 +331,23 @@ class NodeAttachmentService(
override fun queryAttachments(criteria: AttachmentQueryCriteria, sorting: AttachmentSort?): List {
log.info("Attachment query criteria: $criteria, sorting: $sorting")
- val session = currentDBSession()
- val criteriaBuilder = session.criteriaBuilder
+ return database.transaction {
+ val session = currentDBSession()
+ val criteriaBuilder = session.criteriaBuilder
- val criteriaQuery = criteriaBuilder.createQuery(DBAttachment::class.java)
- val root = criteriaQuery.from(DBAttachment::class.java)
+ val criteriaQuery = criteriaBuilder.createQuery(DBAttachment::class.java)
+ val root = criteriaQuery.from(DBAttachment::class.java)
- val criteriaParser = HibernateAttachmentQueryCriteriaParser(criteriaBuilder, criteriaQuery, root)
+ val criteriaParser = HibernateAttachmentQueryCriteriaParser(criteriaBuilder, criteriaQuery, root)
- // parse criteria and build where predicates
- criteriaParser.parse(criteria, sorting)
+ // parse criteria and build where predicates
+ criteriaParser.parse(criteria, sorting)
- // prepare query for execution
- val query = session.createQuery(criteriaQuery)
+ // prepare query for execution
+ val query = session.createQuery(criteriaQuery)
- // execution
- val results = query.resultList
-
- return results.map { AttachmentId.parse(it.attId) }
+ // execution
+ query.resultList.map { AttachmentId.parse(it.attId) }
+ }
}
-
}
diff --git a/node/src/main/kotlin/net/corda/node/services/statemachine/SingleThreadedStateMachineManager.kt b/node/src/main/kotlin/net/corda/node/services/statemachine/SingleThreadedStateMachineManager.kt
index 18ecaa59f4..a4b370c96b 100644
--- a/node/src/main/kotlin/net/corda/node/services/statemachine/SingleThreadedStateMachineManager.kt
+++ b/node/src/main/kotlin/net/corda/node/services/statemachine/SingleThreadedStateMachineManager.kt
@@ -47,6 +47,7 @@ import net.corda.node.services.statemachine.transitions.StateMachine
import net.corda.node.services.statemachine.transitions.StateMachineConfiguration
import net.corda.node.utilities.AffinityExecutor
import net.corda.nodeapi.internal.persistence.CordaPersistence
+import net.corda.nodeapi.internal.persistence.wrapWithDatabaseTransaction
import net.corda.serialization.internal.SerializeAsTokenContextImpl
import net.corda.serialization.internal.withTokenContext
import org.apache.activemq.artemis.utils.ReusableLatch
@@ -185,7 +186,9 @@ class SingleThreadedStateMachineManager(
*/
override fun track(): DataFeed>, StateMachineManager.Change> {
return mutex.locked {
- DataFeed(flows.values.map { it.fiber.logic }, changesPublisher.bufferUntilSubscribed())
+ database.transaction {
+ DataFeed(flows.values.map { it.fiber.logic }, changesPublisher.bufferUntilSubscribed().wrapWithDatabaseTransaction(database))
+ }
}
}
@@ -482,7 +485,7 @@ class SingleThreadedStateMachineManager(
throw SessionRejectException("${message.initiatorFlowClassName} is not a flow")
}
return serviceHub.getFlowFactory(initiatingFlowClass) ?:
- throw SessionRejectException("$initiatingFlowClass is not registered")
+ throw SessionRejectException("$initiatingFlowClass is not registered")
}
private fun startInitiatedFlow(
diff --git a/node/src/main/kotlin/net/corda/node/services/vault/NodeVaultService.kt b/node/src/main/kotlin/net/corda/node/services/vault/NodeVaultService.kt
index 6b5056acd9..fa3eedda0f 100644
--- a/node/src/main/kotlin/net/corda/node/services/vault/NodeVaultService.kt
+++ b/node/src/main/kotlin/net/corda/node/services/vault/NodeVaultService.kt
@@ -58,7 +58,8 @@ class NodeVaultService(
private val clock: Clock,
private val keyManagementService: KeyManagementService,
private val servicesForResolution: ServicesForResolution,
- hibernateConfig: HibernateConfiguration
+ hibernateConfig: HibernateConfiguration,
+ private val database: CordaPersistence
) : SingletonSerializeAsToken(), VaultServiceInternal {
private companion object {
private val log = contextLogger()
@@ -235,19 +236,23 @@ class NodeVaultService(
}
override fun addNoteToTransaction(txnId: SecureHash, noteText: String) {
- val txnNoteEntity = VaultSchemaV1.VaultTxnNote(txnId.toString(), noteText)
- currentDBSession().save(txnNoteEntity)
+ database.transaction {
+ val txnNoteEntity = VaultSchemaV1.VaultTxnNote(txnId.toString(), noteText)
+ currentDBSession().save(txnNoteEntity)
+ }
}
override fun getTransactionNotes(txnId: SecureHash): Iterable {
- val session = currentDBSession()
- val criteriaBuilder = session.criteriaBuilder
- val criteriaQuery = criteriaBuilder.createQuery(VaultSchemaV1.VaultTxnNote::class.java)
- val vaultStates = criteriaQuery.from(VaultSchemaV1.VaultTxnNote::class.java)
- val txIdPredicate = criteriaBuilder.equal(vaultStates.get(VaultSchemaV1.VaultTxnNote::txId.name), txnId.toString())
- criteriaQuery.where(txIdPredicate)
- val results = session.createQuery(criteriaQuery).resultList
- return results.asIterable().map { it.note }
+ return database.transaction {
+ val session = currentDBSession()
+ val criteriaBuilder = session.criteriaBuilder
+ val criteriaQuery = criteriaBuilder.createQuery(VaultSchemaV1.VaultTxnNote::class.java)
+ val vaultStates = criteriaQuery.from(VaultSchemaV1.VaultTxnNote::class.java)
+ val txIdPredicate = criteriaBuilder.equal(vaultStates.get(VaultSchemaV1.VaultTxnNote::txId.name), txnId.toString())
+ criteriaQuery.where(txIdPredicate)
+ val results = session.createQuery(criteriaQuery).resultList
+ results.asIterable().map { it.note }
+ }
}
@Throws(StatesNotAvailableException::class)
@@ -412,90 +417,94 @@ class NodeVaultService(
@Throws(VaultQueryException::class)
private fun _queryBy(criteria: QueryCriteria, paging: PageSpecification, sorting: Sort, contractStateType: Class, skipPagingChecks: Boolean): Vault.Page {
- log.debug {"Vault Query for contract type: $contractStateType, criteria: $criteria, pagination: $paging, sorting: $sorting" }
- // calculate total results where a page specification has been defined
- var totalStates = -1L
- if (!skipPagingChecks && !paging.isDefault) {
- val count = builder { VaultSchemaV1.VaultStates::recordedTime.count() }
- val countCriteria = QueryCriteria.VaultCustomQueryCriteria(count, Vault.StateStatus.ALL)
- val results = _queryBy(criteria.and(countCriteria), PageSpecification(), Sort(emptyList()), contractStateType, true) // only skip pagination checks for total results count query
- totalStates = results.otherResults.last() as Long
- }
+ log.debug { "Vault Query for contract type: $contractStateType, criteria: $criteria, pagination: $paging, sorting: $sorting" }
+ return database.transaction {
+ // calculate total results where a page specification has been defined
+ var totalStates = -1L
+ if (!skipPagingChecks && !paging.isDefault) {
+ val count = builder { VaultSchemaV1.VaultStates::recordedTime.count() }
+ val countCriteria = QueryCriteria.VaultCustomQueryCriteria(count, Vault.StateStatus.ALL)
+ val results = _queryBy(criteria.and(countCriteria), PageSpecification(), Sort(emptyList()), contractStateType, true) // only skip pagination checks for total results count query
+ totalStates = results.otherResults.last() as Long
+ }
- val session = getSession()
+ val session = getSession()
- val criteriaQuery = criteriaBuilder.createQuery(Tuple::class.java)
- val queryRootVaultStates = criteriaQuery.from(VaultSchemaV1.VaultStates::class.java)
+ val criteriaQuery = criteriaBuilder.createQuery(Tuple::class.java)
+ val queryRootVaultStates = criteriaQuery.from(VaultSchemaV1.VaultStates::class.java)
- // TODO: revisit (use single instance of parser for all queries)
- val criteriaParser = HibernateQueryCriteriaParser(contractStateType, contractStateTypeMappings, criteriaBuilder, criteriaQuery, queryRootVaultStates)
+ // TODO: revisit (use single instance of parser for all queries)
+ val criteriaParser = HibernateQueryCriteriaParser(contractStateType, contractStateTypeMappings, criteriaBuilder, criteriaQuery, queryRootVaultStates)
- // parse criteria and build where predicates
- criteriaParser.parse(criteria, sorting)
+ // parse criteria and build where predicates
+ criteriaParser.parse(criteria, sorting)
- // prepare query for execution
- val query = session.createQuery(criteriaQuery)
+ // prepare query for execution
+ val query = session.createQuery(criteriaQuery)
- // pagination checks
- if (!skipPagingChecks && !paging.isDefault) {
- // pagination
- if (paging.pageNumber < DEFAULT_PAGE_NUM) throw VaultQueryException("Page specification: invalid page number ${paging.pageNumber} [page numbers start from $DEFAULT_PAGE_NUM]")
- if (paging.pageSize < 1) throw VaultQueryException("Page specification: invalid page size ${paging.pageSize} [must be a value between 1 and $MAX_PAGE_SIZE]")
- }
+ // pagination checks
+ if (!skipPagingChecks && !paging.isDefault) {
+ // pagination
+ if (paging.pageNumber < DEFAULT_PAGE_NUM) throw VaultQueryException("Page specification: invalid page number ${paging.pageNumber} [page numbers start from $DEFAULT_PAGE_NUM]")
+ if (paging.pageSize < 1) throw VaultQueryException("Page specification: invalid page size ${paging.pageSize} [must be a value between 1 and $MAX_PAGE_SIZE]")
+ }
- query.firstResult = if (paging.pageNumber > 0) (paging.pageNumber - 1) * paging.pageSize else 0 //some DB don't allow a negative value in SELECT TOP query
- query.maxResults = paging.pageSize + 1 // detection too many results
+ query.firstResult = if (paging.pageNumber > 0) (paging.pageNumber - 1) * paging.pageSize else 0 //some DB don't allow a negative value in SELECT TOP query
+ query.maxResults = paging.pageSize + 1 // detection too many results
- // execution
- val results = query.resultList
+ // execution
+ val results = query.resultList
- // final pagination check (fail-fast on too many results when no pagination specified)
- if (!skipPagingChecks && paging.isDefault && results.size > DEFAULT_PAGE_SIZE)
- throw VaultQueryException("Please specify a `PageSpecification` as there are more results [${results.size}] than the default page size [$DEFAULT_PAGE_SIZE]")
+ // final pagination check (fail-fast on too many results when no pagination specified)
+ if (!skipPagingChecks && paging.isDefault && results.size > DEFAULT_PAGE_SIZE)
+ throw VaultQueryException("Please specify a `PageSpecification` as there are more results [${results.size}] than the default page size [$DEFAULT_PAGE_SIZE]")
- val statesAndRefs: MutableList> = mutableListOf()
- val statesMeta: MutableList = mutableListOf()
- val otherResults: MutableList = mutableListOf()
- val stateRefs = mutableSetOf()
+ val statesAndRefs: MutableList> = mutableListOf()
+ val statesMeta: MutableList = mutableListOf()
+ val otherResults: MutableList = mutableListOf()
+ val stateRefs = mutableSetOf()
- results.asSequence()
- .forEachIndexed { index, result ->
- if (result[0] is VaultSchemaV1.VaultStates) {
- if (!paging.isDefault && index == paging.pageSize) // skip last result if paged
- return@forEachIndexed
- val vaultState = result[0] as VaultSchemaV1.VaultStates
- val stateRef = StateRef(SecureHash.parse(vaultState.stateRef!!.txId!!), vaultState.stateRef!!.index!!)
- stateRefs.add(stateRef)
- statesMeta.add(Vault.StateMetadata(stateRef,
- vaultState.contractStateClassName,
- vaultState.recordedTime,
- vaultState.consumedTime,
- vaultState.stateStatus,
- vaultState.notary,
- vaultState.lockId,
- vaultState.lockUpdateTime))
- } else {
- // TODO: improve typing of returned other results
- log.debug { "OtherResults: ${Arrays.toString(result.toArray())}" }
- otherResults.addAll(result.toArray().asList())
+ results.asSequence()
+ .forEachIndexed { index, result ->
+ if (result[0] is VaultSchemaV1.VaultStates) {
+ if (!paging.isDefault && index == paging.pageSize) // skip last result if paged
+ return@forEachIndexed
+ val vaultState = result[0] as VaultSchemaV1.VaultStates
+ val stateRef = StateRef(SecureHash.parse(vaultState.stateRef!!.txId!!), vaultState.stateRef!!.index!!)
+ stateRefs.add(stateRef)
+ statesMeta.add(Vault.StateMetadata(stateRef,
+ vaultState.contractStateClassName,
+ vaultState.recordedTime,
+ vaultState.consumedTime,
+ vaultState.stateStatus,
+ vaultState.notary,
+ vaultState.lockId,
+ vaultState.lockUpdateTime))
+ } else {
+ // TODO: improve typing of returned other results
+ log.debug { "OtherResults: ${Arrays.toString(result.toArray())}" }
+ otherResults.addAll(result.toArray().asList())
+ }
}
- }
- if (stateRefs.isNotEmpty())
- statesAndRefs.addAll(uncheckedCast(servicesForResolution.loadStates(stateRefs)))
+ if (stateRefs.isNotEmpty())
+ statesAndRefs.addAll(uncheckedCast(servicesForResolution.loadStates(stateRefs)))
- return Vault.Page(states = statesAndRefs, statesMetadata = statesMeta, stateTypes = criteriaParser.stateTypes, totalStatesAvailable = totalStates, otherResults = otherResults)
+ Vault.Page(states = statesAndRefs, statesMetadata = statesMeta, stateTypes = criteriaParser.stateTypes, totalStatesAvailable = totalStates, otherResults = otherResults)
+ }
}
@Throws(VaultQueryException::class)
override fun _trackBy(criteria: QueryCriteria, paging: PageSpecification, sorting: Sort, contractStateType: Class): DataFeed, Vault.Update> {
- return concurrentBox.exclusive {
- val snapshotResults = _queryBy(criteria, paging, sorting, contractStateType)
- val updates: Observable> = uncheckedCast(_updatesPublisher.bufferUntilSubscribed().filter { it.containsType(contractStateType, snapshotResults.stateTypes) })
- DataFeed(snapshotResults, updates)
+ return database.transaction {
+ concurrentBox.exclusive {
+ val snapshotResults = _queryBy(criteria, paging, sorting, contractStateType)
+ val updates: Observable> = uncheckedCast(_updatesPublisher.bufferUntilSubscribed().filter { it.containsType(contractStateType, snapshotResults.stateTypes) })
+ DataFeed(snapshotResults, updates)
+ }
}
}
- private fun getSession() = contextDatabase.currentOrNew().session
+ private fun getSession() = database.currentOrNew().session
/**
* Derive list from existing vault states and then incrementally update using vault observables
*/
diff --git a/node/src/test/kotlin/net/corda/node/CordaRPCOpsImplTest.kt b/node/src/test/kotlin/net/corda/node/CordaRPCOpsImplTest.kt
index 2c7087fc5f..09281fd0f3 100644
--- a/node/src/test/kotlin/net/corda/node/CordaRPCOpsImplTest.kt
+++ b/node/src/test/kotlin/net/corda/node/CordaRPCOpsImplTest.kt
@@ -24,11 +24,8 @@ import net.corda.core.flows.FlowLogic
import net.corda.core.flows.StartableByRPC
import net.corda.core.flows.StateMachineRunId
import net.corda.core.identity.Party
-import net.corda.core.messaging.CordaRPCOps
-import net.corda.core.messaging.StateMachineUpdate
-import net.corda.core.messaging.startFlow
-import net.corda.core.messaging.vaultQueryBy
-import net.corda.core.messaging.vaultTrackBy
+import net.corda.core.internal.uncheckedCast
+import net.corda.core.messaging.*
import net.corda.core.node.services.Vault
import net.corda.core.node.services.queryBy
import net.corda.core.transactions.SignedTransaction
@@ -58,8 +55,7 @@ import net.corda.testing.node.internal.InternalMockNetwork.MockNode
import net.corda.testing.node.internal.InternalMockNodeParameters
import net.corda.testing.node.testActor
import org.apache.commons.io.IOUtils
-import org.assertj.core.api.Assertions.assertThat
-import org.assertj.core.api.Assertions.assertThatExceptionOfType
+import org.assertj.core.api.Assertions.*
import org.junit.After
import org.junit.Assert.assertArrayEquals
import org.junit.Before
@@ -115,9 +111,12 @@ class CordaRPCOpsImplTest {
@Test
fun `cash issue accepted`() {
-
- withPermissions(invokeRpc("vaultTrackBy"), invokeRpc("vaultQueryBy"), invokeRpc(CordaRPCOps::stateMachinesFeed), startFlow()) {
-
+ withPermissions(
+ invokeRpc("vaultTrackBy"),
+ invokeRpc("vaultQueryBy"),
+ invokeRpc(CordaRPCOps::stateMachinesFeed),
+ startFlow()
+ ) {
aliceNode.database.transaction {
stateMachineUpdates = rpc.stateMachinesFeed().updates
vaultTrackCash = rpc.vaultTrackBy().updates
@@ -168,7 +167,6 @@ class CordaRPCOpsImplTest {
@Test
fun `issue and move`() {
-
withPermissions(invokeRpc(CordaRPCOps::stateMachinesFeed),
invokeRpc(CordaRPCOps::internalVerifiedTransactionsFeed),
invokeRpc("vaultTrackBy"),
@@ -278,9 +276,9 @@ class CordaRPCOpsImplTest {
withPermissions(invokeRpc(CordaRPCOps::uploadAttachment), invokeRpc(CordaRPCOps::attachmentExists)) {
val inputJar1 = Thread.currentThread().contextClassLoader.getResourceAsStream(testJar)
val inputJar2 = Thread.currentThread().contextClassLoader.getResourceAsStream(testJar)
- val secureHash1 = rpc.uploadAttachment(inputJar1)
+ rpc.uploadAttachment(inputJar1)
assertThatExceptionOfType(java.nio.file.FileAlreadyExistsException::class.java).isThrownBy {
- val secureHash2 = rpc.uploadAttachment(inputJar2)
+ rpc.uploadAttachment(inputJar2)
}
}
}
@@ -311,13 +309,14 @@ class CordaRPCOpsImplTest {
@Test
fun `kill a stuck flow through RPC`() {
-
- withPermissions(startFlow(), invokeRpc(CordaRPCOps::killFlow), invokeRpc(CordaRPCOps::stateMachinesFeed), invokeRpc(CordaRPCOps::stateMachinesSnapshot)) {
-
+ withPermissions(
+ startFlow(),
+ invokeRpc(CordaRPCOps::killFlow),
+ invokeRpc(CordaRPCOps::stateMachinesFeed),
+ invokeRpc(CordaRPCOps::stateMachinesSnapshot)
+ ) {
val flow = rpc.startFlow(::NewJoinerFlow)
-
val killed = rpc.killFlow(flow.id)
-
assertThat(killed).isTrue()
assertThat(rpc.stateMachinesSnapshot().map { info -> info.id }).doesNotContain(flow.id)
}
@@ -325,13 +324,14 @@ class CordaRPCOpsImplTest {
@Test
fun `kill a waiting flow through RPC`() {
-
- withPermissions(startFlow(), invokeRpc(CordaRPCOps::killFlow), invokeRpc(CordaRPCOps::stateMachinesFeed), invokeRpc(CordaRPCOps::stateMachinesSnapshot)) {
-
+ withPermissions(
+ startFlow(),
+ invokeRpc(CordaRPCOps::killFlow),
+ invokeRpc(CordaRPCOps::stateMachinesFeed),
+ invokeRpc(CordaRPCOps::stateMachinesSnapshot)
+ ) {
val flow = rpc.startFlow(::HopefulFlow, alice)
-
val killed = rpc.killFlow(flow.id)
-
assertThat(killed).isTrue()
assertThat(rpc.stateMachinesSnapshot().map { info -> info.id }).doesNotContain(flow.id)
}
@@ -339,23 +339,26 @@ class CordaRPCOpsImplTest {
@Test
fun `kill a nonexistent flow through RPC`() {
-
withPermissions(invokeRpc(CordaRPCOps::killFlow)) {
-
val nonexistentFlowId = StateMachineRunId.createRandom()
-
val killed = rpc.killFlow(nonexistentFlowId)
-
assertThat(killed).isFalse()
}
}
+ @Test
+ fun `non-ContractState class for the contractStateType param in vault queries`() {
+ val nonContractStateClass: Class = uncheckedCast(Cash::class.java)
+ withPermissions(invokeRpc("vaultTrack"), invokeRpc("vaultQuery")) {
+ assertThatThrownBy { rpc.vaultQuery(nonContractStateClass) }.hasMessageContaining(Cash::class.java.name)
+ assertThatThrownBy { rpc.vaultTrack(nonContractStateClass) }.hasMessageContaining(Cash::class.java.name)
+ }
+ }
+
@StartableByRPC
class NewJoinerFlow : FlowLogic() {
-
@Suspendable
override fun call(): String {
-
logger.info("When can I join you say? Almost there buddy...")
Fiber.currentFiber().join()
return "You'll never get me!"
@@ -364,13 +367,10 @@ class CordaRPCOpsImplTest {
@StartableByRPC
class HopefulFlow(private val party: Party) : FlowLogic() {
-
@Suspendable
override fun call(): String {
-
logger.info("Waiting for a miracle...")
- val miracle = initiateFlow(party).receive().unwrap { it }
- return miracle
+ return initiateFlow(party).receive().unwrap { it }
}
}
@@ -394,17 +394,15 @@ class CordaRPCOpsImplTest {
override fun call(): Void? = null
}
- private fun withPermissions(vararg permissions: String, action: () -> Unit) {
-
+ private inline fun withPermissions(vararg permissions: String, action: () -> Unit) {
val previous = CURRENT_RPC_CONTEXT.get()
try {
- CURRENT_RPC_CONTEXT.set(previous.copy(authorizer =
- buildSubject(previous.principal, permissions.toSet())))
+ CURRENT_RPC_CONTEXT.set(previous.copy(authorizer = buildSubject(previous.principal, permissions.toSet())))
action.invoke()
} finally {
CURRENT_RPC_CONTEXT.set(previous)
}
}
- private fun withoutAnyPermissions(action: () -> Unit) = withPermissions(action = action)
-}
\ No newline at end of file
+ private inline fun withoutAnyPermissions(action: () -> Unit) = withPermissions(action = action)
+}
diff --git a/node/src/test/kotlin/net/corda/node/internal/AbstractNodeTests.kt b/node/src/test/kotlin/net/corda/node/internal/AbstractNodeTests.kt
index 235e2b8076..4bb103edeb 100644
--- a/node/src/test/kotlin/net/corda/node/internal/AbstractNodeTests.kt
+++ b/node/src/test/kotlin/net/corda/node/internal/AbstractNodeTests.kt
@@ -42,7 +42,7 @@ class AbstractNodeTests {
@Test
fun `logVendorString does not leak connection`() {
// Note this test also covers a transaction that CordaPersistence does while it's instantiating:
- val database = configureDatabase(hikariProperties(freshURL()), DatabaseConfig(), rigorousMock())
+ val database = configureDatabase(hikariProperties(freshURL()), DatabaseConfig(), { null }, { null })
val log = mock() // Don't care what happens here.
// Actually 10 is enough to reproduce old code hang, as pool size is 10 and we leaked 9 connections and 1 is in flight:
repeat(100) {
diff --git a/node/src/test/kotlin/net/corda/node/internal/NodeTest.kt b/node/src/test/kotlin/net/corda/node/internal/NodeTest.kt
index 9c2557baf2..c5d9b34e76 100644
--- a/node/src/test/kotlin/net/corda/node/internal/NodeTest.kt
+++ b/node/src/test/kotlin/net/corda/node/internal/NodeTest.kt
@@ -79,7 +79,7 @@ class NodeTest {
doReturn("tsp").whenever(it).trustStorePassword
doReturn("ksp").whenever(it).keyStorePassword
}
- configureDatabase(dataSourceProperties, databaseConfig, rigorousMock()).use { _ ->
+ configureDatabase(dataSourceProperties, databaseConfig, { null }, { null }).use { _ ->
val node = Node(configuration, info, initialiseSerialization = false)
assertEquals(node.generateNodeInfo(), node.generateNodeInfo()) // Node info doesn't change (including the serial)
}
diff --git a/node/src/test/kotlin/net/corda/node/services/ServiceHubConcurrentUsageTest.kt b/node/src/test/kotlin/net/corda/node/services/ServiceHubConcurrentUsageTest.kt
new file mode 100644
index 0000000000..92f35bb37c
--- /dev/null
+++ b/node/src/test/kotlin/net/corda/node/services/ServiceHubConcurrentUsageTest.kt
@@ -0,0 +1,70 @@
+package net.corda.node.services
+
+import co.paralleluniverse.fibers.Suspendable
+import net.corda.core.contracts.ContractState
+import net.corda.core.flows.FinalityFlow
+import net.corda.core.flows.FlowLogic
+import net.corda.core.identity.Party
+import net.corda.core.internal.packageName
+import net.corda.core.node.services.queryBy
+import net.corda.core.transactions.SignedTransaction
+import net.corda.core.transactions.TransactionBuilder
+import net.corda.core.utilities.OpaqueBytes
+import net.corda.core.utilities.getOrThrow
+import net.corda.finance.DOLLARS
+import net.corda.finance.contracts.asset.Cash
+import net.corda.finance.issuedBy
+import net.corda.testing.node.internal.InternalMockNetwork
+import net.corda.testing.node.internal.startFlow
+import org.assertj.core.api.Assertions.assertThat
+import org.junit.After
+import org.junit.Test
+import rx.schedulers.Schedulers
+import java.util.concurrent.CountDownLatch
+
+class ServiceHubConcurrentUsageTest {
+
+ private val mockNet = InternalMockNetwork(listOf(Cash::class.packageName))
+
+ @After
+ fun stopNodes() {
+ mockNet.stopNodes()
+ }
+
+ @Test
+ fun `operations requiring a transaction work from another thread`() {
+
+ val latch = CountDownLatch(1)
+ var successful = false
+ val initiatingFlow = TestFlow(mockNet.defaultNotaryIdentity)
+ val node = mockNet.createPartyNode()
+
+ node.services.validatedTransactions.updates.observeOn(Schedulers.io()).subscribe { _ ->
+ try {
+ node.services.vaultService.queryBy().states
+ successful = true
+ } finally {
+ latch.countDown()
+ }
+ }
+
+ val flow = node.services.startFlow(initiatingFlow)
+ mockNet.runNetwork()
+ flow.resultFuture.getOrThrow()
+ latch.await()
+ assertThat(successful).isTrue()
+ }
+
+ class TestFlow(private val notary: Party) : FlowLogic() {
+
+ @Suspendable
+ override fun call(): SignedTransaction {
+
+ val builder = TransactionBuilder(notary)
+ val issuer = ourIdentity.ref(OpaqueBytes.of(0))
+ Cash().generateIssue(builder, 10.DOLLARS.issuedBy(issuer), ourIdentity, notary)
+ val stx = serviceHub.signInitialTransaction(builder)
+ return subFlow(FinalityFlow(stx))
+ }
+ }
+}
\ No newline at end of file
diff --git a/node/src/test/kotlin/net/corda/node/services/events/NodeSchedulerServiceTest.kt b/node/src/test/kotlin/net/corda/node/services/events/NodeSchedulerServiceTest.kt
index af55b3c7a6..f57a5d6acb 100644
--- a/node/src/test/kotlin/net/corda/node/services/events/NodeSchedulerServiceTest.kt
+++ b/node/src/test/kotlin/net/corda/node/services/events/NodeSchedulerServiceTest.kt
@@ -155,7 +155,7 @@ class MockScheduledFlowRepository : ScheduledFlowRepository {
}
class NodeSchedulerServiceTest : NodeSchedulerServiceTestBase() {
- private val database = configureDatabase(MockServices.makeTestDataSourceProperties(), DatabaseConfig(), rigorousMock())
+ private val database = configureDatabase(MockServices.makeTestDataSourceProperties(), DatabaseConfig(), { null }, { null })
@After
fun closeDatabase() {
@@ -306,7 +306,7 @@ class NodeSchedulerPersistenceTest : NodeSchedulerServiceTestBase() {
@Test
fun `test that correct item is returned`() {
val dataSourceProps = MockServices.makeTestDataSourceProperties()
- val database = configureDatabase(dataSourceProps, databaseConfig, rigorousMock())
+ val database = configureDatabase(dataSourceProps, databaseConfig, { null }, { null })
database.transaction {
val repo = PersistentScheduledFlowRepository(database)
val stateRef = StateRef(SecureHash.randomSHA256(), 0)
@@ -325,7 +325,7 @@ class NodeSchedulerPersistenceTest : NodeSchedulerServiceTestBase() {
val timeInTheFuture = mark + 1.days
val stateRef = StateRef(SecureHash.zeroHash, 0)
- configureDatabase(dataSourceProps, databaseConfig, rigorousMock()).use { database ->
+ configureDatabase(dataSourceProps, databaseConfig, { null }, { null }).use { database ->
val scheduler = database.transaction {
createScheduler(database)
}
@@ -347,7 +347,7 @@ class NodeSchedulerPersistenceTest : NodeSchedulerServiceTestBase() {
transactionStates[stateRef] = transactionStateMock(logicRef, timeInTheFuture)
flows[logicRef] = flowLogic
- configureDatabase(dataSourceProps, DatabaseConfig(), rigorousMock()).use { database ->
+ configureDatabase(dataSourceProps, DatabaseConfig(), { null }, { null }).use { database ->
val newScheduler = database.transaction {
createScheduler(database)
}
@@ -370,7 +370,7 @@ class NodeSchedulerPersistenceTest : NodeSchedulerServiceTestBase() {
val logicRef = rigorousMock()
val flowLogic = rigorousMock>()
- configureDatabase(dataSourceProps, databaseConfig, rigorousMock()).use { database ->
+ configureDatabase(dataSourceProps, databaseConfig, { null }, { null }).use { database ->
val scheduler = database.transaction {
createScheduler(database)
}
diff --git a/node/src/test/kotlin/net/corda/node/services/events/PersistentScheduledFlowRepositoryTest.kt b/node/src/test/kotlin/net/corda/node/services/events/PersistentScheduledFlowRepositoryTest.kt
index d6421757fd..f25588ea74 100644
--- a/node/src/test/kotlin/net/corda/node/services/events/PersistentScheduledFlowRepositoryTest.kt
+++ b/node/src/test/kotlin/net/corda/node/services/events/PersistentScheduledFlowRepositoryTest.kt
@@ -21,7 +21,7 @@ class PersistentScheduledFlowRepositoryTest {
fun `test that earliest item is returned`() {
val laterTime = mark + 1.days
val dataSourceProps = MockServices.makeTestDataSourceProperties()
- val database = configureDatabase(dataSourceProps, databaseConfig, rigorousMock())
+ val database = configureDatabase(dataSourceProps, databaseConfig, { null }, { null })
database.transaction {
val repo = PersistentScheduledFlowRepository(database)
@@ -43,7 +43,7 @@ class PersistentScheduledFlowRepositoryTest {
fun `test that item is rescheduled`() {
val laterTime = mark + 1.days
val dataSourceProps = MockServices.makeTestDataSourceProperties()
- val database = configureDatabase(dataSourceProps, databaseConfig, rigorousMock())
+ val database = configureDatabase(dataSourceProps, databaseConfig, { null }, { null })
database.transaction {
val repo = PersistentScheduledFlowRepository(database)
val stateRef = StateRef(SecureHash.randomSHA256(), 0)
diff --git a/node/src/test/kotlin/net/corda/node/services/identity/PersistentIdentityServiceTests.kt b/node/src/test/kotlin/net/corda/node/services/identity/PersistentIdentityServiceTests.kt
index 6c57ee85ee..f577aaf2c5 100644
--- a/node/src/test/kotlin/net/corda/node/services/identity/PersistentIdentityServiceTests.kt
+++ b/node/src/test/kotlin/net/corda/node/services/identity/PersistentIdentityServiceTests.kt
@@ -24,7 +24,11 @@ import net.corda.nodeapi.internal.crypto.X509Utilities
import net.corda.nodeapi.internal.crypto.x509Certificates
import net.corda.nodeapi.internal.persistence.CordaPersistence
import net.corda.nodeapi.internal.persistence.DatabaseConfig
-import net.corda.testing.core.*
+import net.corda.testing.core.ALICE_NAME
+import net.corda.testing.core.BOB_NAME
+import net.corda.testing.core.SerializationEnvironmentRule
+import net.corda.testing.core.TestIdentity
+import net.corda.testing.core.getTestPartyAndCertificate
import net.corda.testing.internal.DEV_INTERMEDIATE_CA
import net.corda.testing.internal.DEV_ROOT_CA
import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties
@@ -33,6 +37,7 @@ import org.junit.After
import org.junit.Before
import org.junit.Rule
import org.junit.Test
+import java.util.concurrent.atomic.AtomicReference
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith
import kotlin.test.assertNull
@@ -60,8 +65,12 @@ class PersistentIdentityServiceTests {
@Before
fun setup() {
- identityService = PersistentIdentityService(DEV_ROOT_CA.certificate)
- database = configureDatabase(makeTestDataSourceProperties(), DatabaseConfig(runMigration = true), identityService)
+ val identityServiceRef = AtomicReference()
+ // Do all of this in a database transaction so anything that might need a connection has one.
+ database = configureDatabase(makeTestDataSourceProperties(), DatabaseConfig(runMigration = true),
+ { name -> identityServiceRef.get().wellKnownPartyFromX500Name(name) },
+ { party -> identityServiceRef.get().wellKnownPartyFromAnonymous(party) })
+ identityService = PersistentIdentityService(DEV_ROOT_CA.certificate, database).also(identityServiceRef::set)
}
@After
@@ -72,78 +81,55 @@ class PersistentIdentityServiceTests {
@Test
fun `get all identities`() {
// Nothing registered, so empty set
- database.transaction {
- assertNull(identityService.getAllIdentities().firstOrNull())
- }
+ assertNull(identityService.getAllIdentities().firstOrNull())
- database.transaction {
- identityService.verifyAndRegisterIdentity(ALICE_IDENTITY)
- }
+ identityService.verifyAndRegisterIdentity(ALICE_IDENTITY)
var expected = setOf(ALICE)
- var actual = database.transaction {
- identityService.getAllIdentities().map { it.party }.toHashSet()
- }
+ var actual = identityService.getAllIdentities().map { it.party }.toHashSet()
+
assertEquals(expected, actual)
// Add a second party and check we get both back
- database.transaction {
- identityService.verifyAndRegisterIdentity(BOB_IDENTITY)
- }
+ identityService.verifyAndRegisterIdentity(BOB_IDENTITY)
expected = setOf(ALICE, BOB)
- actual = database.transaction {
- identityService.getAllIdentities().map { it.party }.toHashSet()
- }
+ actual = identityService.getAllIdentities().map { it.party }.toHashSet()
assertEquals(expected, actual)
}
@Test
fun `get identity by key`() {
- database.transaction {
- assertNull(identityService.partyFromKey(ALICE_PUBKEY))
- identityService.verifyAndRegisterIdentity(ALICE_IDENTITY)
- assertEquals(ALICE, identityService.partyFromKey(ALICE_PUBKEY))
- assertNull(identityService.partyFromKey(BOB_PUBKEY))
- }
+ assertNull(identityService.partyFromKey(ALICE_PUBKEY))
+ identityService.verifyAndRegisterIdentity(ALICE_IDENTITY)
+ assertEquals(ALICE, identityService.partyFromKey(ALICE_PUBKEY))
+ assertNull(identityService.partyFromKey(BOB_PUBKEY))
}
@Test
fun `get identity by name with no registered identities`() {
- database.transaction {
- assertNull(identityService.wellKnownPartyFromX500Name(ALICE.name))
- }
+ assertNull(identityService.wellKnownPartyFromX500Name(ALICE.name))
}
@Test
fun `get identity by substring match`() {
- database.transaction {
- identityService.verifyAndRegisterIdentity(ALICE_IDENTITY)
- identityService.verifyAndRegisterIdentity(BOB_IDENTITY)
- }
+ identityService.verifyAndRegisterIdentity(ALICE_IDENTITY)
+ identityService.verifyAndRegisterIdentity(BOB_IDENTITY)
val alicente = getTestPartyAndCertificate(CordaX500Name(organisation = "Alicente Worldwide", locality = "London", country = "GB"), generateKeyPair().public)
- database.transaction {
- identityService.verifyAndRegisterIdentity(alicente)
- assertEquals(setOf(ALICE, alicente.party), identityService.partiesFromName("Alice", false))
- assertEquals(setOf(ALICE), identityService.partiesFromName("Alice Corp", true))
- assertEquals(setOf(BOB), identityService.partiesFromName("Bob Plc", true))
- }
+ identityService.verifyAndRegisterIdentity(alicente)
+ assertEquals(setOf(ALICE, alicente.party), identityService.partiesFromName("Alice", false))
+ assertEquals(setOf(ALICE), identityService.partiesFromName("Alice Corp", true))
+ assertEquals(setOf(BOB), identityService.partiesFromName("Bob Plc", true))
}
@Test
fun `get identity by name`() {
val identities = listOf("Organisation A", "Organisation B", "Organisation C")
.map { getTestPartyAndCertificate(CordaX500Name(organisation = it, locality = "London", country = "GB"), generateKeyPair().public) }
- database.transaction {
- assertNull(identityService.wellKnownPartyFromX500Name(identities.first().name))
+ assertNull(identityService.wellKnownPartyFromX500Name(identities.first().name))
+ identities.forEach {
+ identityService.verifyAndRegisterIdentity(it)
}
identities.forEach {
- database.transaction {
- identityService.verifyAndRegisterIdentity(it)
- }
- }
- identities.forEach {
- database.transaction {
- assertEquals(it.party, identityService.wellKnownPartyFromX500Name(it.name))
- }
+ assertEquals(it.party, identityService.wellKnownPartyFromX500Name(it.name))
}
}
@@ -159,9 +145,7 @@ class PersistentIdentityServiceTests {
val txIdentity = AnonymousParty(txKey.public)
assertFailsWith {
- database.transaction {
- identityService.assertOwnership(identity, txIdentity)
- }
+ identityService.assertOwnership(identity, txIdentity)
}
}
@@ -175,25 +159,15 @@ class PersistentIdentityServiceTests {
val (_, bobTxIdentity) = createConfidentialIdentity(ALICE.name)
// Now we have identities, construct the service and let it know about both
- database.transaction {
- identityService.verifyAndRegisterIdentity(alice)
- identityService.verifyAndRegisterIdentity(aliceTxIdentity)
- }
+ identityService.verifyAndRegisterIdentity(alice)
+ identityService.verifyAndRegisterIdentity(aliceTxIdentity)
- var actual = database.transaction {
- identityService.certificateFromKey(aliceTxIdentity.party.owningKey)
- }
+ var actual = identityService.certificateFromKey(aliceTxIdentity.party.owningKey)
assertEquals(aliceTxIdentity, actual!!)
- database.transaction {
- assertNull(identityService.certificateFromKey(bobTxIdentity.party.owningKey))
- }
- database.transaction {
- identityService.verifyAndRegisterIdentity(bobTxIdentity)
- }
- actual = database.transaction {
- identityService.certificateFromKey(bobTxIdentity.party.owningKey)
- }
+ assertNull(identityService.certificateFromKey(bobTxIdentity.party.owningKey))
+ identityService.verifyAndRegisterIdentity(bobTxIdentity)
+ actual = identityService.certificateFromKey(bobTxIdentity.party.owningKey)
assertEquals(bobTxIdentity, actual!!)
}
@@ -206,34 +180,24 @@ class PersistentIdentityServiceTests {
val (alice, anonymousAlice) = createConfidentialIdentity(ALICE.name)
val (bob, anonymousBob) = createConfidentialIdentity(BOB.name)
- database.transaction {
- // Now we have identities, construct the service and let it know about both
- identityService.verifyAndRegisterIdentity(anonymousAlice)
- identityService.verifyAndRegisterIdentity(anonymousBob)
- }
+ // Now we have identities, construct the service and let it know about both
+ identityService.verifyAndRegisterIdentity(anonymousAlice)
+ identityService.verifyAndRegisterIdentity(anonymousBob)
// Verify that paths are verified
- database.transaction {
- identityService.assertOwnership(alice.party, anonymousAlice.party.anonymise())
- identityService.assertOwnership(bob.party, anonymousBob.party.anonymise())
+ identityService.assertOwnership(alice.party, anonymousAlice.party.anonymise())
+ identityService.assertOwnership(bob.party, anonymousBob.party.anonymise())
+ assertFailsWith {
+ identityService.assertOwnership(alice.party, anonymousBob.party.anonymise())
}
assertFailsWith {
- database.transaction {
- identityService.assertOwnership(alice.party, anonymousBob.party.anonymise())
- }
- }
- assertFailsWith {
- database.transaction {
- identityService.assertOwnership(bob.party, anonymousAlice.party.anonymise())
- }
+ identityService.assertOwnership(bob.party, anonymousAlice.party.anonymise())
}
assertFailsWith {
val owningKey = DEV_INTERMEDIATE_CA.certificate.publicKey
- database.transaction {
- val subject = CordaX500Name.build(DEV_INTERMEDIATE_CA.certificate.subjectX500Principal)
- identityService.assertOwnership(Party(subject, owningKey), anonymousAlice.party.anonymise())
- }
+ val subject = CordaX500Name.build(DEV_INTERMEDIATE_CA.certificate.subjectX500Principal)
+ identityService.assertOwnership(Party(subject, owningKey), anonymousAlice.party.anonymise())
}
}
@@ -242,33 +206,24 @@ class PersistentIdentityServiceTests {
val (alice, anonymousAlice) = createConfidentialIdentity(ALICE.name)
val (bob, anonymousBob) = createConfidentialIdentity(BOB.name)
- database.transaction {
- // Register well known identities
- identityService.verifyAndRegisterIdentity(alice)
- identityService.verifyAndRegisterIdentity(bob)
- // Register an anonymous identities
- identityService.verifyAndRegisterIdentity(anonymousAlice)
- identityService.verifyAndRegisterIdentity(anonymousBob)
- }
+ // Register well known identities
+ identityService.verifyAndRegisterIdentity(alice)
+ identityService.verifyAndRegisterIdentity(bob)
+ // Register an anonymous identities
+ identityService.verifyAndRegisterIdentity(anonymousAlice)
+ identityService.verifyAndRegisterIdentity(anonymousBob)
// Create new identity service mounted onto same DB
- val newPersistentIdentityService = database.transaction {
- PersistentIdentityService(DEV_ROOT_CA.certificate)
- }
+ val newPersistentIdentityService = PersistentIdentityService(DEV_ROOT_CA.certificate, database)
- database.transaction {
- newPersistentIdentityService.assertOwnership(alice.party, anonymousAlice.party.anonymise())
- newPersistentIdentityService.assertOwnership(bob.party, anonymousBob.party.anonymise())
- }
+ newPersistentIdentityService.assertOwnership(alice.party, anonymousAlice.party.anonymise())
+ newPersistentIdentityService.assertOwnership(bob.party, anonymousBob.party.anonymise())
+
+ val aliceParent = newPersistentIdentityService.wellKnownPartyFromAnonymous(anonymousAlice.party.anonymise())
- val aliceParent = database.transaction {
- newPersistentIdentityService.wellKnownPartyFromAnonymous(anonymousAlice.party.anonymise())
- }
assertEquals(alice.party, aliceParent!!)
- val bobReload = database.transaction {
- newPersistentIdentityService.certificateFromKey(anonymousBob.party.owningKey)
- }
+ val bobReload = newPersistentIdentityService.certificateFromKey(anonymousBob.party.owningKey)
assertEquals(anonymousBob, bobReload!!)
}
diff --git a/node/src/test/kotlin/net/corda/node/services/messaging/ArtemisMessagingTest.kt b/node/src/test/kotlin/net/corda/node/services/messaging/ArtemisMessagingTest.kt
index 5c7263609f..f4d7eb3133 100644
--- a/node/src/test/kotlin/net/corda/node/services/messaging/ArtemisMessagingTest.kt
+++ b/node/src/test/kotlin/net/corda/node/services/messaging/ArtemisMessagingTest.kt
@@ -87,8 +87,8 @@ class ArtemisMessagingTest {
}
LogHelper.setLevel(PersistentUniquenessProvider::class)
- database = configureDatabase(makeTestDataSourceProperties(), DatabaseConfig(runMigration = true), rigorousMock())
- networkMapCache = NetworkMapCacheImpl(PersistentNetworkMapCache(database, emptyList()).start(), rigorousMock())
+ database = configureDatabase(makeTestDataSourceProperties(), DatabaseConfig(runMigration = true), { null }, { null })
+ networkMapCache = NetworkMapCacheImpl(PersistentNetworkMapCache(database, emptyList()).start(), rigorousMock(), database)
}
@After
diff --git a/node/src/test/kotlin/net/corda/node/services/persistence/AppendOnlyPersistentMapTest.kt b/node/src/test/kotlin/net/corda/node/services/persistence/AppendOnlyPersistentMapTest.kt
index b10042dcc6..8d0b0800e5 100644
--- a/node/src/test/kotlin/net/corda/node/services/persistence/AppendOnlyPersistentMapTest.kt
+++ b/node/src/test/kotlin/net/corda/node/services/persistence/AppendOnlyPersistentMapTest.kt
@@ -6,7 +6,7 @@ import net.corda.node.internal.configureDatabase
import net.corda.node.services.schema.NodeSchemaService
import net.corda.node.utilities.AppendOnlyPersistentMap
import net.corda.nodeapi.internal.persistence.DatabaseConfig
-import net.corda.testing.internal.rigorousMock
+
import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties
import org.junit.After
import org.junit.Assert.*
@@ -58,7 +58,7 @@ class AppendOnlyPersistentMapTest(var scenario: Scenario) {
private val database = configureDatabase(makeTestDataSourceProperties(),
DatabaseConfig(),
- rigorousMock(),
+ { null }, { null },
NodeSchemaService(setOf(MappedSchema(AppendOnlyPersistentMapTest::class.java, 1, listOf(PersistentMapEntry::class.java)))))
@After
diff --git a/node/src/test/kotlin/net/corda/node/services/persistence/DBCheckpointStorageTests.kt b/node/src/test/kotlin/net/corda/node/services/persistence/DBCheckpointStorageTests.kt
index 147c7712ac..b3df9c6c06 100644
--- a/node/src/test/kotlin/net/corda/node/services/persistence/DBCheckpointStorageTests.kt
+++ b/node/src/test/kotlin/net/corda/node/services/persistence/DBCheckpointStorageTests.kt
@@ -27,7 +27,6 @@ import net.corda.testing.core.ALICE_NAME
import net.corda.testing.core.SerializationEnvironmentRule
import net.corda.testing.core.TestIdentity
import net.corda.testing.internal.LogHelper
-import net.corda.testing.internal.rigorousMock
import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties
import org.assertj.core.api.Assertions.assertThat
import org.junit.After
@@ -55,7 +54,7 @@ class DBCheckpointStorageTests {
@Before
fun setUp() {
LogHelper.setLevel(PersistentUniquenessProvider::class)
- database = configureDatabase(makeTestDataSourceProperties(), DatabaseConfig(runMigration = true), rigorousMock())
+ database = configureDatabase(makeTestDataSourceProperties(), DatabaseConfig(runMigration = true), { null }, { null })
newCheckpointStorage()
}
diff --git a/node/src/test/kotlin/net/corda/node/services/persistence/DBTransactionStorageTests.kt b/node/src/test/kotlin/net/corda/node/services/persistence/DBTransactionStorageTests.kt
index 258017a121..3d83853133 100644
--- a/node/src/test/kotlin/net/corda/node/services/persistence/DBTransactionStorageTests.kt
+++ b/node/src/test/kotlin/net/corda/node/services/persistence/DBTransactionStorageTests.kt
@@ -18,14 +18,17 @@ import net.corda.core.crypto.TransactionSignature
import net.corda.core.toFuture
import net.corda.core.transactions.SignedTransaction
import net.corda.core.transactions.WireTransaction
-import net.corda.node.services.transactions.PersistentUniquenessProvider
import net.corda.node.internal.configureDatabase
import net.corda.node.services.config.NodeConfiguration
+import net.corda.node.services.transactions.PersistentUniquenessProvider
import net.corda.nodeapi.internal.persistence.CordaPersistence
import net.corda.nodeapi.internal.persistence.DatabaseConfig
-import net.corda.testing.core.*
+import net.corda.testing.core.ALICE_NAME
+import net.corda.testing.core.DUMMY_NOTARY_NAME
+import net.corda.testing.core.SerializationEnvironmentRule
+import net.corda.testing.core.TestIdentity
+import net.corda.testing.core.dummyCommand
import net.corda.testing.internal.LogHelper
-import net.corda.testing.internal.rigorousMock
import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties
import org.assertj.core.api.Assertions.assertThat
import org.junit.After
@@ -51,7 +54,7 @@ class DBTransactionStorageTests {
fun setUp() {
LogHelper.setLevel(PersistentUniquenessProvider::class)
val dataSourceProps = makeTestDataSourceProperties()
- database = configureDatabase(dataSourceProps, DatabaseConfig(runMigration = true), rigorousMock())
+ database = configureDatabase(dataSourceProps, DatabaseConfig(runMigration = true), { null }, { null })
newTransactionStorage()
}
@@ -63,51 +66,33 @@ class DBTransactionStorageTests {
@Test
fun `empty store`() {
- database.transaction {
- assertThat(transactionStorage.getTransaction(newTransaction().id)).isNull()
- }
- database.transaction {
- assertThat(transactionStorage.transactions).isEmpty()
- }
+ assertThat(transactionStorage.getTransaction(newTransaction().id)).isNull()
+ assertThat(transactionStorage.transactions).isEmpty()
newTransactionStorage()
- database.transaction {
- assertThat(transactionStorage.transactions).isEmpty()
- }
+ assertThat(transactionStorage.transactions).isEmpty()
}
@Test
fun `one transaction`() {
val transaction = newTransaction()
- database.transaction {
- transactionStorage.addTransaction(transaction)
- }
+ transactionStorage.addTransaction(transaction)
assertTransactionIsRetrievable(transaction)
- database.transaction {
- assertThat(transactionStorage.transactions).containsExactly(transaction)
- }
+ assertThat(transactionStorage.transactions).containsExactly(transaction)
newTransactionStorage()
assertTransactionIsRetrievable(transaction)
- database.transaction {
- assertThat(transactionStorage.transactions).containsExactly(transaction)
- }
+ assertThat(transactionStorage.transactions).containsExactly(transaction)
}
@Test
fun `two transactions across restart`() {
val firstTransaction = newTransaction()
val secondTransaction = newTransaction()
- database.transaction {
- transactionStorage.addTransaction(firstTransaction)
- }
+ transactionStorage.addTransaction(firstTransaction)
newTransactionStorage()
- database.transaction {
- transactionStorage.addTransaction(secondTransaction)
- }
+ transactionStorage.addTransaction(secondTransaction)
assertTransactionIsRetrievable(firstTransaction)
assertTransactionIsRetrievable(secondTransaction)
- database.transaction {
- assertThat(transactionStorage.transactions).containsOnly(firstTransaction, secondTransaction)
- }
+ assertThat(transactionStorage.transactions).containsOnly(firstTransaction, secondTransaction)
}
@Test
@@ -120,24 +105,18 @@ class DBTransactionStorageTests {
rollback()
}
- database.transaction {
- assertThat(transactionStorage.transactions).isEmpty()
- }
+ assertThat(transactionStorage.transactions).isEmpty()
}
@Test
fun `two transactions in same DB transaction scope`() {
val firstTransaction = newTransaction()
val secondTransaction = newTransaction()
- database.transaction {
- transactionStorage.addTransaction(firstTransaction)
- transactionStorage.addTransaction(secondTransaction)
- }
+ transactionStorage.addTransaction(firstTransaction)
+ transactionStorage.addTransaction(secondTransaction)
assertTransactionIsRetrievable(firstTransaction)
assertTransactionIsRetrievable(secondTransaction)
- database.transaction {
- assertThat(transactionStorage.transactions).containsOnly(firstTransaction, secondTransaction)
- }
+ assertThat(transactionStorage.transactions).containsOnly(firstTransaction, secondTransaction)
}
@Test
@@ -148,36 +127,29 @@ class DBTransactionStorageTests {
transactionStorage.addTransaction(firstTransaction)
}
assertTransactionIsRetrievable(firstTransaction)
- database.transaction {
- assertThat(transactionStorage.transactions).containsOnly(firstTransaction)
- }
+ assertThat(transactionStorage.transactions).containsOnly(firstTransaction)
}
@Test
fun `transaction saved twice in two DB transaction scopes`() {
val firstTransaction = newTransaction()
val secondTransaction = newTransaction()
- database.transaction {
- transactionStorage.addTransaction(firstTransaction)
- }
+
+ transactionStorage.addTransaction(firstTransaction)
database.transaction {
transactionStorage.addTransaction(secondTransaction)
transactionStorage.addTransaction(firstTransaction)
}
assertTransactionIsRetrievable(firstTransaction)
- database.transaction {
- assertThat(transactionStorage.transactions).containsOnly(firstTransaction, secondTransaction)
- }
+ assertThat(transactionStorage.transactions).containsOnly(firstTransaction, secondTransaction)
}
@Test
fun `updates are fired`() {
val future = transactionStorage.updates.toFuture()
val expected = newTransaction()
- database.transaction {
- transactionStorage.addTransaction(expected)
- }
+ transactionStorage.addTransaction(expected)
val actual = future.get(1, TimeUnit.SECONDS)
assertEquals(expected, actual)
}
@@ -196,15 +168,11 @@ class DBTransactionStorageTests {
}
private fun newTransactionStorage(cacheSizeBytesOverride: Long? = null) {
- database.transaction {
- transactionStorage = DBTransactionStorage(cacheSizeBytesOverride ?: NodeConfiguration.defaultTransactionCacheSize)
- }
+ transactionStorage = DBTransactionStorage(cacheSizeBytesOverride ?: NodeConfiguration.defaultTransactionCacheSize, database)
}
private fun assertTransactionIsRetrievable(transaction: SignedTransaction) {
- database.transaction {
- assertThat(transactionStorage.getTransaction(transaction.id)).isEqualTo(transaction)
- }
+ assertThat(transactionStorage.getTransaction(transaction.id)).isEqualTo(transaction)
}
private fun newTransaction(): SignedTransaction {
diff --git a/node/src/test/kotlin/net/corda/node/services/persistence/HibernateConfigurationTest.kt b/node/src/test/kotlin/net/corda/node/services/persistence/HibernateConfigurationTest.kt
index 8918f6c125..80ba084244 100644
--- a/node/src/test/kotlin/net/corda/node/services/persistence/HibernateConfigurationTest.kt
+++ b/node/src/test/kotlin/net/corda/node/services/persistence/HibernateConfigurationTest.kt
@@ -122,7 +122,7 @@ class HibernateConfigurationTest {
}
}
val schemaService = NodeSchemaService(extraSchemas = setOf(CashSchemaV1, SampleCashSchemaV2, SampleCashSchemaV3, DummyLinearStateSchemaV1, DummyLinearStateSchemaV2, DummyDealStateSchemaV1 ))
- database = configureDatabase(dataSourceProps, DatabaseConfig(runMigration = true), identityService, schemaService)
+ database = configureDatabase(dataSourceProps, DatabaseConfig(), identityService::wellKnownPartyFromX500Name, identityService::wellKnownPartyFromAnonymous, schemaService)
database.transaction {
hibernateConfig = database.hibernateConfig
@@ -130,7 +130,7 @@ class HibernateConfigurationTest {
services = object : MockServices(cordappPackages, BOB_NAME, rigorousMock().also {
doNothing().whenever(it).justVerifyAndRegisterIdentity(argThat { name == BOB_NAME })
}, generateKeyPair(), dummyNotary.keyPair) {
- override val vaultService = NodeVaultService(Clock.systemUTC(), keyManagementService, servicesForResolution, hibernateConfig)
+ override val vaultService = NodeVaultService(Clock.systemUTC(), keyManagementService, servicesForResolution, hibernateConfig, database)
override fun recordTransactions(statesToRecord: StatesToRecord, txs: Iterable) {
for (stx in txs) {
(validatedTransactions as WritableTransactionStorage).addTransaction(stx)
diff --git a/node/src/test/kotlin/net/corda/node/services/persistence/NodeAttachmentStorageTest.kt b/node/src/test/kotlin/net/corda/node/services/persistence/NodeAttachmentStorageTest.kt
index 1f2c3dc38d..6bb7517973 100644
--- a/node/src/test/kotlin/net/corda/node/services/persistence/NodeAttachmentStorageTest.kt
+++ b/node/src/test/kotlin/net/corda/node/services/persistence/NodeAttachmentStorageTest.kt
@@ -15,7 +15,11 @@ import com.google.common.jimfs.Configuration
import com.google.common.jimfs.Jimfs
import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.sha256
-import net.corda.core.internal.*
+import net.corda.core.internal.read
+import net.corda.core.internal.readAll
+import net.corda.core.internal.readFully
+import net.corda.core.internal.write
+import net.corda.core.internal.writeLines
import net.corda.core.node.services.vault.AttachmentQueryCriteria
import net.corda.core.node.services.vault.AttachmentSort
import net.corda.core.node.services.vault.Builder
@@ -25,7 +29,6 @@ import net.corda.node.services.transactions.PersistentUniquenessProvider
import net.corda.nodeapi.internal.persistence.CordaPersistence
import net.corda.nodeapi.internal.persistence.DatabaseConfig
import net.corda.testing.internal.LogHelper
-import net.corda.testing.internal.rigorousMock
import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties
import org.junit.After
import org.junit.Before
@@ -45,14 +48,16 @@ class NodeAttachmentStorageTest {
// Use an in memory file system for testing attachment storage.
private lateinit var fs: FileSystem
private lateinit var database: CordaPersistence
+ private lateinit var storage: NodeAttachmentService
@Before
fun setUp() {
LogHelper.setLevel(PersistentUniquenessProvider::class)
val dataSourceProperties = makeTestDataSourceProperties()
- database = configureDatabase(dataSourceProperties, DatabaseConfig(runMigration = true), rigorousMock())
+ database = configureDatabase(dataSourceProperties, DatabaseConfig(runMigration = true), { null }, { null })
fs = Jimfs.newFileSystem(Configuration.unix())
+ storage = NodeAttachmentService(MetricRegistry(), database = database).also { it.start() }
}
@After
@@ -62,28 +67,25 @@ class NodeAttachmentStorageTest {
@Test
fun `insert and retrieve`() {
- val (testJar,expectedHash) = makeTestJar()
+ val (testJar, expectedHash) = makeTestJar()
- database.transaction {
- val storage = NodeAttachmentService(MetricRegistry())
- val id = testJar.read { storage.importAttachment(it) }
- assertEquals(expectedHash, id)
+ val id = testJar.read { storage.importAttachment(it) }
+ assertEquals(expectedHash, id)
- assertNull(storage.openAttachment(SecureHash.randomSHA256()))
- val stream = storage.openAttachment(expectedHash)!!.openAsJAR()
- val e1 = stream.nextJarEntry!!
- assertEquals("test1.txt", e1.name)
- assertEquals(stream.readBytes().toString(StandardCharsets.UTF_8), "This is some useful content")
- val e2 = stream.nextJarEntry!!
- assertEquals("test2.txt", e2.name)
- assertEquals(stream.readBytes().toString(StandardCharsets.UTF_8), "Some more useful content")
+ assertNull(storage.openAttachment(SecureHash.randomSHA256()))
+ val stream = storage.openAttachment(expectedHash)!!.openAsJAR()
+ val e1 = stream.nextJarEntry!!
+ assertEquals("test1.txt", e1.name)
+ assertEquals(stream.readBytes().toString(StandardCharsets.UTF_8), "This is some useful content")
+ val e2 = stream.nextJarEntry!!
+ assertEquals("test2.txt", e2.name)
+ assertEquals(stream.readBytes().toString(StandardCharsets.UTF_8), "Some more useful content")
- stream.close()
+ stream.close()
- storage.openAttachment(id)!!.openAsJAR().use {
- it.nextJarEntry
- it.readBytes()
- }
+ storage.openAttachment(id)!!.openAsJAR().use {
+ it.nextJarEntry
+ it.readBytes()
}
}
@@ -92,138 +94,121 @@ class NodeAttachmentStorageTest {
val (testJar, expectedHash) = makeTestJar()
val (jarB, hashB) = makeTestJar(listOf(Pair("file", "content")))
- database.transaction {
- val storage = NodeAttachmentService(MetricRegistry())
- val id = testJar.read { storage.importAttachment(it) }
- assertEquals(expectedHash, id)
+ val id = testJar.read { storage.importAttachment(it) }
+ assertEquals(expectedHash, id)
- assertNull(storage.openAttachment(hashB))
- val stream = storage.openAttachment(expectedHash)!!.openAsJAR()
- val e1 = stream.nextJarEntry!!
- assertEquals("test1.txt", e1.name)
- assertEquals(stream.readBytes().toString(StandardCharsets.UTF_8), "This is some useful content")
- val e2 = stream.nextJarEntry!!
- assertEquals("test2.txt", e2.name)
- assertEquals(stream.readBytes().toString(StandardCharsets.UTF_8), "Some more useful content")
+ assertNull(storage.openAttachment(hashB))
+ val stream = storage.openAttachment(expectedHash)!!.openAsJAR()
+ val e1 = stream.nextJarEntry!!
+ assertEquals("test1.txt", e1.name)
+ assertEquals(stream.readBytes().toString(StandardCharsets.UTF_8), "This is some useful content")
+ val e2 = stream.nextJarEntry!!
+ assertEquals("test2.txt", e2.name)
+ assertEquals(stream.readBytes().toString(StandardCharsets.UTF_8), "Some more useful content")
- stream.close()
+ stream.close()
- val idB = jarB.read { storage.importAttachment(it) }
- assertEquals(hashB, idB)
+ val idB = jarB.read { storage.importAttachment(it) }
+ assertEquals(hashB, idB)
- storage.openAttachment(id)!!.openAsJAR().use {
- it.nextJarEntry
- it.readBytes()
- }
+ storage.openAttachment(id)!!.openAsJAR().use {
+ it.nextJarEntry
+ it.readBytes()
+ }
- storage.openAttachment(idB)!!.openAsJAR().use {
- it.nextJarEntry
- it.readBytes()
- }
+ storage.openAttachment(idB)!!.openAsJAR().use {
+ it.nextJarEntry
+ it.readBytes()
}
}
-
@Test
fun `metadata can be used to search`() {
val (jarA, _) = makeTestJar()
- val (jarB, hashB) = makeTestJar(listOf(Pair("file","content")))
- val (jarC, hashC) = makeTestJar(listOf(Pair("magic_file","magic_content_puff")))
+ val (jarB, hashB) = makeTestJar(listOf(Pair("file", "content")))
+ val (jarC, hashC) = makeTestJar(listOf(Pair("magic_file", "magic_content_puff")))
- database.transaction {
- val storage = NodeAttachmentService(MetricRegistry())
+ jarA.read { storage.importAttachment(it) }
+ jarB.read { storage.importAttachment(it, "uploaderB", "fileB.zip") }
+ jarC.read { storage.importAttachment(it, "uploaderC", "fileC.zip") }
- jarA.read { storage.importAttachment(it) }
- jarB.read { storage.importAttachment(it, "uploaderB", "fileB.zip") }
- jarC.read { storage.importAttachment(it, "uploaderC", "fileC.zip") }
-
- assertEquals(
+ assertEquals(
listOf(hashB),
- storage.queryAttachments( AttachmentQueryCriteria.AttachmentsQueryCriteria( Builder.equal("uploaderB")))
- )
+ storage.queryAttachments(AttachmentQueryCriteria.AttachmentsQueryCriteria(Builder.equal("uploaderB")))
+ )
- assertEquals (
- listOf(hashB, hashC),
- storage.queryAttachments( AttachmentQueryCriteria.AttachmentsQueryCriteria( Builder.like ("%uploader%")))
- )
- }
+ assertEquals(
+ listOf(hashB, hashC),
+ storage.queryAttachments(AttachmentQueryCriteria.AttachmentsQueryCriteria(Builder.like("%uploader%")))
+ )
}
@Test
fun `sorting and compound conditions work`() {
- val (jarA,hashA) = makeTestJar(listOf(Pair("a","a")))
- val (jarB,hashB) = makeTestJar(listOf(Pair("b","b")))
- val (jarC,hashC) = makeTestJar(listOf(Pair("c","c")))
+ val (jarA, hashA) = makeTestJar(listOf(Pair("a", "a")))
+ val (jarB, hashB) = makeTestJar(listOf(Pair("b", "b")))
+ val (jarC, hashC) = makeTestJar(listOf(Pair("c", "c")))
- fun uploaderCondition(s:String) = AttachmentQueryCriteria.AttachmentsQueryCriteria(uploaderCondition = Builder.equal(s))
- fun filenamerCondition(s:String) = AttachmentQueryCriteria.AttachmentsQueryCriteria(filenameCondition = Builder.equal(s))
+ fun uploaderCondition(s: String) = AttachmentQueryCriteria.AttachmentsQueryCriteria(uploaderCondition = Builder.equal(s))
+ fun filenamerCondition(s: String) = AttachmentQueryCriteria.AttachmentsQueryCriteria(filenameCondition = Builder.equal(s))
fun filenameSort(direction: Sort.Direction) = AttachmentSort(listOf(AttachmentSort.AttachmentSortColumn(AttachmentSort.AttachmentSortAttribute.FILENAME, direction)))
- database.transaction {
- val storage = NodeAttachmentService(MetricRegistry())
+ jarA.read { storage.importAttachment(it, "complexA", "archiveA.zip") }
+ jarB.read { storage.importAttachment(it, "complexB", "archiveB.zip") }
+ jarC.read { storage.importAttachment(it, "complexC", "archiveC.zip") }
- jarA.read { storage.importAttachment(it, "complexA", "archiveA.zip") }
- jarB.read { storage.importAttachment(it, "complexB", "archiveB.zip") }
- jarC.read { storage.importAttachment(it, "complexC", "archiveC.zip") }
+ // DOCSTART AttachmentQueryExample1
- // DOCSTART AttachmentQueryExample1
-
- assertEquals(
+ assertEquals(
emptyList(),
storage.queryAttachments(
- AttachmentQueryCriteria.AttachmentsQueryCriteria(uploaderCondition = Builder.equal("complexA"))
- .and(AttachmentQueryCriteria.AttachmentsQueryCriteria(uploaderCondition = Builder.equal("complexB"))))
- )
+ AttachmentQueryCriteria.AttachmentsQueryCriteria(uploaderCondition = Builder.equal("complexA"))
+ .and(AttachmentQueryCriteria.AttachmentsQueryCriteria(uploaderCondition = Builder.equal("complexB"))))
+ )
- assertEquals(
+ assertEquals(
listOf(hashA, hashB),
storage.queryAttachments(
- AttachmentQueryCriteria.AttachmentsQueryCriteria(uploaderCondition = Builder.equal("complexA"))
- .or(AttachmentQueryCriteria.AttachmentsQueryCriteria(uploaderCondition = Builder.equal("complexB"))))
- )
+ AttachmentQueryCriteria.AttachmentsQueryCriteria(uploaderCondition = Builder.equal("complexA"))
+ .or(AttachmentQueryCriteria.AttachmentsQueryCriteria(uploaderCondition = Builder.equal("complexB"))))
+ )
- val complexCondition =
- (uploaderCondition("complexB").and(filenamerCondition("archiveB.zip"))).or(filenamerCondition("archiveC.zip"))
+ val complexCondition =
+ (uploaderCondition("complexB").and(filenamerCondition("archiveB.zip"))).or(filenamerCondition("archiveC.zip"))
- // DOCEND AttachmentQueryExample1
+ // DOCEND AttachmentQueryExample1
- assertEquals (
- listOf(hashB, hashC),
+ assertEquals(
+ listOf(hashB, hashC),
storage.queryAttachments(complexCondition, sorting = filenameSort(Sort.Direction.ASC))
- )
- assertEquals (
- listOf(hashC, hashB),
+ )
+ assertEquals(
+ listOf(hashC, hashB),
storage.queryAttachments(complexCondition, sorting = filenameSort(Sort.Direction.DESC))
- )
-
- }
+ )
}
@Ignore("We need to be able to restart nodes - make importing attachments idempotent?")
@Test
fun `duplicates not allowed`() {
- val (testJar,_) = makeTestJar()
- database.transaction {
- val storage = NodeAttachmentService(MetricRegistry())
+ val (testJar, _) = makeTestJar()
+ testJar.read {
+ storage.importAttachment(it)
+ }
+ assertFailsWith {
testJar.read {
storage.importAttachment(it)
}
- assertFailsWith {
- testJar.read {
- storage.importAttachment(it)
- }
- }
}
}
@Test
fun `corrupt entry throws exception`() {
- val (testJar,_) = makeTestJar()
+ val (testJar, _) = makeTestJar()
val id = database.transaction {
- val storage = NodeAttachmentService(MetricRegistry())
val id = testJar.read { storage.importAttachment(it) }
// Corrupt the file in the store.
@@ -234,37 +219,31 @@ class NodeAttachmentStorageTest {
session.merge(corruptAttachment)
id
}
- database.transaction {
- val storage = NodeAttachmentService(MetricRegistry())
- val e = assertFailsWith {
- storage.openAttachment(id)!!.open().readFully()
- }
- assertEquals(e.expected, id)
+ val e = assertFailsWith {
+ storage.openAttachment(id)!!.open().readFully()
+ }
+ assertEquals(e.expected, id)
- // But if we skip around and read a single entry, no exception is thrown.
- storage.openAttachment(id)!!.openAsJAR().use {
- it.nextJarEntry
- it.readBytes()
- }
+ // But if we skip around and read a single entry, no exception is thrown.
+ storage.openAttachment(id)!!.openAsJAR().use {
+ it.nextJarEntry
+ it.readBytes()
}
}
@Test
fun `non jar rejected`() {
- database.transaction {
- val storage = NodeAttachmentService(MetricRegistry())
- val path = fs.getPath("notajar")
- path.writeLines(listOf("Hey", "there!"))
- path.read {
- assertFailsWith("either empty or not a JAR") {
- storage.importAttachment(it)
- }
+ val path = fs.getPath("notajar")
+ path.writeLines(listOf("Hey", "there!"))
+ path.read {
+ assertFailsWith("either empty or not a JAR") {
+ storage.importAttachment(it)
}
}
}
private var counter = 0
- private fun makeTestJar(extraEntries: List> = emptyList()): Pair {
+ private fun makeTestJar(extraEntries: List> = emptyList()): Pair {
counter++
val file = fs.getPath("$counter.jar")
file.write {
diff --git a/node/src/test/kotlin/net/corda/node/services/persistence/TransactionCallbackTest.kt b/node/src/test/kotlin/net/corda/node/services/persistence/TransactionCallbackTest.kt
index cd46899392..7231c3afed 100644
--- a/node/src/test/kotlin/net/corda/node/services/persistence/TransactionCallbackTest.kt
+++ b/node/src/test/kotlin/net/corda/node/services/persistence/TransactionCallbackTest.kt
@@ -2,7 +2,6 @@ package net.corda.node.services.persistence
import net.corda.node.internal.configureDatabase
import net.corda.nodeapi.internal.persistence.DatabaseConfig
-import net.corda.testing.internal.rigorousMock
import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties
import org.junit.After
import org.junit.Test
@@ -10,7 +9,7 @@ import kotlin.test.assertEquals
class TransactionCallbackTest {
- private val database = configureDatabase(makeTestDataSourceProperties(), DatabaseConfig(), rigorousMock())
+ private val database = configureDatabase(makeTestDataSourceProperties(), DatabaseConfig(), { null }, { null })
@After
fun closeDatabase() {
diff --git a/node/src/test/kotlin/net/corda/node/services/schema/HibernateObserverTests.kt b/node/src/test/kotlin/net/corda/node/services/schema/HibernateObserverTests.kt
index 00c29ef524..e338f199dd 100644
--- a/node/src/test/kotlin/net/corda/node/services/schema/HibernateObserverTests.kt
+++ b/node/src/test/kotlin/net/corda/node/services/schema/HibernateObserverTests.kt
@@ -29,7 +29,6 @@ import net.corda.testing.internal.LogHelper
import net.corda.testing.core.TestIdentity
import net.corda.testing.contracts.DummyContract
import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties
-import net.corda.testing.internal.rigorousMock
import org.junit.After
import org.junit.Before
import org.junit.Test
@@ -78,7 +77,7 @@ class HibernateObserverTests {
return parent
}
}
- val database = configureDatabase(makeTestDataSourceProperties(), DatabaseConfig(runMigration = true), rigorousMock(), schemaService)
+ val database = configureDatabase(makeTestDataSourceProperties(), DatabaseConfig(runMigration = true), { null }, { null }, schemaService)
HibernateObserver.install(rawUpdatesPublisher, database.hibernateConfig, schemaService)
database.transaction {
val MEGA_CORP = TestIdentity(CordaX500Name("MegaCorp", "London", "GB")).party
diff --git a/node/src/test/kotlin/net/corda/node/services/transactions/PersistentUniquenessProviderTests.kt b/node/src/test/kotlin/net/corda/node/services/transactions/PersistentUniquenessProviderTests.kt
index 7e62930b50..c1d8cc05ff 100644
--- a/node/src/test/kotlin/net/corda/node/services/transactions/PersistentUniquenessProviderTests.kt
+++ b/node/src/test/kotlin/net/corda/node/services/transactions/PersistentUniquenessProviderTests.kt
@@ -26,7 +26,6 @@ import net.corda.testing.core.SerializationEnvironmentRule
import net.corda.testing.core.TestIdentity
import net.corda.testing.core.generateStateRef
import net.corda.testing.internal.LogHelper
-import net.corda.testing.internal.rigorousMock
import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties
import org.junit.After
import org.junit.Before
@@ -49,7 +48,7 @@ class PersistentUniquenessProviderTests {
@Before
fun setUp() {
LogHelper.setLevel(PersistentUniquenessProvider::class)
- database = configureDatabase(makeTestDataSourceProperties(), DatabaseConfig(runMigration = true), rigorousMock(), NodeSchemaService(includeNotarySchemas = true))
+ database = configureDatabase(makeTestDataSourceProperties(), DatabaseConfig(runMigration = true), { null }, { null }, NodeSchemaService(includeNotarySchemas = true))
}
@After
diff --git a/node/src/test/kotlin/net/corda/node/services/transactions/RaftTransactionCommitLogTests.kt b/node/src/test/kotlin/net/corda/node/services/transactions/RaftTransactionCommitLogTests.kt
index 815eef3daa..0df56851ea 100644
--- a/node/src/test/kotlin/net/corda/node/services/transactions/RaftTransactionCommitLogTests.kt
+++ b/node/src/test/kotlin/net/corda/node/services/transactions/RaftTransactionCommitLogTests.kt
@@ -32,7 +32,6 @@ import net.corda.testing.core.ALICE_NAME
import net.corda.testing.core.SerializationEnvironmentRule
import net.corda.testing.core.freeLocalHostAndPort
import net.corda.testing.internal.LogHelper
-import net.corda.testing.internal.rigorousMock
import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties
import org.hamcrest.Matchers.instanceOf
import org.junit.*
@@ -159,7 +158,7 @@ class RaftTransactionCommitLogTests {
private fun createReplica(myAddress: NetworkHostAndPort, clusterAddress: NetworkHostAndPort? = null): CompletableFuture {
val storage = Storage.builder().withStorageLevel(StorageLevel.MEMORY).build()
val address = Address(myAddress.host, myAddress.port)
- val database = configureDatabase(makeTestDataSourceProperties(), DatabaseConfig(runMigration = true), rigorousMock(), NodeSchemaService(includeNotarySchemas = true))
+ val database = configureDatabase(makeTestDataSourceProperties(), DatabaseConfig(runMigration = true), { null }, { null }, NodeSchemaService(includeNotarySchemas = true))
databases.add(database)
val stateMachineFactory = { RaftTransactionCommitLog(database, Clock.systemUTC(), RaftUniquenessProvider.Companion::createMap) }
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 8054711fd1..72ef3f6346 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
@@ -195,7 +195,7 @@ abstract class VaultQueryTestsBase : VaultQueryParties {
@Ignore
@Test
fun createPersistentTestDb() {
- val database = configureDatabase(makePersistentDataSourceProperties(), DatabaseConfig(runMigration = true), identitySvc)
+ val database = configureDatabase(makePersistentDataSourceProperties(), DatabaseConfig(runMigration = true), identitySvc::wellKnownPartyFromX500Name, identitySvc::wellKnownPartyFromAnonymous)
setUpDb(database, 5000)
database.close()
diff --git a/node/src/test/kotlin/net/corda/node/services/vault/VaultSoftLockManagerTest.kt b/node/src/test/kotlin/net/corda/node/services/vault/VaultSoftLockManagerTest.kt
index e4698681d6..b234afa306 100644
--- a/node/src/test/kotlin/net/corda/node/services/vault/VaultSoftLockManagerTest.kt
+++ b/node/src/test/kotlin/net/corda/node/services/vault/VaultSoftLockManagerTest.kt
@@ -35,6 +35,7 @@ import net.corda.core.utilities.getOrThrow
import net.corda.core.utilities.unwrap
import net.corda.node.internal.InitiatedFlowFactory
import net.corda.node.services.api.VaultServiceInternal
+import net.corda.nodeapi.internal.persistence.CordaPersistence
import net.corda.nodeapi.internal.persistence.HibernateConfiguration
import net.corda.testing.core.singleIdentity
import net.corda.testing.internal.rigorousMock
@@ -93,9 +94,9 @@ class VaultSoftLockManagerTest {
}
private val mockNet = InternalMockNetwork(cordappPackages = listOf(ContractImpl::class.packageName), defaultFactory = { args ->
object : InternalMockNetwork.MockNode(args) {
- override fun makeVaultService(keyManagementService: KeyManagementService, services: ServicesForResolution, hibernateConfig: HibernateConfiguration): VaultServiceInternal {
+ override fun makeVaultService(keyManagementService: KeyManagementService, services: ServicesForResolution, hibernateConfig: HibernateConfiguration, database: CordaPersistence): VaultServiceInternal {
val node = this
- val realVault = super.makeVaultService(keyManagementService, services, hibernateConfig)
+ val realVault = super.makeVaultService(keyManagementService, services, hibernateConfig, database)
return object : VaultServiceInternal by realVault {
override fun softLockRelease(lockId: UUID, stateRefs: NonEmptySet?) {
// Should be called before flow is removed
diff --git a/node/src/test/kotlin/net/corda/node/utilities/ObservablesTests.kt b/node/src/test/kotlin/net/corda/node/utilities/ObservablesTests.kt
index acaf457c9b..1e470c2576 100644
--- a/node/src/test/kotlin/net/corda/node/utilities/ObservablesTests.kt
+++ b/node/src/test/kotlin/net/corda/node/utilities/ObservablesTests.kt
@@ -15,7 +15,6 @@ import net.corda.core.internal.bufferUntilSubscribed
import net.corda.core.internal.tee
import net.corda.node.internal.configureDatabase
import net.corda.nodeapi.internal.persistence.*
-import net.corda.testing.internal.rigorousMock
import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties
import org.assertj.core.api.Assertions.assertThat
import org.junit.After
@@ -31,7 +30,7 @@ class ObservablesTests {
private val toBeClosed = mutableListOf()
private fun createDatabase(): CordaPersistence {
- val database = configureDatabase(makeTestDataSourceProperties(), DatabaseConfig(runMigration = true), rigorousMock())
+ val database = configureDatabase(makeTestDataSourceProperties(), DatabaseConfig(runMigration = true), { null }, { null })
toBeClosed += database
return database
}
diff --git a/node/src/test/kotlin/net/corda/node/utilities/PersistentMapTests.kt b/node/src/test/kotlin/net/corda/node/utilities/PersistentMapTests.kt
index c5add42c8a..671e9c1fc8 100644
--- a/node/src/test/kotlin/net/corda/node/utilities/PersistentMapTests.kt
+++ b/node/src/test/kotlin/net/corda/node/utilities/PersistentMapTests.kt
@@ -4,14 +4,13 @@ import net.corda.core.crypto.SecureHash
import net.corda.node.internal.configureDatabase
import net.corda.node.services.upgrade.ContractUpgradeServiceImpl
import net.corda.nodeapi.internal.persistence.DatabaseConfig
-import net.corda.testing.internal.rigorousMock
import net.corda.testing.node.MockServices
import org.junit.Test
import kotlin.test.assertEquals
class PersistentMapTests {
private val databaseConfig = DatabaseConfig()
- private val database get() = configureDatabase(dataSourceProps, databaseConfig, rigorousMock())
+ private val database get() = configureDatabase(dataSourceProps, databaseConfig, { null }, { null })
private val dataSourceProps = MockServices.makeTestDataSourceProperties()
//create a test map using an existing db table
diff --git a/samples/irs-demo/cordapp/src/test/kotlin/net/corda/irs/api/NodeInterestRatesTest.kt b/samples/irs-demo/cordapp/src/test/kotlin/net/corda/irs/api/NodeInterestRatesTest.kt
index fa68ac46be..69c4d8c6a8 100644
--- a/samples/irs-demo/cordapp/src/test/kotlin/net/corda/irs/api/NodeInterestRatesTest.kt
+++ b/samples/irs-demo/cordapp/src/test/kotlin/net/corda/irs/api/NodeInterestRatesTest.kt
@@ -77,7 +77,7 @@ class NodeInterestRatesTest {
@Before
fun setUp() {
- database = configureDatabase(makeTestDataSourceProperties(), DatabaseConfig(runMigration = true), rigorousMock())
+ database = configureDatabase(makeTestDataSourceProperties(), DatabaseConfig(runMigration = true), { null }, { null })
database.transaction {
oracle = createMockCordaService(services, NodeInterestRates::Oracle)
oracle.knownFixes = TEST_DATA
diff --git a/samples/network-visualiser/src/main/kotlin/net/corda/netmap/simulation/IRSSimulation.kt b/samples/network-visualiser/src/main/kotlin/net/corda/netmap/simulation/IRSSimulation.kt
index a50f00e430..fffcb7ff23 100644
--- a/samples/network-visualiser/src/main/kotlin/net/corda/netmap/simulation/IRSSimulation.kt
+++ b/samples/network-visualiser/src/main/kotlin/net/corda/netmap/simulation/IRSSimulation.kt
@@ -104,10 +104,7 @@ class IRSSimulation(networkSendManuallyPumped: Boolean, runAsync: Boolean, laten
val node1 = banks[i].started!!
val node2 = banks[j].started!!
- val swaps =
- node1.database.transaction {
- node1.services.vaultService.queryBy().states
- }
+ val swaps = node1.services.vaultService.queryBy().states
val theDealRef: StateAndRef = swaps.single()
// Do we have any more days left in this deal's lifetime? If not, return.
diff --git a/samples/network-visualiser/src/main/kotlin/net/corda/netmap/simulation/Simulation.kt b/samples/network-visualiser/src/main/kotlin/net/corda/netmap/simulation/Simulation.kt
index 83be14b1d2..d94e57e868 100644
--- a/samples/network-visualiser/src/main/kotlin/net/corda/netmap/simulation/Simulation.kt
+++ b/samples/network-visualiser/src/main/kotlin/net/corda/netmap/simulation/Simulation.kt
@@ -73,9 +73,7 @@ abstract class Simulation(val networkSendManuallyPumped: Boolean,
registerInitiatedFlow(NodeInterestRates.FixQueryHandler::class.java)
registerInitiatedFlow(NodeInterestRates.FixSignHandler::class.java)
javaClass.classLoader.getResourceAsStream("net/corda/irs/simulation/example.rates.txt").use {
- database.transaction {
- services.cordaService(NodeInterestRates.Oracle::class.java).uploadFixes(it.reader().readText())
- }
+ services.cordaService(NodeInterestRates.Oracle::class.java).uploadFixes(it.reader().readText())
}
}
}
diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/ArraySerializer.kt b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/ArraySerializer.kt
index 01f28ca5ce..7e3132c3b8 100644
--- a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/ArraySerializer.kt
+++ b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/ArraySerializer.kt
@@ -11,6 +11,10 @@
package net.corda.serialization.internal.amqp
import net.corda.core.serialization.SerializationContext
+import net.corda.core.utilities.contextLogger
+import net.corda.core.utilities.debug
+import net.corda.core.utilities.loggerFor
+import net.corda.core.utilities.trace
import org.apache.qpid.proton.amqp.Symbol
import org.apache.qpid.proton.codec.Data
import java.io.NotSerializableException
@@ -21,30 +25,48 @@ import java.lang.reflect.Type
*/
open class ArraySerializer(override val type: Type, factory: SerializerFactory) : AMQPSerializer {
companion object {
- fun make(type: Type, factory: SerializerFactory) = when (type) {
- Array::class.java -> CharArraySerializer(factory)
- else -> ArraySerializer(type, factory)
+ fun make(type: Type, factory: SerializerFactory) : AMQPSerializer {
+ contextLogger().debug { "Making array serializer, typename=${type.typeName}" }
+ return when (type) {
+ Array::class.java -> CharArraySerializer(factory)
+ else -> ArraySerializer(type, factory)
+ }
}
}
+ private val logger = loggerFor()
+
// because this might be an array of array of primitives (to any recursive depth) and
// because we care that the lowest type is unboxed we can't rely on the inbuilt type
// id to generate it properly (it will always return [[[Ljava.lang.type -> type[][][]
// for example).
//
- // We *need* to retain knowledge for AMQP deserialization weather that lowest primitive
+ // We *need* to retain knowledge for AMQP deserialization whether that lowest primitive
// was boxed or unboxed so just infer it recursively.
- private fun calcTypeName(type: Type): String =
- if (type.componentType().isArray()) {
- val typeName = calcTypeName(type.componentType()); "$typeName[]"
+ private fun calcTypeName(type: Type, debugOffset : Int = 0): String {
+ logger.trace { "${"".padStart(debugOffset, ' ') } calcTypeName - ${type.typeName}" }
+
+ return if (type.componentType().isArray()) {
+ // Special case handler for primitive byte arrays. This is needed because we can silently
+ // coerce a byte[] to our own binary type. Normally, if the component type was itself an
+ // array we'd keep walking down the chain but for byte[] stop here and use binary instead
+ val typeName = if (SerializerFactory.isPrimitive(type.componentType())) {
+ SerializerFactory.nameForType(type.componentType())
} else {
- val arrayType = if (type.asClass()!!.componentType.isPrimitive) "[p]" else "[]"
- "${type.componentType().typeName}$arrayType"
+ calcTypeName(type.componentType(), debugOffset + 4)
}
+ "$typeName[]"
+ } else {
+ val arrayType = if (type.asClass()!!.componentType.isPrimitive) "[p]" else "[]"
+ "${type.componentType().typeName}$arrayType"
+ }
+ }
+
override val typeDescriptor: Symbol by lazy {
Symbol.valueOf("$DESCRIPTOR_DOMAIN:${factory.fingerPrinter.fingerprint(type)}")
}
+
internal val elementType: Type by lazy { type.componentType() }
internal open val typeName by lazy { calcTypeName(type) }
diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/SerializerFactory.kt b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/SerializerFactory.kt
index 418a2428c4..382c530210 100644
--- a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/SerializerFactory.kt
+++ b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/SerializerFactory.kt
@@ -14,6 +14,7 @@ import com.google.common.primitives.Primitives
import com.google.common.reflect.TypeResolver
import net.corda.core.internal.uncheckedCast
import net.corda.core.serialization.ClassWhitelist
+import net.corda.core.utilities.debug
import net.corda.core.utilities.loggerFor
import net.corda.core.utilities.trace
import net.corda.serialization.internal.carpenter.*
@@ -255,7 +256,7 @@ open class SerializerFactory(
private fun processSchema(schemaAndDescriptor: FactorySchemaAndDescriptor, sentinel: Boolean = false) {
val metaSchema = CarpenterMetaSchema.newInstance()
for (typeNotation in schemaAndDescriptor.schemas.schema.types) {
- logger.trace("descriptor=${schemaAndDescriptor.typeDescriptor}, typeNotation=${typeNotation.name}")
+ logger.debug { "descriptor=${schemaAndDescriptor.typeDescriptor}, typeNotation=${typeNotation.name}" }
try {
val serialiser = processSchemaEntry(typeNotation)
// if we just successfully built a serializer for the type but the type fingerprint
diff --git a/serialization/src/test/kotlin/net/corda/serialization/internal/amqp/DeserializeSimpleTypesTests.kt b/serialization/src/test/kotlin/net/corda/serialization/internal/amqp/DeserializeSimpleTypesTests.kt
index 3137d92fb3..bd9f074525 100644
--- a/serialization/src/test/kotlin/net/corda/serialization/internal/amqp/DeserializeSimpleTypesTests.kt
+++ b/serialization/src/test/kotlin/net/corda/serialization/internal/amqp/DeserializeSimpleTypesTests.kt
@@ -10,10 +10,7 @@
package net.corda.serialization.internal.amqp
-import net.corda.serialization.internal.amqp.testutils.TestSerializationOutput
-import net.corda.serialization.internal.amqp.testutils.deserialize
-import net.corda.serialization.internal.amqp.testutils.serialize
-import net.corda.serialization.internal.amqp.testutils.testDefaultFactoryNoEvolution
+import net.corda.serialization.internal.amqp.testutils.*
import org.junit.Test
import kotlin.test.assertEquals
@@ -263,6 +260,14 @@ class DeserializeSimpleTypesTests {
assertEquals(c.c[0], deserializedC.c[0])
assertEquals(c.c[1], deserializedC.c[1])
assertEquals(c.c[2], deserializedC.c[2])
+
+ val di = DeserializationInput(sf2)
+ val deserializedC2 = di.deserialize(serialisedC)
+
+ assertEquals(c.c.size, deserializedC2.c.size)
+ assertEquals(c.c[0], deserializedC2.c[0])
+ assertEquals(c.c[1], deserializedC2.c[1])
+ assertEquals(c.c[2], deserializedC2.c[2])
}
@Test
@@ -504,7 +509,33 @@ class DeserializeSimpleTypesTests {
assertEquals(3, da2.a?.a?.b)
assertEquals(2, da2.a?.a?.a?.b)
assertEquals(1, da2.a?.a?.a?.a?.b)
-
}
+
+ // Replicates CORDA-1545
+ @Test
+ fun arrayOfByteArray() {
+ class A(val a : Array)
+
+ val ba1 = ByteArray(3)
+ ba1[0] = 0b0001; ba1[1] = 0b0101; ba1[2] = 0b1111
+
+ val ba2 = ByteArray(3)
+ ba2[0] = 0b1000; ba2[1] = 0b1100; ba2[2] = 0b1110
+
+ val a = A(arrayOf(ba1, ba2))
+
+ val serializedA = TestSerializationOutput(VERBOSE, sf1).serializeAndReturnSchema(a)
+
+ serializedA.schema.types.forEach {
+ println(it)
+ }
+
+ // This not throwing is the point of the test
+ DeserializationInput(sf1).deserialize(serializedA.obj)
+
+ // This not throwing is the point of the test
+ DeserializationInput(sf2).deserialize(serializedA.obj)
+ }
+
}
diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/InMemoryMessagingNetwork.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/InMemoryMessagingNetwork.kt
index c77f72a821..615931eb29 100644
--- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/InMemoryMessagingNetwork.kt
+++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/InMemoryMessagingNetwork.kt
@@ -28,7 +28,12 @@ import net.corda.core.utilities.ByteSequence
import net.corda.core.utilities.OpaqueBytes
import net.corda.core.utilities.getOrThrow
import net.corda.core.utilities.trace
-import net.corda.node.services.messaging.*
+import net.corda.node.services.messaging.DeduplicationHandler
+import net.corda.node.services.messaging.Message
+import net.corda.node.services.messaging.MessageHandler
+import net.corda.node.services.messaging.MessageHandlerRegistration
+import net.corda.node.services.messaging.MessagingService
+import net.corda.node.services.messaging.ReceivedMessage
import net.corda.node.services.statemachine.DeduplicationId
import net.corda.node.services.statemachine.ExternalEvent
import net.corda.node.services.statemachine.SenderDeduplicationId
@@ -47,6 +52,7 @@ import java.util.concurrent.LinkedBlockingQueue
import javax.annotation.concurrent.ThreadSafe
import kotlin.concurrent.schedule
import kotlin.concurrent.thread
+import kotlin.jvm.Volatile
/**
* An in-memory network allows you to manufacture [InternalMockMessagingService]s for a set of participants. Each
@@ -137,8 +143,7 @@ class InMemoryMessagingNetwork private constructor(
id: Int,
executor: AffinityExecutor,
notaryService: PartyAndCertificate?,
- description: CordaX500Name = CordaX500Name(organisation = "In memory node $id", locality = "London", country = "UK"),
- database: CordaPersistence)
+ description: CordaX500Name = CordaX500Name(organisation = "In memory node $id", locality = "London", country = "UK"))
: InternalMockMessagingService {
val peerHandle = PeerHandle(id, description)
peersMapping[peerHandle.name] = peerHandle // Assume that the same name - the same entity in MockNetwork.
@@ -146,7 +151,7 @@ class InMemoryMessagingNetwork private constructor(
val serviceHandles = notaryService?.let { listOf(DistributedServiceHandle(it.party)) }
?: emptyList() //TODO only notary can be distributed?
synchronized(this) {
- val node = InMemoryMessaging(manuallyPumped, peerHandle, executor, database)
+ val node = InMemoryMessaging(manuallyPumped, peerHandle, executor)
val oldNode = handleEndpointMap.put(peerHandle, node)
if (oldNode != null) {
node.inheritPendingRedelivery(oldNode)
@@ -364,8 +369,7 @@ class InMemoryMessagingNetwork private constructor(
@ThreadSafe
private inner class InMemoryMessaging(private val manuallyPumped: Boolean,
private val peerHandle: PeerHandle,
- private val executor: AffinityExecutor,
- private val database: CordaPersistence) : SingletonSerializeAsToken(), InternalMockMessagingService {
+ private val executor: AffinityExecutor) : SingletonSerializeAsToken(), InternalMockMessagingService {
private inner class Handler(val topicSession: String, val callback: MessageHandler) : MessageHandlerRegistration
@Volatile
@@ -406,10 +410,8 @@ class InMemoryMessagingNetwork private constructor(
val (handler, transfers) = state.locked {
val handler = Handler(topic, callback).apply { handlers.add(this) }
val pending = ArrayList()
- database.transaction {
- pending.addAll(pendingRedelivery)
- pendingRedelivery.clear()
- }
+ pending.addAll(pendingRedelivery)
+ pendingRedelivery.clear()
Pair(handler, pending)
}
@@ -496,9 +498,7 @@ class InMemoryMessagingNetwork private constructor(
// up a handler for yet. Most unit tests don't run threaded, but we want to test true parallelism at
// least sometimes.
log.warn("Message to ${transfer.message.topic} could not be delivered")
- database.transaction {
- pendingRedelivery.add(transfer)
- }
+ pendingRedelivery.add(transfer)
null
} else {
matchingHandlers
@@ -516,19 +516,17 @@ class InMemoryMessagingNetwork private constructor(
val (transfer, deliverTo) = getNextQueue(q, block) ?: return null
if (transfer.message.uniqueMessageId !in processedMessages) {
executor.execute {
- database.transaction {
- for (handler in deliverTo) {
- try {
- val receivedMessage = transfer.toReceivedMessage()
- state.locked { pendingRedelivery.add(transfer) }
- handler.callback(receivedMessage, handler, InMemoryDeduplicationHandler(receivedMessage, transfer))
- } catch (e: Exception) {
- log.error("Caught exception in handler for $this/${handler.topicSession}", e)
- }
+ for (handler in deliverTo) {
+ try {
+ val receivedMessage = transfer.toReceivedMessage()
+ state.locked { pendingRedelivery.add(transfer) }
+ handler.callback(receivedMessage, handler, InMemoryDeduplicationHandler(receivedMessage, transfer))
+ } catch (e: Exception) {
+ log.error("Caught exception in handler for $this/${handler.topicSession}", e)
}
- _receivedMessages.onNext(transfer)
- messagesInFlight.countDown()
}
+ _receivedMessages.onNext(transfer)
+ messagesInFlight.countDown()
}
} else {
log.info("Drop duplicate message ${transfer.message.uniqueMessageId}")
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 8b0e4cf35c..3295bd63c7 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
@@ -111,16 +111,17 @@ open class MockServices private constructor(
val cordappLoader = CordappLoader.createWithTestPackages(cordappPackages)
val dataSourceProps = makeTestDataSourceProperties(initialIdentity.name.organisation, SecureHash.randomSHA256().toString())
val schemaService = NodeSchemaService(cordappLoader.cordappSchemas)
- val database = configureDatabase(dataSourceProps, makeTestDatabaseProperties(initialIdentity.name.organisation), identityService, schemaService)
+ val database = configureDatabase(dataSourceProps, makeTestDatabaseProperties(initialIdentity.name.organisation), identityService::wellKnownPartyFromX500Name, identityService::wellKnownPartyFromAnonymous, schemaService)
val mockService = database.transaction {
object : MockServices(cordappLoader, identityService, networkParameters, initialIdentity, moreKeys) {
- override val vaultService: VaultService = makeVaultService(database.hibernateConfig, schemaService)
+ override val vaultService: VaultService = makeVaultService(database.hibernateConfig, schemaService, database)
override fun recordTransactions(statesToRecord: StatesToRecord, txs: Iterable) {
ServiceHubInternal.recordTransactions(statesToRecord, txs,
validatedTransactions as WritableTransactionStorage,
mockStateMachineRecordedTransactionMappingStorage,
- vaultService as VaultServiceInternal)
+ vaultService as VaultServiceInternal,
+ database)
}
override fun jdbcSession(): Connection = database.createSession()
@@ -251,8 +252,8 @@ open class MockServices private constructor(
protected val servicesForResolution: ServicesForResolution get() = ServicesForResolutionImpl(identityService, attachments, cordappProvider, networkParameters, validatedTransactions)
- internal fun makeVaultService(hibernateConfig: HibernateConfiguration, schemaService: SchemaService): VaultServiceInternal {
- val vaultService = NodeVaultService(Clock.systemUTC(), keyManagementService, servicesForResolution, hibernateConfig)
+ internal fun makeVaultService(hibernateConfig: HibernateConfiguration, schemaService: SchemaService, database: CordaPersistence): VaultServiceInternal {
+ val vaultService = NodeVaultService(Clock.systemUTC(), keyManagementService, servicesForResolution, hibernateConfig, database)
HibernateObserver.install(vaultService.rawUpdates, hibernateConfig, schemaService)
return vaultService
}
diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/InternalMockNetwork.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/InternalMockNetwork.kt
index 94acec8715..f2115e32a1 100644
--- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/InternalMockNetwork.kt
+++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/InternalMockNetwork.kt
@@ -18,6 +18,7 @@ import net.corda.core.DoNotImplement
import net.corda.core.crypto.Crypto
import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.random63BitValue
+import net.corda.core.identity.AbstractParty
import net.corda.core.identity.CordaX500Name
import net.corda.core.identity.Party
import net.corda.core.identity.PartyAndCertificate
@@ -281,15 +282,14 @@ open class InternalMockNetwork(private val cordappPackages: List,
id,
serverThread,
myNotaryIdentity,
- configuration.myLegalName,
- database).also { runOnStop += it::stop }
+ configuration.myLegalName).also { runOnStop += it::stop }
}
fun setMessagingServiceSpy(messagingServiceSpy: MessagingServiceSpy) {
network = messagingServiceSpy
}
- override fun makeKeyManagementService(identityService: IdentityService, keyPairs: Set): KeyManagementService {
+ override fun makeKeyManagementService(identityService: IdentityService, keyPairs: Set, database: CordaPersistence): KeyManagementService {
return E2ETestKeyManagementService(identityService, keyPairs)
}
@@ -326,8 +326,10 @@ open class InternalMockNetwork(private val cordappPackages: List,
override val serializationWhitelists: List
get() = _serializationWhitelists
private var dbCloser: (() -> Any?)? = null
- override fun initialiseDatabasePersistence(schemaService: SchemaService, identityService: IdentityService): CordaPersistence {
- return super.initialiseDatabasePersistence(schemaService, identityService).also { dbCloser = it::close }
+ override fun initialiseDatabasePersistence(schemaService: SchemaService,
+ wellKnownPartyFromX500Name: (CordaX500Name) -> Party?,
+ wellKnownPartyFromAnonymous: (AbstractParty) -> Party?): CordaPersistence {
+ return super.initialiseDatabasePersistence(schemaService, wellKnownPartyFromX500Name, wellKnownPartyFromAnonymous).also { dbCloser = it::close }
}
fun disableDBCloseOnStop() {
diff --git a/testing/test-common/src/main/resources/log4j2-test.xml b/testing/test-common/src/main/resources/log4j2-test.xml
index 910ad4e00c..55e4799ff2 100644
--- a/testing/test-common/src/main/resources/log4j2-test.xml
+++ b/testing/test-common/src/main/resources/log4j2-test.xml
@@ -15,7 +15,22 @@
-
+
+
+
+
+
+
+
diff --git a/tools/explorer/src/main/resources/log4j2.xml b/tools/explorer/src/main/resources/log4j2.xml
index 2511e86540..c29a4f7eab 100644
--- a/tools/explorer/src/main/resources/log4j2.xml
+++ b/tools/explorer/src/main/resources/log4j2.xml
@@ -18,7 +18,22 @@
-
+
+
+
+
+
+
+