mirror of
https://github.com/corda/corda.git
synced 2024-12-21 05:53:23 +00:00
CORDA-3974: NetworkMapCache should link entries with different public keys by X.500 name (#6711)
This commit is contained in:
parent
d1735b8c42
commit
acb82f77b4
@ -263,7 +263,7 @@ class P2PMessagingClient(val config: NodeConfiguration,
|
||||
}
|
||||
|
||||
private fun updateBridgesOnNetworkChange(change: NetworkMapCache.MapChange) {
|
||||
log.info("Updating bridges on network map change: ${change.node}")
|
||||
log.info("Updating bridges on network map change: ${change::class.simpleName} ${change.node}")
|
||||
fun gatherAddresses(node: NodeInfo): Sequence<BridgeEntry> {
|
||||
return state.locked {
|
||||
node.legalIdentitiesAndCerts.map {
|
||||
|
@ -161,12 +161,15 @@ open class PersistentNetworkMapCache(cacheFactory: NamedCacheFactory,
|
||||
}
|
||||
}
|
||||
|
||||
private fun NodeInfo.printWithKey() = "$this, owningKey=${legalIdentities.first().owningKey.toStringShort()}"
|
||||
|
||||
override fun addOrUpdateNodes(nodes: List<NodeInfo>) {
|
||||
synchronized(_changed) {
|
||||
val newNodes = mutableListOf<NodeInfo>()
|
||||
val updatedNodes = mutableListOf<Pair<NodeInfo, NodeInfo>>()
|
||||
nodes.map { it to getNodesByLegalIdentityKey(it.legalIdentities.first().owningKey).firstOrNull() }
|
||||
nodes.map { it to getNodeInfo(it.legalIdentities.first()) }
|
||||
.forEach { (node, previousNode) ->
|
||||
logger.info("Adding node with info: ${node.printWithKey()}")
|
||||
when {
|
||||
previousNode == null -> {
|
||||
logger.info("No previous node found for ${node.legalIdentities.first().name}")
|
||||
@ -178,7 +181,7 @@ open class PersistentNetworkMapCache(cacheFactory: NamedCacheFactory,
|
||||
logger.info("Discarding older nodeInfo for ${node.legalIdentities.first().name}")
|
||||
}
|
||||
previousNode != node -> {
|
||||
logger.info("Previous node was found for ${node.legalIdentities.first().name} as: $previousNode")
|
||||
logger.info("Previous node was found for ${node.legalIdentities.first().name} as: ${previousNode.printWithKey()}")
|
||||
// TODO We should be adding any new identities as well
|
||||
if (verifyIdentities(node)) {
|
||||
updatedNodes.add(node to previousNode)
|
||||
@ -196,6 +199,23 @@ open class PersistentNetworkMapCache(cacheFactory: NamedCacheFactory,
|
||||
recursivelyUpdateNodes(newNodes.map { nodeInfo -> Pair(nodeInfo, MapChange.Added(nodeInfo)) } +
|
||||
updatedNodes.map { (nodeInfo, previousNodeInfo) -> Pair(nodeInfo, MapChange.Modified(nodeInfo, previousNodeInfo)) })
|
||||
}
|
||||
logger.debug { "Done adding nodes with info: $nodes" }
|
||||
}
|
||||
|
||||
// Obtain node info by its legal identity key or, if the key was rotated, by name.
|
||||
private fun getNodeInfo(party: Party): NodeInfo? {
|
||||
val infoByKey = getNodesByLegalIdentityKey(party.owningKey).firstOrNull()
|
||||
return infoByKey ?: getPeerCertificateByLegalName(party.name)?.let {
|
||||
getNodesByLegalIdentityKey(it.owningKey).firstOrNull()
|
||||
}
|
||||
}
|
||||
|
||||
// Obtain node info by its legal identity key or, if the key was rotated, by name.
|
||||
private fun getPersistentNodeInfo(session: Session, party: Party): NodeInfoSchemaV1.PersistentNodeInfo? {
|
||||
val infoByKey = findByIdentityKey(session, party.owningKey).firstOrNull()
|
||||
return infoByKey ?: queryIdentityByLegalName(session, party.name)?.let {
|
||||
findByIdentityKey(session, it.owningKey).firstOrNull()
|
||||
}
|
||||
}
|
||||
|
||||
private fun recursivelyUpdateNodes(nodeUpdates: List<Pair<NodeInfo, MapChange>>) {
|
||||
@ -222,16 +242,14 @@ open class PersistentNetworkMapCache(cacheFactory: NamedCacheFactory,
|
||||
private fun persistNodeUpdates(nodeUpdates: List<Pair<NodeInfo, MapChange>>) {
|
||||
database.transaction {
|
||||
nodeUpdates.forEach { (nodeInfo, change) ->
|
||||
updateInfoDB(nodeInfo, session)
|
||||
updateInfoDB(nodeInfo, session, change)
|
||||
changePublisher.onNext(change)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun addOrUpdateNode(node: NodeInfo) {
|
||||
logger.info("Adding node with info: $node")
|
||||
addOrUpdateNodes(listOf(node))
|
||||
logger.debug { "Done adding node with info: $node" }
|
||||
}
|
||||
|
||||
private fun verifyIdentities(node: NodeInfo): Boolean {
|
||||
@ -279,17 +297,20 @@ open class PersistentNetworkMapCache(cacheFactory: NamedCacheFactory,
|
||||
return session.createQuery(criteria).resultList
|
||||
}
|
||||
|
||||
private fun updateInfoDB(nodeInfo: NodeInfo, session: Session) {
|
||||
// TODO For now the main legal identity is left in NodeInfo, this should be set comparision/come up with index for NodeInfo?
|
||||
val info = findByIdentityKey(session, nodeInfo.legalIdentitiesAndCerts.first().owningKey)
|
||||
private fun updateInfoDB(nodeInfo: NodeInfo, session: Session, change: MapChange) {
|
||||
val info = getPersistentNodeInfo(session, nodeInfo.legalIdentitiesAndCerts.first().party)
|
||||
val nodeInfoEntry = generateMappedObject(nodeInfo)
|
||||
if (info.isNotEmpty()) {
|
||||
nodeInfoEntry.id = info.first().id
|
||||
if (info != null) {
|
||||
nodeInfoEntry.id = info.id
|
||||
}
|
||||
session.merge(nodeInfoEntry)
|
||||
// invalidate cache last - this way, we might serve up the wrong info for a short time, but it will get refreshed
|
||||
// on the next load
|
||||
invalidateCaches(nodeInfo)
|
||||
// invalidate cache for previous key on rotated certificate
|
||||
if (change is MapChange.Modified) {
|
||||
invalidateCaches(change.previousNode)
|
||||
}
|
||||
}
|
||||
|
||||
private fun removeInfoDB(session: Session, nodeInfo: NodeInfo) {
|
||||
|
@ -1,11 +1,11 @@
|
||||
package net.corda.node.services.network
|
||||
|
||||
import net.corda.core.crypto.generateKeyPair
|
||||
import net.corda.core.crypto.toStringShort
|
||||
import net.corda.core.node.services.NetworkMapCache
|
||||
import net.corda.core.serialization.serialize
|
||||
import net.corda.node.services.api.NetworkMapCacheInternal
|
||||
import net.corda.testing.core.ALICE_NAME
|
||||
import net.corda.testing.core.BOB_NAME
|
||||
import net.corda.testing.core.getTestPartyAndCertificate
|
||||
import net.corda.testing.core.singleIdentity
|
||||
import net.corda.testing.node.internal.InternalMockNetwork
|
||||
import net.corda.testing.node.internal.InternalMockNodeParameters
|
||||
@ -108,13 +108,81 @@ class NetworkMapCacheTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test(timeout=300_000)
|
||||
fun `add two nodes the same name different keys`() {
|
||||
val aliceNode = mockNet.createPartyNode(ALICE_NAME)
|
||||
val aliceCache = aliceNode.services.networkMapCache
|
||||
val alicePartyAndCert2 = getTestPartyAndCertificate(ALICE_NAME, generateKeyPair().public)
|
||||
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)
|
||||
@Test(timeout = 300_000)
|
||||
fun `replace node with the same key but different certificate`() {
|
||||
val bobCache = mockNet.createPartyNode(BOB_NAME).services.networkMapCache
|
||||
|
||||
// Add node info with original key and certificate
|
||||
val aliceInfo = mockNet.createNode(InternalMockNodeParameters(legalName = ALICE_NAME, entropyRoot = BigInteger.valueOf(70))).info
|
||||
val aliceKey = aliceInfo.legalIdentities.single().owningKey
|
||||
val aliceCertificate = aliceInfo.legalIdentitiesAndCerts.single()
|
||||
bobCache.addOrUpdateNode(aliceInfo)
|
||||
|
||||
// Check database
|
||||
assertThat(bobCache.getNodeByLegalName(ALICE_NAME)).isEqualTo(aliceInfo)
|
||||
assertThat(bobCache.getNodesByOwningKeyIndex(aliceKey.toStringShort())).containsOnly(aliceInfo)
|
||||
// Check cache
|
||||
assertThat(bobCache.getNodeByHash(aliceInfo.serialize().hash)).isEqualTo(aliceInfo)
|
||||
assertThat(bobCache.getPeerCertificateByLegalName(ALICE_NAME)).isEqualTo(aliceCertificate)
|
||||
assertThat(bobCache.getNodesByLegalIdentityKey(aliceKey)).containsOnly(aliceInfo)
|
||||
|
||||
// Update node info with new key and certificate
|
||||
val aliceInfo2 = mockNet.createNode(InternalMockNodeParameters(legalName = ALICE_NAME, entropyRoot = BigInteger.valueOf(70))).info
|
||||
val aliceKey2 = aliceInfo2.legalIdentities.single().owningKey
|
||||
val aliceCertificate2 = aliceInfo2.legalIdentitiesAndCerts.single()
|
||||
bobCache.addOrUpdateNode(aliceInfo2)
|
||||
|
||||
// Check that keys are the same and certificates are different
|
||||
assertThat(aliceKey).isEqualTo(aliceKey2)
|
||||
assertThat(aliceCertificate.certificate).isNotEqualTo(aliceCertificate2.certificate)
|
||||
|
||||
// Check new entry
|
||||
assertThat(bobCache.getNodeByLegalName(ALICE_NAME)).isEqualTo(aliceInfo2)
|
||||
assertThat(bobCache.getNodesByOwningKeyIndex(aliceKey2.toStringShort())).containsOnly(aliceInfo2)
|
||||
assertThat(bobCache.getNodeByHash(aliceInfo2.serialize().hash)).isEqualTo(aliceInfo2)
|
||||
assertThat(bobCache.getPeerCertificateByLegalName(ALICE_NAME)).isEqualTo(aliceCertificate2)
|
||||
assertThat(bobCache.getNodesByLegalIdentityKey(aliceKey2)).containsOnly(aliceInfo2)
|
||||
// Check old entry
|
||||
assertThat(bobCache.getNodeByHash(aliceInfo.serialize().hash)).isNull()
|
||||
}
|
||||
|
||||
@Test(timeout = 300_000)
|
||||
fun `replace node with the same name but different key and certificate`() {
|
||||
val bobCache = mockNet.createPartyNode(BOB_NAME).services.networkMapCache
|
||||
|
||||
// Add node info with original key and certificate
|
||||
val aliceInfo = mockNet.createNode(InternalMockNodeParameters(legalName = ALICE_NAME, entropyRoot = BigInteger.valueOf(70))).info
|
||||
val aliceKey = aliceInfo.legalIdentities.single().owningKey
|
||||
val aliceCertificate = aliceInfo.legalIdentitiesAndCerts.single()
|
||||
bobCache.addOrUpdateNode(aliceInfo)
|
||||
|
||||
// Check database
|
||||
assertThat(bobCache.getNodeByLegalName(ALICE_NAME)).isEqualTo(aliceInfo)
|
||||
assertThat(bobCache.getNodesByOwningKeyIndex(aliceKey.toStringShort())).containsOnly(aliceInfo)
|
||||
// Check cache
|
||||
assertThat(bobCache.getNodeByHash(aliceInfo.serialize().hash)).isEqualTo(aliceInfo)
|
||||
assertThat(bobCache.getPeerCertificateByLegalName(ALICE_NAME)).isEqualTo(aliceCertificate)
|
||||
assertThat(bobCache.getNodesByLegalIdentityKey(aliceKey)).containsOnly(aliceInfo)
|
||||
|
||||
// Update node info with new key and certificate
|
||||
val aliceInfo2 = mockNet.createNode(InternalMockNodeParameters(legalName = ALICE_NAME, entropyRoot = BigInteger.valueOf(71))).info
|
||||
val aliceKey2 = aliceInfo2.legalIdentities.single().owningKey
|
||||
val aliceCertificate2 = aliceInfo2.legalIdentitiesAndCerts.single()
|
||||
bobCache.addOrUpdateNode(aliceInfo2)
|
||||
|
||||
// Check that keys and certificates are different
|
||||
assertThat(aliceKey).isNotEqualTo(aliceKey2)
|
||||
assertThat(aliceCertificate.certificate).isNotEqualTo(aliceCertificate2.certificate)
|
||||
|
||||
// Check new entry
|
||||
assertThat(bobCache.getNodeByLegalName(ALICE_NAME)).isEqualTo(aliceInfo2)
|
||||
assertThat(bobCache.getNodesByOwningKeyIndex(aliceKey2.toStringShort())).containsOnly(aliceInfo2)
|
||||
assertThat(bobCache.getNodeByHash(aliceInfo2.serialize().hash)).isEqualTo(aliceInfo2)
|
||||
assertThat(bobCache.getPeerCertificateByLegalName(ALICE_NAME)).isEqualTo(aliceCertificate2)
|
||||
assertThat(bobCache.getNodesByLegalIdentityKey(aliceKey2)).containsOnly(aliceInfo2)
|
||||
// Check old entry
|
||||
assertThat(bobCache.getNodesByOwningKeyIndex(aliceKey.toStringShort())).isEmpty()
|
||||
assertThat(bobCache.getNodeByHash(aliceInfo.serialize().hash)).isNull()
|
||||
assertThat(bobCache.getNodesByLegalIdentityKey(aliceKey)).isEmpty()
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user