mirror of
https://github.com/corda/corda.git
synced 2024-12-21 05:53:23 +00:00
CORDA-3662: Use an INNER JOIN for network map cache queries, (#6062)
- rename add or update function for clarity - put removal of old nodes after retrieval of new ones to avoid gaps in the map - plus add a test
This commit is contained in:
parent
8eda8b744f
commit
ebdd40049c
@ -50,7 +50,7 @@ class PersistentNetworkMapCacheTest {
|
||||
@Test(timeout=300_000)
|
||||
fun addNode() {
|
||||
val alice = createNodeInfo(listOf(ALICE))
|
||||
charlieNetMapCache.addNode(alice)
|
||||
charlieNetMapCache.addOrUpdateNode(alice)
|
||||
val fromDb = database.transaction {
|
||||
session.createQuery(
|
||||
"from ${NodeInfoSchemaV1.PersistentNodeInfo::class.java.name}",
|
||||
@ -62,7 +62,7 @@ class PersistentNetworkMapCacheTest {
|
||||
|
||||
@Test(timeout=300_000)
|
||||
fun `unknown legal name`() {
|
||||
charlieNetMapCache.addNode(createNodeInfo(listOf(ALICE)))
|
||||
charlieNetMapCache.addOrUpdateNode(createNodeInfo(listOf(ALICE)))
|
||||
assertThat(charlieNetMapCache.getNodesByLegalName(DUMMY_NOTARY_NAME)).isEmpty()
|
||||
assertThat(charlieNetMapCache.getNodeByLegalName(DUMMY_NOTARY_NAME)).isNull()
|
||||
assertThat(charlieNetMapCache.getPeerByLegalName(DUMMY_NOTARY_NAME)).isNull()
|
||||
@ -71,13 +71,13 @@ class PersistentNetworkMapCacheTest {
|
||||
|
||||
@Test(timeout=300_000)
|
||||
fun `nodes in distributed service`() {
|
||||
charlieNetMapCache.addNode(createNodeInfo(listOf(ALICE)))
|
||||
charlieNetMapCache.addOrUpdateNode(createNodeInfo(listOf(ALICE)))
|
||||
|
||||
val distributedIdentity = TestIdentity(DUMMY_NOTARY_NAME)
|
||||
|
||||
val distServiceNodeInfos = (1..2).map {
|
||||
val nodeInfo = createNodeInfo(identities = listOf(TestIdentity.fresh("Org-$it"), distributedIdentity))
|
||||
charlieNetMapCache.addNode(nodeInfo)
|
||||
charlieNetMapCache.addOrUpdateNode(nodeInfo)
|
||||
nodeInfo
|
||||
}
|
||||
|
||||
@ -90,7 +90,7 @@ class PersistentNetworkMapCacheTest {
|
||||
@Test(timeout=300_000)
|
||||
fun `get nodes by owning key and by name`() {
|
||||
val alice = createNodeInfo(listOf(ALICE))
|
||||
charlieNetMapCache.addNode(alice)
|
||||
charlieNetMapCache.addOrUpdateNode(alice)
|
||||
assertThat(charlieNetMapCache.getNodesByLegalIdentityKey(ALICE.publicKey)).containsOnly(alice)
|
||||
assertThat(charlieNetMapCache.getNodeByLegalName(ALICE.name)).isEqualTo(alice)
|
||||
}
|
||||
@ -98,31 +98,31 @@ class PersistentNetworkMapCacheTest {
|
||||
@Test(timeout=300_000)
|
||||
fun `get nodes by address`() {
|
||||
val alice = createNodeInfo(listOf(ALICE))
|
||||
charlieNetMapCache.addNode(alice)
|
||||
charlieNetMapCache.addOrUpdateNode(alice)
|
||||
assertThat(charlieNetMapCache.getNodeByAddress(alice.addresses[0])).isEqualTo(alice)
|
||||
}
|
||||
|
||||
@Test(timeout=300_000)
|
||||
fun `insert two node infos with the same host and port`() {
|
||||
val alice = createNodeInfo(listOf(ALICE))
|
||||
charlieNetMapCache.addNode(alice)
|
||||
charlieNetMapCache.addOrUpdateNode(alice)
|
||||
val bob = createNodeInfo(listOf(BOB), address = alice.addresses[0])
|
||||
charlieNetMapCache.addNode(bob)
|
||||
charlieNetMapCache.addOrUpdateNode(bob)
|
||||
val nodeInfos = charlieNetMapCache.allNodes.filter { alice.addresses[0] in it.addresses }
|
||||
assertThat(nodeInfos).hasSize(2)
|
||||
}
|
||||
|
||||
@Test(timeout=300_000)
|
||||
fun `negative test - attempt to insert invalid node info`() {
|
||||
charlieNetMapCache.addNode(createNodeInfo(listOf(LONG_PLC)))
|
||||
charlieNetMapCache.addOrUpdateNode(createNodeInfo(listOf(LONG_PLC)))
|
||||
assertThat(charlieNetMapCache.allNodes).hasSize(0)
|
||||
}
|
||||
|
||||
@Test(timeout=300_000)
|
||||
fun `negative test - attempt to update existing node with invalid node info`() {
|
||||
charlieNetMapCache.addNode(createNodeInfo(listOf(ALICE)))
|
||||
charlieNetMapCache.addOrUpdateNode(createNodeInfo(listOf(ALICE)))
|
||||
val aliceUpdate = TestIdentity(LONG_X500_NAME, ALICE.keyPair)
|
||||
charlieNetMapCache.addNode(createNodeInfo(listOf(aliceUpdate)))
|
||||
charlieNetMapCache.addOrUpdateNode(createNodeInfo(listOf(aliceUpdate)))
|
||||
assertThat(charlieNetMapCache.allNodes).hasSize(1)
|
||||
assertThat(charlieNetMapCache.getNodeByLegalName(ALICE_NAME)).isNotNull
|
||||
assertThat(charlieNetMapCache.getNodeByLegalName(LONG_X500_NAME)).isNull()
|
||||
@ -130,7 +130,7 @@ class PersistentNetworkMapCacheTest {
|
||||
|
||||
@Test(timeout=300_000)
|
||||
fun `negative test - insert two valid node infos and one invalid one`() {
|
||||
charlieNetMapCache.addNodes(listOf(createNodeInfo(listOf(ALICE)),
|
||||
charlieNetMapCache.addOrUpdateNodes(listOf(createNodeInfo(listOf(ALICE)),
|
||||
createNodeInfo(listOf(BOB)),
|
||||
createNodeInfo(listOf(LONG_PLC))))
|
||||
assertThat(charlieNetMapCache.allNodes).hasSize(2)
|
||||
@ -139,7 +139,7 @@ class PersistentNetworkMapCacheTest {
|
||||
|
||||
@Test(timeout=300_000)
|
||||
fun `negative test - insert three valid node infos and two invalid ones`() {
|
||||
charlieNetMapCache.addNodes(listOf(createNodeInfo(listOf(LONG_PLC)),
|
||||
charlieNetMapCache.addOrUpdateNodes(listOf(createNodeInfo(listOf(LONG_PLC)),
|
||||
createNodeInfo(listOf(ALICE)),
|
||||
createNodeInfo(listOf(BOB)),
|
||||
createNodeInfo(listOf(CHARLIE)),
|
||||
@ -150,9 +150,9 @@ class PersistentNetworkMapCacheTest {
|
||||
|
||||
@Test(timeout=300_000)
|
||||
fun `negative test - insert one valid node info then attempt to add one invalid node info and update the existing valid nodeinfo`() {
|
||||
charlieNetMapCache.addNode(createNodeInfo(listOf(ALICE)))
|
||||
charlieNetMapCache.addOrUpdateNode(createNodeInfo(listOf(ALICE)))
|
||||
val aliceUpdate = TestIdentity(LONG_X500_NAME, ALICE.keyPair)
|
||||
charlieNetMapCache.addNodes(listOf(createNodeInfo(listOf(aliceUpdate)),
|
||||
charlieNetMapCache.addOrUpdateNodes(listOf(createNodeInfo(listOf(aliceUpdate)),
|
||||
createNodeInfo(listOf(LONGER_PLC)), createNodeInfo(listOf(BOB))))
|
||||
assertThat(charlieNetMapCache.allNodes).hasSize(2)
|
||||
assertThat(charlieNetMapCache.getNodeByLegalName(ALICE_NAME)).isNotNull
|
||||
|
@ -611,7 +611,7 @@ abstract class AbstractNode<S>(val configuration: NodeConfiguration,
|
||||
} else {
|
||||
log.info("Node-info has changed so submitting update. Old node-info was $nodeInfoFromDb")
|
||||
val newNodeInfo = potentialNodeInfo.copy(serial = platformClock.millis())
|
||||
networkMapCache.addNode(newNodeInfo)
|
||||
networkMapCache.addOrUpdateNode(newNodeInfo)
|
||||
log.info("New node-info: $newNodeInfo")
|
||||
newNodeInfo
|
||||
}
|
||||
|
@ -40,10 +40,11 @@ interface NetworkMapCacheInternal : NetworkMapCache, NetworkMapCacheBase {
|
||||
* This is used for Artemis bridge lookup process. */
|
||||
fun getNodesByOwningKeyIndex(identityKeyIndex: String): List<NodeInfo>
|
||||
|
||||
/** Adds a node to the local cache (generally only used for adding ourselves). */
|
||||
fun addNode(node: NodeInfo)
|
||||
/** Adds (or updates) a node to the local cache (generally only used for adding ourselves). */
|
||||
fun addOrUpdateNode(node: NodeInfo)
|
||||
|
||||
fun addNodes(nodes: List<NodeInfo>)
|
||||
/** Adds (or updates) nodes to the local cache. */
|
||||
fun addOrUpdateNodes(nodes: List<NodeInfo>)
|
||||
|
||||
/** Removes a node from the local cache. */
|
||||
fun removeNode(node: NodeInfo)
|
||||
|
@ -4,7 +4,13 @@ import com.google.common.util.concurrent.MoreExecutors
|
||||
import net.corda.core.CordaRuntimeException
|
||||
import net.corda.core.crypto.SecureHash
|
||||
import net.corda.core.crypto.SignedData
|
||||
import net.corda.core.internal.*
|
||||
import net.corda.core.internal.NetworkParametersStorage
|
||||
import net.corda.core.internal.VisibleForTesting
|
||||
import net.corda.core.internal.copyTo
|
||||
import net.corda.core.internal.div
|
||||
import net.corda.core.internal.exists
|
||||
import net.corda.core.internal.readObject
|
||||
import net.corda.core.internal.sign
|
||||
import net.corda.core.messaging.DataFeed
|
||||
import net.corda.core.messaging.ParametersUpdateInfo
|
||||
import net.corda.core.node.AutoAcceptable
|
||||
@ -20,7 +26,12 @@ import net.corda.node.services.config.NetworkParameterAcceptanceSettings
|
||||
import net.corda.node.utilities.NamedThreadFactory
|
||||
import net.corda.nodeapi.exceptions.OutdatedNetworkParameterHashException
|
||||
import net.corda.nodeapi.internal.SignedNodeInfo
|
||||
import net.corda.nodeapi.internal.network.*
|
||||
import net.corda.nodeapi.internal.network.NETWORK_PARAMS_FILE_NAME
|
||||
import net.corda.nodeapi.internal.network.NETWORK_PARAMS_UPDATE_FILE_NAME
|
||||
import net.corda.nodeapi.internal.network.NetworkMap
|
||||
import net.corda.nodeapi.internal.network.ParametersUpdate
|
||||
import net.corda.nodeapi.internal.network.SignedNetworkParameters
|
||||
import net.corda.nodeapi.internal.network.verifiedNetworkParametersCert
|
||||
import rx.Subscription
|
||||
import rx.subjects.PublishSubject
|
||||
import java.lang.Integer.max
|
||||
@ -118,7 +129,7 @@ class NetworkMapUpdater(private val networkMapCache: NetworkMapCacheInternal,
|
||||
.subscribe {
|
||||
for (update in it) {
|
||||
when (update) {
|
||||
is NodeInfoUpdate.Add -> networkMapCache.addNode(update.nodeInfo)
|
||||
is NodeInfoUpdate.Add -> networkMapCache.addOrUpdateNode(update.nodeInfo)
|
||||
is NodeInfoUpdate.Remove -> {
|
||||
if (update.hash != ourNodeInfoHash) {
|
||||
val nodeInfo = networkMapCache.getNodeByHash(update.hash)
|
||||
@ -177,11 +188,12 @@ class NetworkMapUpdater(private val networkMapCache: NetworkMapCacheInternal,
|
||||
if (currentParametersHash != globalNetworkMap.networkParameterHash) {
|
||||
exitOnParametersMismatch(globalNetworkMap)
|
||||
}
|
||||
val currentNodeHashes = networkMapCache.allNodeHashes
|
||||
// Remove node info from network map.
|
||||
(currentNodeHashes - allHashesFromNetworkMap - nodeInfoWatcher.processedNodeInfoHashes)
|
||||
.mapNotNull { if (it != ourNodeInfoHash) networkMapCache.getNodeByHash(it) else null }
|
||||
.forEach(networkMapCache::removeNode)
|
||||
// Calculate any nodes that are now gone and remove _only_ them from the cache
|
||||
// NOTE: We won't remove them until after the add/update cycle as only then will we definitely know which nodes are no longer
|
||||
// in the network
|
||||
val allNodeHashes = networkMapCache.allNodeHashes
|
||||
val nodeHashesToBeDeleted = (allNodeHashes - allHashesFromNetworkMap - nodeInfoWatcher.processedNodeInfoHashes)
|
||||
.filter { it != ourNodeInfoHash }
|
||||
//at the moment we use a blocking HTTP library - but under the covers, the OS will interleave threads waiting for IO
|
||||
//as HTTP GET is mostly IO bound, use more threads than CPU's
|
||||
//maximum threads to use = 24, as if we did not limit this on large machines it could result in 100's of concurrent requests
|
||||
@ -189,7 +201,7 @@ class NetworkMapUpdater(private val networkMapCache: NetworkMapCacheInternal,
|
||||
val executorToUseForDownloadingNodeInfos = Executors.newFixedThreadPool(threadsToUseForNetworkMapDownload, NamedThreadFactory("NetworkMapUpdaterNodeInfoDownloadThread"))
|
||||
//DB insert is single threaded - use a single threaded executor for it.
|
||||
val executorToUseForInsertionIntoDB = Executors.newSingleThreadExecutor(NamedThreadFactory("NetworkMapUpdateDBInsertThread"))
|
||||
val hashesToFetch = (allHashesFromNetworkMap - currentNodeHashes)
|
||||
val hashesToFetch = (allHashesFromNetworkMap - allNodeHashes)
|
||||
val networkMapDownloadStartTime = System.currentTimeMillis()
|
||||
if (hashesToFetch.isNotEmpty()) {
|
||||
val networkMapDownloadFutures = hashesToFetch.chunked(max(hashesToFetch.size / threadsToUseForNetworkMapDownload, 1))
|
||||
@ -207,7 +219,7 @@ class NetworkMapUpdater(private val networkMapCache: NetworkMapCacheInternal,
|
||||
}
|
||||
}, executorToUseForDownloadingNodeInfos).thenAcceptAsync(Consumer { retrievedNodeInfos ->
|
||||
// Add new node info to the network map cache, these could be new node info or modification of node info for existing nodes.
|
||||
networkMapCache.addNodes(retrievedNodeInfos)
|
||||
networkMapCache.addOrUpdateNodes(retrievedNodeInfos)
|
||||
}, executorToUseForInsertionIntoDB)
|
||||
}.toTypedArray()
|
||||
//wait for all the futures to complete
|
||||
@ -218,6 +230,10 @@ class NetworkMapUpdater(private val networkMapCache: NetworkMapCacheInternal,
|
||||
executorToUseForInsertionIntoDB.shutdown()
|
||||
}.getOrThrow()
|
||||
}
|
||||
// NOTE: We remove nodes after any new/updates because updated nodes will have a new hash and, therefore, any
|
||||
// nodes that we can actually pull out of the cache (with the old hashes) should be a truly removed node.
|
||||
nodeHashesToBeDeleted.mapNotNull { networkMapCache.getNodeByHash(it) }.forEach(networkMapCache::removeNode)
|
||||
|
||||
// Mark the network map cache as ready on a successful poll of the HTTP network map, even on the odd chance that
|
||||
// it's empty
|
||||
networkMapCache.nodeReady.set(null)
|
||||
|
@ -159,7 +159,7 @@ open class PersistentNetworkMapCache(cacheFactory: NamedCacheFactory,
|
||||
}
|
||||
}
|
||||
|
||||
override fun addNodes(nodes: List<NodeInfo>) {
|
||||
override fun addOrUpdateNodes(nodes: List<NodeInfo>) {
|
||||
synchronized(_changed) {
|
||||
val newNodes = mutableListOf<NodeInfo>()
|
||||
val updatedNodes = mutableListOf<Pair<NodeInfo, NodeInfo>>()
|
||||
@ -226,9 +226,9 @@ open class PersistentNetworkMapCache(cacheFactory: NamedCacheFactory,
|
||||
}
|
||||
}
|
||||
|
||||
override fun addNode(node: NodeInfo) {
|
||||
override fun addOrUpdateNode(node: NodeInfo) {
|
||||
logger.info("Adding node with info: $node")
|
||||
addNodes(listOf(node))
|
||||
addOrUpdateNodes(listOf(node))
|
||||
logger.debug { "Done adding node with info: $node" }
|
||||
}
|
||||
|
||||
@ -305,7 +305,7 @@ open class PersistentNetworkMapCache(cacheFactory: NamedCacheFactory,
|
||||
|
||||
private fun findByIdentityKeyIndex(session: Session, identityKeyIndex: String): List<NodeInfoSchemaV1.PersistentNodeInfo> {
|
||||
val query = session.createQuery(
|
||||
"SELECT n FROM ${NodeInfoSchemaV1.PersistentNodeInfo::class.java.name} n JOIN n.legalIdentitiesAndCerts l WHERE l.owningKeyHash = :owningKeyHash",
|
||||
"SELECT n FROM ${NodeInfoSchemaV1.PersistentNodeInfo::class.java.name} n INNER JOIN n.legalIdentitiesAndCerts l WHERE l.owningKeyHash = :owningKeyHash",
|
||||
NodeInfoSchemaV1.PersistentNodeInfo::class.java)
|
||||
query.setParameter("owningKeyHash", identityKeyIndex)
|
||||
return query.resultList
|
||||
@ -323,7 +323,7 @@ open class PersistentNetworkMapCache(cacheFactory: NamedCacheFactory,
|
||||
private fun queryIdentityByLegalName(session: Session, name: CordaX500Name): PartyAndCertificate? {
|
||||
val query = session.createQuery(
|
||||
// We do the JOIN here to restrict results to those present in the network map
|
||||
"SELECT DISTINCT l FROM ${NodeInfoSchemaV1.PersistentNodeInfo::class.java.name} n JOIN n.legalIdentitiesAndCerts l WHERE l.name = :name",
|
||||
"SELECT DISTINCT l FROM ${NodeInfoSchemaV1.PersistentNodeInfo::class.java.name} n INNER JOIN n.legalIdentitiesAndCerts l WHERE l.name = :name",
|
||||
NodeInfoSchemaV1.DBPartyAndCertificate::class.java)
|
||||
query.setParameter("name", name.toString())
|
||||
val candidates = query.resultList.map { it.toLegalIdentityAndCert() }
|
||||
@ -333,7 +333,7 @@ open class PersistentNetworkMapCache(cacheFactory: NamedCacheFactory,
|
||||
|
||||
private fun queryByLegalName(session: Session, name: CordaX500Name): List<NodeInfo> {
|
||||
val query = session.createQuery(
|
||||
"SELECT n FROM ${NodeInfoSchemaV1.PersistentNodeInfo::class.java.name} n JOIN n.legalIdentitiesAndCerts l WHERE l.name = :name",
|
||||
"SELECT n FROM ${NodeInfoSchemaV1.PersistentNodeInfo::class.java.name} n INNER JOIN n.legalIdentitiesAndCerts l WHERE l.name = :name",
|
||||
NodeInfoSchemaV1.PersistentNodeInfo::class.java)
|
||||
query.setParameter("name", name.toString())
|
||||
val result = query.resultList
|
||||
@ -342,7 +342,7 @@ open class PersistentNetworkMapCache(cacheFactory: NamedCacheFactory,
|
||||
|
||||
private fun queryByAddress(session: Session, hostAndPort: NetworkHostAndPort): NodeInfo? {
|
||||
val query = session.createQuery(
|
||||
"SELECT n FROM ${NodeInfoSchemaV1.PersistentNodeInfo::class.java.name} n JOIN n.addresses a WHERE a.host = :host AND a.port = :port",
|
||||
"SELECT n FROM ${NodeInfoSchemaV1.PersistentNodeInfo::class.java.name} n INNER JOIN n.addresses a WHERE a.host = :host AND a.port = :port",
|
||||
NodeInfoSchemaV1.PersistentNodeInfo::class.java)
|
||||
query.setParameter("host", hostAndPort.host)
|
||||
query.setParameter("port", hostAndPort.port)
|
||||
|
@ -31,8 +31,8 @@ class NodeRestartTests {
|
||||
val alice = mockNet.createNode(InternalMockNodeParameters(legalName = ALICE_NAME))
|
||||
val bob = mockNet.createNode(InternalMockNodeParameters(legalName = BOB_NAME))
|
||||
bob.registerInitiatedFlow(Responder::class.java)
|
||||
alice.services.networkMapCache.addNode(bob.info)
|
||||
bob.services.networkMapCache.addNode(alice.info)
|
||||
alice.services.networkMapCache.addOrUpdateNode(bob.info)
|
||||
bob.services.networkMapCache.addOrUpdateNode(alice.info)
|
||||
val alice2 = mockNet.restartNode(alice)
|
||||
val result = alice2.services.startFlow(Initiator(bob.info.singleIdentity())).resultFuture.getOrThrow()
|
||||
assertThat(result).isEqualTo(123)
|
||||
|
@ -37,7 +37,7 @@ class NetworkMapCacheTest {
|
||||
val bob = bobNode.info.singleIdentity()
|
||||
assertEquals(alice, bob)
|
||||
|
||||
aliceNode.services.networkMapCache.addNode(bobNode.info)
|
||||
aliceNode.services.networkMapCache.addOrUpdateNode(bobNode.info)
|
||||
// The details of node B write over those for node A
|
||||
assertEquals(aliceNode.services.networkMapCache.getNodesByLegalIdentityKey(alice.owningKey).singleOrNull(), bobNode.info)
|
||||
}
|
||||
@ -86,7 +86,7 @@ class NetworkMapCacheTest {
|
||||
assertNull(bobCache.getPeerByLegalName(ALICE_NAME))
|
||||
assertThat(bobCache.getNodesByLegalIdentityKey(aliceNode.info.singleIdentity().owningKey).isEmpty())
|
||||
|
||||
bobCacheInternal.addNode(aliceNode.info)
|
||||
bobCacheInternal.addOrUpdateNode(aliceNode.info)
|
||||
|
||||
assertEquals(aliceNode.info.singleIdentity(), bobCache.getPeerByLegalName(ALICE_NAME))
|
||||
assertEquals(aliceNode.info, bobCache.getNodesByLegalIdentityKey(aliceNode.info.singleIdentity().owningKey).single())
|
||||
@ -113,7 +113,7 @@ class NetworkMapCacheTest {
|
||||
val aliceNode = mockNet.createPartyNode(ALICE_NAME)
|
||||
val aliceCache = aliceNode.services.networkMapCache
|
||||
val alicePartyAndCert2 = getTestPartyAndCertificate(ALICE_NAME, generateKeyPair().public)
|
||||
aliceCache.addNode(aliceNode.info.copy(legalIdentitiesAndCerts = listOf(alicePartyAndCert2)))
|
||||
aliceCache.addOrUpdateNode(aliceNode.info.copy(legalIdentitiesAndCerts = listOf(alicePartyAndCert2)))
|
||||
// This is correct behaviour as we may have distributed service nodes.
|
||||
assertEquals(2, aliceCache.getNodesByLegalName(ALICE_NAME).size)
|
||||
}
|
||||
|
@ -2,15 +2,26 @@ package net.corda.node.services.network
|
||||
|
||||
import com.google.common.jimfs.Configuration.unix
|
||||
import com.google.common.jimfs.Jimfs
|
||||
import com.nhaarman.mockito_kotlin.*
|
||||
import com.nhaarman.mockito_kotlin.any
|
||||
import com.nhaarman.mockito_kotlin.mock
|
||||
import com.nhaarman.mockito_kotlin.never
|
||||
import com.nhaarman.mockito_kotlin.times
|
||||
import com.nhaarman.mockito_kotlin.verify
|
||||
import net.corda.core.crypto.Crypto
|
||||
import net.corda.core.crypto.SecureHash
|
||||
import net.corda.core.crypto.generateKeyPair
|
||||
import net.corda.core.crypto.sign
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.core.internal.*
|
||||
import net.corda.core.internal.NODE_INFO_DIRECTORY
|
||||
import net.corda.core.internal.NetworkParametersStorage
|
||||
import net.corda.core.internal.bufferUntilSubscribed
|
||||
import net.corda.core.internal.concurrent.openFuture
|
||||
import net.corda.core.internal.delete
|
||||
import net.corda.core.internal.div
|
||||
import net.corda.core.internal.exists
|
||||
import net.corda.core.internal.readObject
|
||||
import net.corda.core.internal.sign
|
||||
import net.corda.core.messaging.ParametersUpdateInfo
|
||||
import net.corda.core.node.NetworkParameters
|
||||
import net.corda.core.node.NodeInfo
|
||||
@ -28,10 +39,15 @@ import net.corda.nodeapi.internal.network.NodeInfoFilesCopier
|
||||
import net.corda.nodeapi.internal.network.SignedNetworkParameters
|
||||
import net.corda.nodeapi.internal.network.verifiedNetworkParametersCert
|
||||
import net.corda.testing.common.internal.testNetworkParameters
|
||||
import net.corda.testing.core.*
|
||||
import net.corda.testing.internal.DEV_ROOT_CA
|
||||
import net.corda.testing.internal.TestNodeInfoBuilder
|
||||
import net.corda.testing.internal.createNodeInfoAndSigned
|
||||
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.expect
|
||||
import net.corda.testing.core.expectEvents
|
||||
import net.corda.testing.core.sequence
|
||||
import net.corda.testing.node.internal.MockKeyManagementService
|
||||
import net.corda.testing.node.internal.MockPublicKeyToOwningIdentityCache
|
||||
import net.corda.testing.node.internal.network.NetworkMapServer
|
||||
@ -39,7 +55,11 @@ import net.corda.testing.node.makeTestIdentityService
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.assertj.core.api.Assertions.assertThatThrownBy
|
||||
import org.hamcrest.collection.IsIterableContainingInAnyOrder
|
||||
import org.junit.*
|
||||
import org.junit.After
|
||||
import org.junit.Assert
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import rx.schedulers.TestScheduler
|
||||
import java.io.IOException
|
||||
import java.net.URL
|
||||
@ -119,7 +139,7 @@ class NetworkMapUpdaterTest {
|
||||
//Test adding new node.
|
||||
networkMapClient.publish(signedNodeInfo1)
|
||||
//Not subscribed yet.
|
||||
verify(networkMapCache, times(0)).addNode(any())
|
||||
verify(networkMapCache, times(0)).addOrUpdateNode(any())
|
||||
|
||||
startUpdater()
|
||||
networkMapClient.publish(signedNodeInfo2)
|
||||
@ -195,13 +215,56 @@ class NetworkMapUpdaterTest {
|
||||
assertThat(networkMapCache.allNodeHashes).containsOnly(fileNodeInfoAndSigned.nodeInfo.serialize().hash)
|
||||
}
|
||||
|
||||
@Test(timeout=300_000)
|
||||
fun `process remove, add, and update node from network map`() {
|
||||
setUpdater()
|
||||
val (nodeInfo1, signedNodeInfo1) = createNodeInfoAndSigned("Info 1")
|
||||
val (nodeInfo3, signedNodeInfo3) = createNodeInfoAndSigned("Info 3")
|
||||
|
||||
val builder = TestNodeInfoBuilder()
|
||||
builder.addLegalIdentity(CordaX500Name("Test", "London", "GB"))
|
||||
val (nodeInfo2, signedNodeInfo2) = builder.buildWithSigned(1)
|
||||
val (nodeInfo2_2, signedNodeInfo2_2) = builder.buildWithSigned(2)
|
||||
|
||||
//Add all nodes.
|
||||
networkMapClient.publish(signedNodeInfo1)
|
||||
networkMapClient.publish(signedNodeInfo2)
|
||||
|
||||
startUpdater()
|
||||
advanceTime()
|
||||
//TODO: Remove sleep in unit test.
|
||||
Thread.sleep(2L * cacheExpiryMs)
|
||||
|
||||
Assert.assertThat(networkMapCache.allNodeHashes, IsIterableContainingInAnyOrder.containsInAnyOrder(
|
||||
signedNodeInfo1.raw.hash,
|
||||
signedNodeInfo2.raw.hash
|
||||
))
|
||||
|
||||
// remove one node, add another and update a third.
|
||||
server.removeNodeInfo(nodeInfo1)
|
||||
networkMapClient.publish(signedNodeInfo3)
|
||||
networkMapClient.publish(signedNodeInfo2_2)
|
||||
|
||||
advanceTime()
|
||||
//TODO: Remove sleep in unit test.
|
||||
Thread.sleep(2L * cacheExpiryMs)
|
||||
verify(networkMapCache, times(1)).removeNode(nodeInfo1)
|
||||
verify(networkMapCache, times(0)).removeNode(nodeInfo2)
|
||||
verify(networkMapCache, times(1)).addOrUpdateNodes(listOf(nodeInfo2_2))
|
||||
verify(networkMapCache, times(1)).addOrUpdateNodes(listOf(nodeInfo3))
|
||||
assertThat(networkMapCache.allNodeHashes).hasSameElementsAs(listOf(
|
||||
signedNodeInfo2_2.raw.hash,
|
||||
signedNodeInfo3.raw.hash
|
||||
))
|
||||
}
|
||||
|
||||
@Test(timeout=300_000)
|
||||
fun `receive node infos from directory, without a network map`() {
|
||||
setUpdater(netMapClient = null)
|
||||
val fileNodeInfoAndSigned = createNodeInfoAndSigned("Info from file")
|
||||
|
||||
//Not subscribed yet.
|
||||
verify(networkMapCache, times(0)).addNode(any())
|
||||
verify(networkMapCache, times(0)).addOrUpdateNode(any())
|
||||
|
||||
startUpdater()
|
||||
|
||||
@ -209,8 +272,8 @@ class NetworkMapUpdaterTest {
|
||||
assertThat(nodeReadyFuture).isNotDone()
|
||||
advanceTime()
|
||||
|
||||
verify(networkMapCache, times(1)).addNode(any())
|
||||
verify(networkMapCache, times(1)).addNode(fileNodeInfoAndSigned.nodeInfo)
|
||||
verify(networkMapCache, times(1)).addOrUpdateNode(any())
|
||||
verify(networkMapCache, times(1)).addOrUpdateNode(fileNodeInfoAndSigned.nodeInfo)
|
||||
assertThat(nodeReadyFuture).isDone()
|
||||
|
||||
assertThat(networkMapCache.allNodeHashes).containsOnly(fileNodeInfoAndSigned.nodeInfo.serialize().hash)
|
||||
@ -331,9 +394,9 @@ class NetworkMapUpdaterTest {
|
||||
NodeInfoWatcher.saveToFile(nodeInfoDir, fileNodeInfoAndSigned1)
|
||||
NodeInfoWatcher.saveToFile(nodeInfoDir, fileNodeInfoAndSigned2)
|
||||
advanceTime()
|
||||
verify(networkMapCache, times(2)).addNode(any())
|
||||
verify(networkMapCache, times(1)).addNode(fileNodeInfoAndSigned1.nodeInfo)
|
||||
verify(networkMapCache, times(1)).addNode(fileNodeInfoAndSigned2.nodeInfo)
|
||||
verify(networkMapCache, times(2)).addOrUpdateNode(any())
|
||||
verify(networkMapCache, times(1)).addOrUpdateNode(fileNodeInfoAndSigned1.nodeInfo)
|
||||
verify(networkMapCache, times(1)).addOrUpdateNode(fileNodeInfoAndSigned2.nodeInfo)
|
||||
assertThat(networkMapCache.allNodeHashes).containsExactlyInAnyOrder(fileNodeInfoAndSigned1.signed.raw.hash, fileNodeInfoAndSigned2.signed.raw.hash)
|
||||
//Remove one of the nodes
|
||||
val fileName1 = "${NodeInfoFilesCopier.NODE_INFO_FILE_NAME_PREFIX}${fileNodeInfoAndSigned1.nodeInfo.legalIdentities[0].name.serialize().hash}"
|
||||
@ -361,7 +424,7 @@ class NetworkMapUpdaterTest {
|
||||
networkMapClient.publish(serverSignedNodeInfo)
|
||||
startUpdater()
|
||||
advanceTime()
|
||||
verify(networkMapCache, times(1)).addNode(localNodeInfo)
|
||||
verify(networkMapCache, times(1)).addOrUpdateNode(localNodeInfo)
|
||||
Thread.sleep(2L * cacheExpiryMs)
|
||||
//Node from file has higher serial than the one from NetworkMapServer
|
||||
assertThat(networkMapCache.allNodeHashes).containsOnly(localSignedNodeInfo.signed.raw.hash)
|
||||
@ -383,7 +446,7 @@ class NetworkMapUpdaterTest {
|
||||
val (myInfo, signedMyInfo) = createNodeInfoAndSigned("My node info")
|
||||
val (_, signedOtherInfo) = createNodeInfoAndSigned("Other info")
|
||||
setUpdater()
|
||||
networkMapCache.addNode(myInfo) //Simulate behaviour on node startup when our node info is added to cache
|
||||
networkMapCache.addOrUpdateNode(myInfo) //Simulate behaviour on node startup when our node info is added to cache
|
||||
networkMapClient.publish(signedOtherInfo)
|
||||
startUpdater(ourNodeInfo = signedMyInfo)
|
||||
Thread.sleep(2L * cacheExpiryMs)
|
||||
@ -406,19 +469,22 @@ class NetworkMapUpdaterTest {
|
||||
//Test adding new node.
|
||||
networkMapClient.publish(signedNodeInfo1)
|
||||
//Not subscribed yet.
|
||||
verify(networkMapCache, times(0)).addNode(any())
|
||||
verify(networkMapCache, times(0)).addOrUpdateNode(any())
|
||||
|
||||
startUpdater()
|
||||
|
||||
//TODO: Remove sleep in unit test.
|
||||
Thread.sleep(2L * cacheExpiryMs)
|
||||
assert(networkMapCache.allNodeHashes.size == 1)
|
||||
assert(networkMapCache.allNodeHashes.first() == signedNodeInfo1.raw.hash)
|
||||
verify(networkMapCache, times(1)).addOrUpdateNodes(listOf(signedNodeInfo1.verified()))
|
||||
networkMapClient.publish(signedNodeInfo2)
|
||||
Thread.sleep(2L * cacheExpiryMs)
|
||||
advanceTime()
|
||||
|
||||
verify(networkMapCache, times(1)).removeNode(signedNodeInfo1.verified())
|
||||
verify(networkMapCache, times(1)).addOrUpdateNodes(listOf(signedNodeInfo1.verified()))
|
||||
assert(networkMapCache.allNodeHashes.size == 1)
|
||||
assert(networkMapCache.allNodeHashes.first() == signedNodeInfo2.raw.hash)
|
||||
}
|
||||
|
||||
@Test(timeout=300_000)
|
||||
@ -459,15 +525,11 @@ class NetworkMapUpdaterTest {
|
||||
return mock {
|
||||
on { nodeReady }.thenReturn(nodeReadyFuture)
|
||||
val data = ConcurrentHashMap<Party, NodeInfo>()
|
||||
on { addNode(any()) }.then {
|
||||
on { addOrUpdateNode(any()) }.then {
|
||||
val nodeInfo = it.arguments[0] as NodeInfo
|
||||
val party = nodeInfo.legalIdentities[0]
|
||||
data.compute(party) { _, current ->
|
||||
if (current == null || current.serial < nodeInfo.serial) nodeInfo else current
|
||||
}
|
||||
addNodeToMockCache(nodeInfo, data)
|
||||
}
|
||||
|
||||
on { addNodes(any<List<NodeInfo>>()) }.then {
|
||||
on { addOrUpdateNodes(any()) }.then {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
val nodeInfos = it.arguments[0] as List<NodeInfo>
|
||||
nodeInfos.forEach { nodeInfo ->
|
||||
@ -477,6 +539,7 @@ class NetworkMapUpdaterTest {
|
||||
|
||||
on { removeNode(any()) }.then { data.remove((it.arguments[0] as NodeInfo).legalIdentities[0]) }
|
||||
on { getNodeByLegalIdentity(any()) }.then { data[it.arguments[0]] }
|
||||
on { allNodes }.then { data.values.toList() }
|
||||
on { allNodeHashes }.then { data.values.map { it.serialize().hash } }
|
||||
on { getNodeByHash(any()) }.then { mock -> data.values.singleOrNull { it.serialize().hash == mock.arguments[0] } }
|
||||
}
|
||||
|
@ -352,8 +352,8 @@ open class InternalMockNetwork(cordappPackages: List<String> = emptyList(),
|
||||
mockNet.nodes
|
||||
.mapNotNull { it.started }
|
||||
.forEach { existingNode ->
|
||||
newNode.services.networkMapCache.addNode(existingNode.info)
|
||||
existingNode.services.networkMapCache.addNode(newNode.info)
|
||||
newNode.services.networkMapCache.addOrUpdateNode(existingNode.info)
|
||||
existingNode.services.networkMapCache.addOrUpdateNode(newNode.info)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -147,7 +147,7 @@ constructor(private val cordappPackages: List<String> = emptyList(), private val
|
||||
val runningNodesInfo = runningNodes.map { it.info }
|
||||
for (node in runningNodes)
|
||||
for (nodeInfo in runningNodesInfo) {
|
||||
node.services.networkMapCache.addNode(nodeInfo)
|
||||
node.services.networkMapCache.addOrUpdateNode(nodeInfo)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user