ENT-6386: Revert change of behaviour in rpcOps.wellKnownPartyFromX500Name for revoked identity (#7032)

* ENT-6386: Reverting wellKnownPartyFromX500Name functionality to work … (#4347)
This commit is contained in:
Adel El-Beik 2022-01-20 11:08:13 +00:00 committed by GitHub
parent c05c1934cf
commit b17e4571bf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 280 additions and 5 deletions

View File

@ -31,6 +31,7 @@ import java.security.PublicKey
import kotlin.test.assertEquals import kotlin.test.assertEquals
import kotlin.test.assertNotEquals import kotlin.test.assertNotEquals
import kotlin.test.assertNull import kotlin.test.assertNull
import kotlin.test.assertNotNull
class CertificateRotationTest { class CertificateRotationTest {
private val ref = OpaqueBytes.of(0x01) private val ref = OpaqueBytes.of(0x01)
@ -180,7 +181,7 @@ class CertificateRotationTest {
advertiseNodesToNetwork(mockNet.defaultNotaryNode, bob2, charlie) advertiseNodesToNetwork(mockNet.defaultNotaryNode, bob2, charlie)
assertNull(bob2.services.identityService.wellKnownPartyFromX500Name(ALICE_NAME)) assertNotNull(bob2.services.identityService.wellKnownPartyFromX500Name(ALICE_NAME))
assertNull(charlie.services.identityService.wellKnownPartyFromX500Name(ALICE_NAME)) assertNull(charlie.services.identityService.wellKnownPartyFromX500Name(ALICE_NAME))
bob2.services.startFlow(CashPaymentFlow(1000.DOLLARS, charlie.party, false)) bob2.services.startFlow(CashPaymentFlow(1000.DOLLARS, charlie.party, false))

View File

@ -14,6 +14,7 @@ import net.corda.node.internal.DBNetworkParametersStorage
import net.corda.node.internal.schemas.NodeInfoSchemaV1 import net.corda.node.internal.schemas.NodeInfoSchemaV1
import net.corda.node.services.identity.PersistentIdentityService import net.corda.node.services.identity.PersistentIdentityService
import net.corda.node.services.keys.BasicHSMKeyManagementService import net.corda.node.services.keys.BasicHSMKeyManagementService
import net.corda.node.services.network.PersistentNetworkMapCache
import net.corda.node.services.persistence.DBTransactionStorage import net.corda.node.services.persistence.DBTransactionStorage
import net.corda.node.services.persistence.NodeAttachmentService import net.corda.node.services.persistence.NodeAttachmentService
import net.corda.node.services.vault.NodeVaultService import net.corda.node.services.vault.NodeVaultService
@ -133,7 +134,8 @@ object VaultMigrationSchemaV1 : MappedSchema(schemaFamily = VaultMigrationSchema
PersistentIdentityService.PersistentPublicKeyHashToParty::class.java, PersistentIdentityService.PersistentPublicKeyHashToParty::class.java,
BasicHSMKeyManagementService.PersistentKey::class.java, BasicHSMKeyManagementService.PersistentKey::class.java,
NodeAttachmentService.DBAttachment::class.java, NodeAttachmentService.DBAttachment::class.java,
DBNetworkParametersStorage.PersistentNetworkParameters::class.java DBNetworkParametersStorage.PersistentNetworkParameters::class.java,
PersistentNetworkMapCache.PersistentPartyToPublicKeyHash::class.java
) )
) )

View File

@ -1,5 +1,6 @@
package net.corda.node.services.identity package net.corda.node.services.identity
import com.google.common.util.concurrent.ThreadFactoryBuilder
import net.corda.core.crypto.Crypto import net.corda.core.crypto.Crypto
import net.corda.core.crypto.toStringShort import net.corda.core.crypto.toStringShort
import net.corda.core.identity.AbstractParty import net.corda.core.identity.AbstractParty
@ -22,6 +23,7 @@ import net.corda.node.internal.schemas.NodeInfoSchemaV1
import net.corda.node.services.api.IdentityServiceInternal import net.corda.node.services.api.IdentityServiceInternal
import net.corda.node.services.keys.BasicHSMKeyManagementService import net.corda.node.services.keys.BasicHSMKeyManagementService
import net.corda.node.services.network.NotaryUpdateListener import net.corda.node.services.network.NotaryUpdateListener
import net.corda.node.services.network.PersistentNetworkMapCache
import net.corda.node.services.persistence.PublicKeyHashToExternalId import net.corda.node.services.persistence.PublicKeyHashToExternalId
import net.corda.node.services.persistence.WritablePublicKeyToOwningIdentityCache import net.corda.node.services.persistence.WritablePublicKeyToOwningIdentityCache
import net.corda.node.utilities.AppendOnlyPersistentMap import net.corda.node.utilities.AppendOnlyPersistentMap
@ -46,6 +48,8 @@ import java.security.cert.CollectionCertStoreParameters
import java.security.cert.TrustAnchor import java.security.cert.TrustAnchor
import java.security.cert.X509Certificate import java.security.cert.X509Certificate
import java.util.* import java.util.*
import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors
import java.util.stream.Stream import java.util.stream.Stream
import javax.annotation.concurrent.ThreadSafe import javax.annotation.concurrent.ThreadSafe
import javax.persistence.Column import javax.persistence.Column
@ -140,6 +144,8 @@ class PersistentIdentityService(cacheFactory: NamedCacheFactory) : SingletonSeri
private fun mapToKey(party: PartyAndCertificate) = party.owningKey.toStringShort() private fun mapToKey(party: PartyAndCertificate) = party.owningKey.toStringShort()
} }
val archiveIdentityExecutor: ExecutorService = Executors.newCachedThreadPool(ThreadFactoryBuilder().setNameFormat("archive-named-identity-thread-%d").build())
@Entity @Entity
@javax.persistence.Table(name = "${NODE_DATABASE_PREFIX}identities") @javax.persistence.Table(name = "${NODE_DATABASE_PREFIX}identities")
class PersistentPublicKeyHashToCertificate( class PersistentPublicKeyHashToCertificate(
@ -312,7 +318,76 @@ class PersistentIdentityService(cacheFactory: NamedCacheFactory) : SingletonSeri
} }
override fun wellKnownPartyFromX500Name(name: CordaX500Name): Party? = database.transaction { override fun wellKnownPartyFromX500Name(name: CordaX500Name): Party? = database.transaction {
nameToParty[name]?.orElse(null) if (nameToParty[name]?.isPresent == true) {
nameToParty[name]?.get()
}
else {
retrievePartyFromArchive(name)
}
}
private fun retrievePartyFromArchive(name: CordaX500Name): Party? {
val hashKey = database.transaction {
val query = session.criteriaBuilder.createQuery(PersistentNetworkMapCache.PersistentPartyToPublicKeyHash::class.java)
val queryRoot = query.from(PersistentNetworkMapCache.PersistentPartyToPublicKeyHash::class.java)
query.where(session.criteriaBuilder.equal(queryRoot.get<String>("name"), name.toString()))
val resultList = session.createQuery(query).resultList
if (resultList.isNotEmpty()) {
resultList?.first()?.publicKeyHash
}
else {
retrieveHashKeyAndCacheParty(name)
}
}
return hashKey?.let { keyToPartyAndCert[it]?.party }
}
private fun retrieveHashKeyAndCacheParty(name: CordaX500Name): String? {
return database.transaction {
val cb = session.criteriaBuilder
val query = cb.createQuery(PersistentPublicKeyHashToParty::class.java)
val root = query.from(PersistentPublicKeyHashToParty::class.java)
val isNotConfidentialIdentity = cb.equal(root.get<String>("publicKeyHash"), root.get<String>("owningKeyHash"))
val matchName = cb.equal(root.get<String>("name"), name.toString())
query.select(root).where(cb.and(matchName, isNotConfidentialIdentity))
val resultList = session.createQuery(query).resultList
var hashKey: String? = if (resultList.isNotEmpty()) {
if (resultList.size == 1) {
resultList?.single()?.owningKeyHash
}
else {
selectIdentityHash(session, resultList.mapNotNull { it.publicKeyHash }, name)
}
} else {
null
}
archiveNamedIdentity(name.toString(), hashKey)
hashKey
}
}
private fun selectIdentityHash(session: Session, hashList: List<String>, name: CordaX500Name): String? {
val cb = session.criteriaBuilder
val query = cb.createQuery(PersistentPublicKeyHashToCertificate::class.java)
val root = query.from(PersistentPublicKeyHashToCertificate::class.java)
query.select(root).where(root.get<String>("publicKeyHash").`in`(hashList))
val resultList = session.createQuery(query).resultList
resultList.sortWith(compareBy { PartyAndCertificate(X509CertificateFactory().delegate.generateCertPath(it.identity.inputStream())).certificate.notBefore })
log.warn("Retrieving identity hash for removed identity '${name}', more that one hash found, returning last one according to notBefore validity of certificate." +
" Hash returned is ${resultList.last().publicKeyHash}")
return resultList.last().publicKeyHash
}
private fun archiveNamedIdentity(name:String, publicKeyHash: String?) {
archiveIdentityExecutor.submit {
database.transaction {
val deleteQuery = session.criteriaBuilder.createCriteriaDelete(PersistentNetworkMapCache.PersistentPartyToPublicKeyHash::class.java)
val queryRoot = deleteQuery.from(PersistentNetworkMapCache.PersistentPartyToPublicKeyHash::class.java)
deleteQuery.where(session.criteriaBuilder.equal(queryRoot.get<String>("name"), name))
session.createQuery(deleteQuery).executeUpdate()
session.save(PersistentNetworkMapCache.PersistentPartyToPublicKeyHash(name, publicKeyHash))
}
}.get()
} }
override fun wellKnownPartyFromAnonymous(party: AbstractParty): Party? { override fun wellKnownPartyFromAnonymous(party: AbstractParty): Party? {

View File

@ -17,6 +17,7 @@ import net.corda.core.node.services.NetworkMapCache.MapChange
import net.corda.core.node.services.PartyInfo import net.corda.core.node.services.PartyInfo
import net.corda.core.serialization.SingletonSerializeAsToken import net.corda.core.serialization.SingletonSerializeAsToken
import net.corda.core.serialization.serialize import net.corda.core.serialization.serialize
import net.corda.core.utilities.MAX_HASH_HEX_SIZE
import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.NetworkHostAndPort
import net.corda.core.utilities.contextLogger import net.corda.core.utilities.contextLogger
import net.corda.core.utilities.debug import net.corda.core.utilities.debug
@ -34,6 +35,9 @@ import java.security.PublicKey
import java.security.cert.CertPathValidatorException import java.security.cert.CertPathValidatorException
import java.util.* import java.util.*
import javax.annotation.concurrent.ThreadSafe import javax.annotation.concurrent.ThreadSafe
import javax.persistence.Column
import javax.persistence.Entity
import javax.persistence.Id
import javax.persistence.PersistenceException import javax.persistence.PersistenceException
/** Database-based network map cache. */ /** Database-based network map cache. */
@ -61,6 +65,18 @@ open class PersistentNetworkMapCache(cacheFactory: NamedCacheFactory,
@Volatile @Volatile
private lateinit var rotatedNotaries: Set<CordaX500Name> private lateinit var rotatedNotaries: Set<CordaX500Name>
@Entity
@javax.persistence.Table(name = "node_named_identities")
data class PersistentPartyToPublicKeyHash(
@Id
@Suppress("MagicNumber") // database column width
@Column(name = "name", length = 128, nullable = false)
var name: String = "",
@Column(name = "pk_hash", length = MAX_HASH_HEX_SIZE, nullable = true)
var publicKeyHash: String? = ""
)
// Notary whitelist may contain multiple identities with the same X.500 name after certificate rotation. // Notary whitelist may contain multiple identities with the same X.500 name after certificate rotation.
// Exclude duplicated entries, which are not present in the network map. // Exclude duplicated entries, which are not present in the network map.
override val notaryIdentities: List<Party> get() = notaries.map { it.identity } override val notaryIdentities: List<Party> get() = notaries.map { it.identity }
@ -294,6 +310,7 @@ open class PersistentNetworkMapCache(cacheFactory: NamedCacheFactory,
synchronized(_changed) { synchronized(_changed) {
database.transaction { database.transaction {
removeInfoDB(session, node) removeInfoDB(session, node)
archiveNamedIdentity(session, node)
changePublisher.onNext(MapChange.Removed(node)) changePublisher.onNext(MapChange.Removed(node))
} }
} }
@ -302,6 +319,16 @@ open class PersistentNetworkMapCache(cacheFactory: NamedCacheFactory,
logger.debug { "Done removing node with info: $node" } logger.debug { "Done removing node with info: $node" }
} }
private fun archiveNamedIdentity(session: Session, nodeInfo: NodeInfo) {
nodeInfo.legalIdentities.forEach { party ->
val deleteQuery = session.criteriaBuilder.createCriteriaDelete(PersistentPartyToPublicKeyHash::class.java)
val queryRoot = deleteQuery.from(PersistentPartyToPublicKeyHash::class.java)
deleteQuery.where(session.criteriaBuilder.equal(queryRoot.get<String>("name"), party.name.toString()))
session.createQuery(deleteQuery).executeUpdate()
session.save(PersistentPartyToPublicKeyHash(party.name.toString(), party.owningKey.toStringShort() ))
}
}
override val allNodes: List<NodeInfo> override val allNodes: List<NodeInfo>
get() { get() {
return database.transaction { return database.transaction {

View File

@ -14,6 +14,7 @@ import net.corda.node.services.events.NodeSchedulerService
import net.corda.node.services.identity.PersistentIdentityService import net.corda.node.services.identity.PersistentIdentityService
import net.corda.node.services.keys.BasicHSMKeyManagementService import net.corda.node.services.keys.BasicHSMKeyManagementService
import net.corda.node.services.messaging.P2PMessageDeduplicator import net.corda.node.services.messaging.P2PMessageDeduplicator
import net.corda.node.services.network.PersistentNetworkMapCache
import net.corda.node.services.persistence.DBCheckpointStorage import net.corda.node.services.persistence.DBCheckpointStorage
import net.corda.node.services.persistence.DBTransactionStorage import net.corda.node.services.persistence.DBTransactionStorage
import net.corda.node.services.persistence.NodeAttachmentService import net.corda.node.services.persistence.NodeAttachmentService
@ -49,7 +50,8 @@ class NodeSchemaService(private val extraSchemas: Set<MappedSchema> = emptySet()
PersistentIdentityService.PersistentHashToPublicKey::class.java, PersistentIdentityService.PersistentHashToPublicKey::class.java,
ContractUpgradeServiceImpl.DBContractUpgrade::class.java, ContractUpgradeServiceImpl.DBContractUpgrade::class.java,
DBNetworkParametersStorage.PersistentNetworkParameters::class.java, DBNetworkParametersStorage.PersistentNetworkParameters::class.java,
PublicKeyHashToExternalId::class.java PublicKeyHashToExternalId::class.java,
PersistentNetworkMapCache.PersistentPartyToPublicKeyHash::class.java
)) { )) {
override val migrationResource = "node-core.changelog-master" override val migrationResource = "node-core.changelog-master"
} }

View File

@ -27,6 +27,7 @@
<include file="migration/node-core.changelog-v14-data.xml"/> <include file="migration/node-core.changelog-v14-data.xml"/>
<include file="migration/node-core.changelog-v16.xml"/> <include file="migration/node-core.changelog-v16.xml"/>
<include file="migration/node-core.changelog-v20.xml"/> <include file="migration/node-core.changelog-v20.xml"/>
<include file="migration/node-core.changelog-v22.xml"/>
<!-- This must run after node-core.changelog-init.xml, to prevent database columns being created twice. --> <!-- This must run after node-core.changelog-init.xml, to prevent database columns being created twice. -->
<include file="migration/vault-schema.changelog-v9.xml"/> <include file="migration/vault-schema.changelog-v9.xml"/>

View File

@ -0,0 +1,13 @@
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.5.xsd">
<changeSet author="R3.Corda" id="add_node_named_identities_table_back_in">
<createTable tableName="node_named_identities">
<column name="name" type="NVARCHAR(128)">
<constraints nullable="false"/>
</column>
<column name="pk_hash" type="NVARCHAR(130)"/>
</createTable>
</changeSet>
</databaseChangeLog>

View File

@ -50,7 +50,7 @@ object IdentityTestSchemaV1 : MappedSchema(
@Column(name = "name", length = 128, nullable = false) @Column(name = "name", length = 128, nullable = false)
var name: String = "", var name: String = "",
@Column(name = "pk_hash", length = MAX_HASH_HEX_SIZE, nullable = false) @Column(name = "pk_hash", length = MAX_HASH_HEX_SIZE, nullable = true)
var publicKeyHash: String = "" var publicKeyHash: String = ""
) )

View File

@ -1,16 +1,22 @@
package net.corda.node.services.network package net.corda.node.services.network
import net.corda.core.crypto.toStringShort import net.corda.core.crypto.toStringShort
import net.corda.core.identity.CordaX500Name
import net.corda.core.node.services.NetworkMapCache import net.corda.core.node.services.NetworkMapCache
import net.corda.core.serialization.serialize import net.corda.core.serialization.serialize
import net.corda.node.services.api.NetworkMapCacheInternal import net.corda.node.services.api.NetworkMapCacheInternal
import net.corda.testing.core.ALICE_NAME import net.corda.testing.core.ALICE_NAME
import net.corda.testing.core.BOB_NAME import net.corda.testing.core.BOB_NAME
import net.corda.testing.core.CHARLIE_NAME
import net.corda.testing.core.singleIdentity import net.corda.testing.core.singleIdentity
import net.corda.testing.node.internal.InternalMockNetwork import net.corda.testing.node.internal.InternalMockNetwork
import net.corda.testing.node.internal.InternalMockNodeParameters import net.corda.testing.node.internal.InternalMockNodeParameters
import net.corda.testing.node.internal.TestStartedNode
import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThat
import org.junit.After import org.junit.After
import org.junit.Assert
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Test import org.junit.Test
import java.math.BigInteger import java.math.BigInteger
import kotlin.test.assertEquals import kotlin.test.assertEquals
@ -18,6 +24,7 @@ import kotlin.test.assertNotNull
import kotlin.test.assertNull import kotlin.test.assertNull
class NetworkMapCacheTest { class NetworkMapCacheTest {
private val TestStartedNode.party get() = info.legalIdentities.first()
private val mockNet = InternalMockNetwork() private val mockNet = InternalMockNetwork()
@After @After
@ -25,6 +32,153 @@ class NetworkMapCacheTest {
mockNet.stopNodes() mockNet.stopNodes()
} }
@Test(timeout=300_000)
fun `unknown Party object gets recorded as null entry in node_named_identities table`() {
val bobNode = mockNet.createPartyNode(BOB_NAME)
assertEquals(null, bobNode.services.identityService.wellKnownPartyFromX500Name(CHARLIE_NAME))
bobNode.database.transaction {
val cb = session.criteriaBuilder
val query = cb.createQuery(PersistentNetworkMapCache.PersistentPartyToPublicKeyHash::class.java)
val root = query.from(PersistentNetworkMapCache.PersistentPartyToPublicKeyHash::class.java)
val matchPublicKey = cb.isNull(root.get<String>("publicKeyHash"))
val matchName = cb.equal(root.get<String>("name"), CHARLIE_NAME.toString())
query.select(root).where(cb.and(matchName, matchPublicKey))
val resultList = session.createQuery(query).resultList
assertEquals(1, resultList.size)
}
}
@Test(timeout=300_000)
fun `check Party object can still be retrieved when not in node_named_identities table`() {
val aliceNode = mockNet.createPartyNode(ALICE_NAME)
val bobNode = mockNet.createPartyNode(BOB_NAME)
val bobCache: NetworkMapCache = bobNode.services.networkMapCache
val bobCacheInternal = bobCache as NetworkMapCacheInternal
assertNotNull(bobCacheInternal)
bobCache.removeNode(aliceNode.info)
val alicePubKeyHash = aliceNode.info.legalIdentities[0].owningKey.toStringShort()
// Remove node adds an entry to the PersistentPartyToPublicKeyHash, so for this test delete this entry.
removeNodeFromNodeNamedIdentitiesTable(bobNode, alicePubKeyHash)
assertEquals(aliceNode.party, bobNode.services.identityService.wellKnownPartyFromX500Name(ALICE_NAME))
assertEquals(1, queryNodeNamedIdentities(bobNode, ALICE_NAME, alicePubKeyHash).size)
}
private fun removeNodeFromNodeNamedIdentitiesTable(node: TestStartedNode, publicKeyHashToRemove: String) {
// Remove node adds an entry to the PersistentPartyToPublicKeyHash, so for this test delete this entry.
node.database.transaction {
val deleteQuery = session.criteriaBuilder.createCriteriaDelete(PersistentNetworkMapCache.PersistentPartyToPublicKeyHash::class.java)
val queryRoot = deleteQuery.from(PersistentNetworkMapCache.PersistentPartyToPublicKeyHash::class.java)
deleteQuery.where(session.criteriaBuilder.equal(queryRoot.get<String>("publicKeyHash"), publicKeyHashToRemove))
session.createQuery(deleteQuery).executeUpdate()
}
}
private fun queryNodeNamedIdentities(node: TestStartedNode, party: CordaX500Name, publicKeyHash: String): List<PersistentNetworkMapCache.PersistentPartyToPublicKeyHash> {
return node.database.transaction {
val cb = session.criteriaBuilder
val query = cb.createQuery(PersistentNetworkMapCache.PersistentPartyToPublicKeyHash::class.java)
val root = query.from(PersistentNetworkMapCache.PersistentPartyToPublicKeyHash::class.java)
val matchPublicKeyHash = cb.equal(root.get<String>("publicKeyHash"), publicKeyHash)
val matchName = cb.equal(root.get<String>("name"), party.toString())
query.select(root).where(cb.and(matchName, matchPublicKeyHash))
session.createQuery(query).resultList
}
}
@Test(timeout=300_000)
fun `check removed node is inserted into node_name_identities table and then its Party object can be retrieved`() {
val aliceNode = mockNet.createPartyNode(ALICE_NAME)
val bobNode = mockNet.createPartyNode(BOB_NAME)
val bobCache: NetworkMapCache = bobNode.services.networkMapCache
val bobCacheInternal = bobCache as NetworkMapCacheInternal
assertNotNull(bobCacheInternal)
val aliceParty1 = bobNode.services.identityService.wellKnownPartyFromX500Name(ALICE_NAME)
println("alicePart1 = $aliceParty1")
bobCache.removeNode(aliceNode.info)
val alicePubKeyHash = aliceNode.info.legalIdentities[0].owningKey.toStringShort()
assertEquals(1, queryNodeNamedIdentities(bobNode, ALICE_NAME, alicePubKeyHash).size)
assertEquals(aliceNode.party, bobNode.services.identityService.wellKnownPartyFromX500Name(ALICE_NAME))
}
@Test(timeout=300_000)
fun `check two removed nodes are both archived and then both Party objects are retrievable`() {
val aliceNode = mockNet.createPartyNode(ALICE_NAME)
val bobNode = mockNet.createPartyNode(BOB_NAME)
val charlieNode = mockNet.createPartyNode(CHARLIE_NAME)
val bobCache: NetworkMapCache = bobNode.services.networkMapCache
val bobCacheInternal = bobCache as NetworkMapCacheInternal
assertNotNull(bobCacheInternal)
bobCache.removeNode(aliceNode.info)
bobCache.removeNode(charlieNode.info)
val alicePubKeyHash = aliceNode.info.legalIdentities[0].owningKey.toStringShort()
val charliePubKeyHash = charlieNode.info.legalIdentities[0].owningKey.toStringShort()
assertEquals(1, queryNodeNamedIdentities(bobNode, ALICE_NAME, alicePubKeyHash).size)
assertEquals(1, queryNodeNamedIdentities(bobNode, CHARLIE_NAME, charliePubKeyHash).size)
assertEquals(aliceNode.party, bobNode.services.identityService.wellKnownPartyFromX500Name(ALICE_NAME))
assertEquals(charlieNode.party, bobNode.services.identityService.wellKnownPartyFromX500Name(CHARLIE_NAME))
}
@Test(timeout=300_000)
fun `check latest identity returned according to certificate after identity mock rotatated`() {
val aliceNode1 = mockNet.createPartyNode(ALICE_NAME)
val bobNode = mockNet.createPartyNode(BOB_NAME)
val bobCache: NetworkMapCache = bobNode.services.networkMapCache
val alicePubKeyHash1 = aliceNode1.info.legalIdentities[0].owningKey.toStringShort()
val bobCacheInternal = bobCache as NetworkMapCacheInternal
assertNotNull(bobCacheInternal)
bobCache.removeNode(aliceNode1.info)
// Remove node adds an entry to the PersistentPartyToPublicKeyHash, so for this test delete this entry.
removeNodeFromNodeNamedIdentitiesTable(bobNode, alicePubKeyHash1)
val aliceNode2 = mockNet.createPartyNode(ALICE_NAME)
val alicePubKeyHash2 = aliceNode2.info.legalIdentities[0].owningKey.toStringShort()
bobCache.removeNode(aliceNode2.info)
// Remove node adds an entry to the PersistentPartyToPublicKeyHash, so for this test delete this entry.
removeNodeFromNodeNamedIdentitiesTable(bobNode, alicePubKeyHash2)
val retrievedParty = bobNode.services.identityService.wellKnownPartyFromX500Name(ALICE_NAME)
// For both identity certificates the valid from date is the start of the day, so either could be returned.
assertTrue(aliceNode2.party == retrievedParty || aliceNode1.party == retrievedParty)
}
@Test(timeout=300_000)
fun `latest identity is archived after identity rotated`() {
var aliceNode = mockNet.createPartyNode(ALICE_NAME)
val bobNode = mockNet.createPartyNode(BOB_NAME)
val bobCache: NetworkMapCache = bobNode.services.networkMapCache
val bobCacheInternal = bobCache as NetworkMapCacheInternal
assertNotNull(bobCacheInternal)
bobCache.removeNode(aliceNode.info)
fun checkArchivedIdentity(bobNode: TestStartedNode, aliceNode: TestStartedNode) {
val alicePubKeyHash = aliceNode.info.legalIdentities[0].owningKey.toStringShort()
bobNode.database.transaction {
val hashToIdentityStatement = database.dataSource.connection.prepareStatement("SELECT name, pk_hash FROM node_named_identities WHERE pk_hash=?")
hashToIdentityStatement.setString(1, alicePubKeyHash)
val aliceResultSet = hashToIdentityStatement.executeQuery()
Assert.assertTrue(aliceResultSet.next())
Assert.assertEquals(ALICE_NAME.toString(), aliceResultSet.getString("name"))
Assert.assertEquals(alicePubKeyHash.toString(), aliceResultSet.getString("pk_hash"))
Assert.assertFalse(aliceResultSet.next())
}
}
checkArchivedIdentity(bobNode, aliceNode)
aliceNode.dispose()
aliceNode = mockNet.createPartyNode(ALICE_NAME)
bobCache.removeNode(aliceNode.info)
checkArchivedIdentity(bobNode, aliceNode)
}
@Test(timeout=300_000) @Test(timeout=300_000)
fun `key collision`() { fun `key collision`() {
val entropy = BigInteger.valueOf(24012017L) val entropy = BigInteger.valueOf(24012017L)