CORDA-2745: Cache notary identity lookups (#4892)

Add a cache for notary identities in the PersistentIdentityService. This
solves a reported problem where notary identity lookup fails if its network
map entry is missing, which results in an exception when trying to insert
a state into the vault after recording a transaction.
This commit is contained in:
Andrius Dagys 2019-03-15 11:14:48 +01:00 committed by GitHub
parent 82c45c6f83
commit e3ada049d4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 19 additions and 7 deletions

View File

@ -343,7 +343,7 @@ abstract class AbstractNode<S>(val configuration: NodeConfiguration,
X509Utilities.validateCertPath(trustRoot, identity.certPath)
val nodeCa = configuration.signingCertificateStore.get()[CORDA_CLIENT_CA]
identityService.start(trustRoot, listOf(identity.certificate, nodeCa))
identityService.start(trustRoot, listOf(identity.certificate, nodeCa), netParams.notaries.map { it.identity })
val (keyPairs, nodeInfoAndSigned, myNotaryIdentity) = database.transaction {
updateNodeInfo(identity, identityKeyPair, publish = true)

View File

@ -101,16 +101,20 @@ class PersistentIdentityService(cacheFactory: NamedCacheFactory) : SingletonSeri
private lateinit var _trustAnchor: TrustAnchor
override val trustAnchor: TrustAnchor get() = _trustAnchor
/** Stores notary identities obtained from the network parameters, for which we don't need to perform a database lookup. */
private val notaryIdentityCache = HashSet<Party>()
// CordaPersistence is not a c'tor parameter to work around the cyclic dependency
lateinit var database: CordaPersistence
private val keyToParties = createPKMap(cacheFactory)
private val principalToParties = createX500Map(cacheFactory)
fun start(trustRoot: X509Certificate, caCertificates: List<X509Certificate> = emptyList()) {
fun start(trustRoot: X509Certificate, caCertificates: List<X509Certificate> = emptyList(), notaryIdentities: List<Party> = emptyList()) {
_trustRoot = trustRoot
_trustAnchor = TrustAnchor(trustRoot, null)
_caCertStore = CertStore.getInstance("Collection", CollectionCertStoreParameters(caCertificates.toSet() + trustRoot))
notaryIdentityCache.addAll(notaryIdentities)
}
fun loadIdentities(identities: Collection<PartyAndCertificate> = emptySet(), confidentialIdentities: Collection<PartyAndCertificate> = emptySet()) {
@ -170,7 +174,16 @@ class PersistentIdentityService(cacheFactory: NamedCacheFactory) : SingletonSeri
override fun wellKnownPartyFromX500Name(name: CordaX500Name): Party? = certificateFromCordaX500Name(name)?.party
override fun wellKnownPartyFromAnonymous(party: AbstractParty): Party? = database.transaction { super.wellKnownPartyFromAnonymous(party) }
override fun wellKnownPartyFromAnonymous(party: AbstractParty): Party? {
// Skip database lookup if the party is a notary identity.
// This also prevents an issue where the notary identity can't be resolved if it's not in the network map cache. The node obtains
// a trusted list of notary identities from the network parameters automatically.
return if (party is Party && party in notaryIdentityCache) {
party
} else {
database.transaction { super.wellKnownPartyFromAnonymous(party) }
}
}
override fun partiesFromName(query: String, exactMatch: Boolean): Set<Party> {
return database.transaction {
@ -194,5 +207,4 @@ class PersistentIdentityService(cacheFactory: NamedCacheFactory) : SingletonSeri
fun stripNotOurKeys(keys: Iterable<PublicKey>): Iterable<PublicKey> {
return keys.filter { certificateFromKey(it)?.name in ourNames }
}
}

View File

@ -8,8 +8,8 @@ import javax.persistence.AttributeConverter
import javax.persistence.Converter
/**
* Converter to persist a party as its' well known identity (where resolvable).
* Completely anonymous parties are stored as null (to preserve privacy).
* Converter to persist a party as its well known identity (where resolvable).
* Completely anonymous parties are stored as *null* (to preserve privacy).
*/
@Converter(autoApply = true)
class AbstractPartyToX500NameAsStringConverter(private val wellKnownPartyFromX500Name: (CordaX500Name) -> Party?,
@ -24,7 +24,7 @@ class AbstractPartyToX500NameAsStringConverter(private val wellKnownPartyFromX50
if (partyName != null) return partyName
log.warn("Identity service unable to resolve AbstractParty: $party")
}
return null // non resolvable anonymous parties
return null // non resolvable anonymous parties are stored as nulls
}
override fun convertToEntityAttribute(dbData: String?): AbstractParty? {