Fixed AbstractNode to load custom notary services properly (#1720)

* Fixed AbstractNode to load custom notary services properly.
Added a custom notary sample.

* Prevent multiple custom notaries from being loaded

* Throw if more than once custom notary service is loaded
This commit is contained in:
Andrius Dagys
2017-10-13 10:36:25 +01:00
committed by GitHub
parent 544b761682
commit 7b10e92819
11 changed files with 148 additions and 45 deletions

View File

@ -19,14 +19,11 @@ import net.corda.core.internal.concurrent.flatMap
import net.corda.core.internal.concurrent.openFuture
import net.corda.core.internal.toX509CertHolder
import net.corda.core.internal.uncheckedCast
import net.corda.core.messaging.CordaRPCOps
import net.corda.core.messaging.RPCOps
import net.corda.core.messaging.SingleMessageRecipient
import net.corda.core.node.*
import net.corda.core.messaging.*
import net.corda.core.node.AppServiceHub
import net.corda.core.node.NodeInfo
import net.corda.core.node.ServiceHub
import net.corda.core.node.StateLoader
import net.corda.core.node.services.*
import net.corda.core.node.services.NetworkMapCache.MapChange
import net.corda.core.schemas.MappedSchema
@ -257,7 +254,8 @@ abstract class AbstractNode(config: NodeConfiguration,
private class ServiceInstantiationException(cause: Throwable?) : CordaException("Service Instantiation Error", cause)
private fun installCordaServices() {
cordappProvider.cordapps.flatMap { it.services }.forEach {
val loadedServices = cordappProvider.cordapps.flatMap { it.services }
filterServicesToInstall(loadedServices).forEach {
try {
installCordaService(it)
} catch (e: NoSuchMethodException) {
@ -271,6 +269,25 @@ abstract class AbstractNode(config: NodeConfiguration,
}
}
private fun filterServicesToInstall(loadedServices: List<Class<out SerializeAsToken>>): List<Class<out SerializeAsToken>> {
val customNotaryServiceList = loadedServices.filter { isNotaryService(it) }
if (customNotaryServiceList.isNotEmpty()) {
if (configuration.notary?.custom == true) {
require(customNotaryServiceList.size == 1) {
"Attempting to install more than one notary service: ${customNotaryServiceList.joinToString()}"
}
}
else return loadedServices - customNotaryServiceList
}
return loadedServices
}
/**
* If the [serviceClass] is a notary service, it will only be enable if the "custom" flag is set in
* the notary configuration.
*/
private fun isNotaryService(serviceClass: Class<*>) = NotaryService::class.java.isAssignableFrom(serviceClass)
/**
* This customizes the ServiceHub for each CordaService that is initiating flows
*/
@ -321,14 +338,15 @@ abstract class AbstractNode(config: NodeConfiguration,
fun <T : SerializeAsToken> installCordaService(serviceClass: Class<T>): T {
serviceClass.requireAnnotation<CordaService>()
val service = try {
if (NotaryService::class.java.isAssignableFrom(serviceClass)) {
val serviceContext = AppServiceHubImpl<T>(services)
if (isNotaryService(serviceClass)) {
check(myNotaryIdentity != null) { "Trying to install a notary service but no notary identity specified" }
val constructor = serviceClass.getDeclaredConstructor(ServiceHub::class.java, PublicKey::class.java).apply { isAccessible = true }
constructor.newInstance(services, myNotaryIdentity!!.owningKey)
val constructor = serviceClass.getDeclaredConstructor(AppServiceHub::class.java, PublicKey::class.java).apply { isAccessible = true }
serviceContext.serviceInstance = constructor.newInstance(serviceContext, myNotaryIdentity!!.owningKey)
serviceContext.serviceInstance
} else {
try {
val extendedServiceConstructor = serviceClass.getDeclaredConstructor(AppServiceHub::class.java).apply { isAccessible = true }
val serviceContext = AppServiceHubImpl<T>(services)
serviceContext.serviceInstance = extendedServiceConstructor.newInstance(serviceContext)
serviceContext.serviceInstance
} catch (ex: NoSuchMethodException) {
@ -688,7 +706,9 @@ abstract class AbstractNode(config: NodeConfiguration,
// Node's main identity
Pair("identity", myLegalName)
} else {
val notaryId = notaryConfig.run { NotaryService.constructId(validating, raft != null, bftSMaRt != null) }
val notaryId = notaryConfig.run {
NotaryService.constructId(validating, raft != null, bftSMaRt != null, custom)
}
if (notaryConfig.bftSMaRt == null && notaryConfig.raft == null) {
// Node's notary identity
Pair(notaryId, myLegalName.copy(commonName = notaryId))

View File

@ -36,9 +36,15 @@ interface NodeConfiguration : NodeSSLConfiguration {
val additionalNodeInfoPollingFrequencyMsec: Long
}
data class NotaryConfig(val validating: Boolean, val raft: RaftConfig? = null, val bftSMaRt: BFTSMaRtConfiguration? = null) {
data class NotaryConfig(val validating: Boolean,
val raft: RaftConfig? = null,
val bftSMaRt: BFTSMaRtConfiguration? = null,
val custom: Boolean = false
) {
init {
require(raft == null || bftSMaRt == null) { "raft and bftSMaRt configs cannot be specified together" }
require(raft == null || bftSMaRt == null || !custom) {
"raft, bftSMaRt, and custom configs cannot be specified together"
}
}
}
@ -46,9 +52,10 @@ data class RaftConfig(val nodeAddress: NetworkHostAndPort, val clusterAddresses:
/** @param exposeRaces for testing only, so its default is not in reference.conf but here. */
data class BFTSMaRtConfiguration constructor(val replicaId: Int,
val clusterAddresses: List<NetworkHostAndPort>,
val debug: Boolean = false,
val exposeRaces: Boolean = false) {
val clusterAddresses: List<NetworkHostAndPort>,
val debug: Boolean = false,
val exposeRaces: Boolean = false
) {
init {
require(replicaId >= 0) { "replicaId cannot be negative" }
}