mirror of
https://github.com/corda/corda.git
synced 2025-04-07 11:27:01 +00:00
CORDA-535: Remove the old mechanism for loading custom notary service implementations.
All notary service implementations are now assumed to be loaded from CorDapps.
This commit is contained in:
parent
554b1fa371
commit
b8b2cc772d
@ -4,13 +4,12 @@ Writing a custom notary service (experimental)
|
||||
==============================================
|
||||
|
||||
.. warning:: Customising a notary service is still an experimental feature and not recommended for most use-cases. The APIs
|
||||
for writing a custom notary may change in the future. Additionally, customising Raft or BFT notaries is not yet
|
||||
fully supported. If you want to write your own Raft notary you will have to implement a custom database connector
|
||||
(or use a separate database for the notary), and use a custom configuration file.
|
||||
for writing a custom notary may change in the future.
|
||||
|
||||
Similarly to writing an oracle service, the first step is to create a service class in your CorDapp and annotate it
|
||||
with ``@CordaService``. The Corda node scans for any class with this annotation and initialises them. The custom notary
|
||||
service class should provide a constructor with two parameters of types ``AppServiceHub`` and ``PublicKey``.
|
||||
The first step is to create a service class in your CorDapp that extends the ``NotaryService`` abstract class.
|
||||
This will ensure that it is recognised as a notary service.
|
||||
The custom notary service class should provide a constructor with two parameters of types ``ServiceHubInternal`` and ``PublicKey``.
|
||||
Note that ``ServiceHubInternal`` does not provide any API stability guarantees.
|
||||
|
||||
.. literalinclude:: ../../samples/notary-demo/src/main/kotlin/net/corda/notarydemo/MyCustomNotaryService.kt
|
||||
:language: kotlin
|
||||
@ -32,5 +31,5 @@ To enable the service, add the following to the node configuration:
|
||||
|
||||
notary : {
|
||||
validating : true # Set to false if your service is non-validating
|
||||
custom : true
|
||||
className : "net.corda.notarydemo.MyCustomValidatingNotaryService" # The fully qualified name of your service class
|
||||
}
|
@ -42,9 +42,12 @@ import net.corda.node.services.ContractUpgradeHandler
|
||||
import net.corda.node.services.FinalityHandler
|
||||
import net.corda.node.services.NotaryChangeHandler
|
||||
import net.corda.node.services.api.*
|
||||
import net.corda.node.services.config.*
|
||||
import net.corda.node.services.config.NodeConfiguration
|
||||
import net.corda.node.services.config.NotaryConfig
|
||||
import net.corda.node.services.config.configureWithDevSSLCertificate
|
||||
import net.corda.node.services.config.rpc.NodeRpcOptions
|
||||
import net.corda.node.services.config.shell.toShellConfig
|
||||
import net.corda.node.services.config.shouldInitCrashShell
|
||||
import net.corda.node.services.events.NodeSchedulerService
|
||||
import net.corda.node.services.events.ScheduledActivityObserver
|
||||
import net.corda.node.services.identity.PersistentIdentityService
|
||||
@ -59,7 +62,8 @@ import net.corda.node.services.network.PersistentNetworkMapCache
|
||||
import net.corda.node.services.persistence.*
|
||||
import net.corda.node.services.schema.NodeSchemaService
|
||||
import net.corda.node.services.statemachine.*
|
||||
import net.corda.node.services.transactions.*
|
||||
import net.corda.node.services.transactions.InMemoryTransactionVerifierService
|
||||
import net.corda.node.services.transactions.SimpleNotaryService
|
||||
import net.corda.node.services.upgrade.ContractUpgradeServiceImpl
|
||||
import net.corda.node.services.vault.NodeVaultService
|
||||
import net.corda.node.utilities.*
|
||||
@ -518,9 +522,9 @@ abstract class AbstractNode<S>(val configuration: NodeConfiguration,
|
||||
|
||||
private fun installCordaServices(myNotaryIdentity: PartyAndCertificate?) {
|
||||
val loadedServices = cordappLoader.cordapps.flatMap { it.services }
|
||||
filterServicesToInstall(loadedServices).forEach {
|
||||
loadedServices.forEach {
|
||||
try {
|
||||
installCordaService(flowStarter, it, myNotaryIdentity)
|
||||
installCordaService(flowStarter, it)
|
||||
} catch (e: NoSuchMethodException) {
|
||||
log.error("${it.name}, as a Corda service, must have a constructor with a single parameter of type " +
|
||||
ServiceHub::class.java.name)
|
||||
@ -532,24 +536,6 @@ abstract class AbstractNode<S>(val configuration: 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 enabled 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.
|
||||
*/
|
||||
@ -590,53 +576,30 @@ abstract class AbstractNode<S>(val configuration: NodeConfiguration,
|
||||
override fun hashCode() = Objects.hash(serviceHub, flowStarter, serviceInstance)
|
||||
}
|
||||
|
||||
private fun <T : SerializeAsToken> installCordaService(flowStarter: FlowStarter, serviceClass: Class<T>, myNotaryIdentity: PartyAndCertificate?) {
|
||||
private fun <T : SerializeAsToken> installCordaService(flowStarter: FlowStarter, serviceClass: Class<T>) {
|
||||
serviceClass.requireAnnotation<CordaService>()
|
||||
|
||||
val service = try {
|
||||
if (isNotaryService(serviceClass)) {
|
||||
myNotaryIdentity ?: throw IllegalStateException("Trying to install a notary service but no notary identity specified")
|
||||
try {
|
||||
val constructor = serviceClass.getDeclaredConstructor(ServiceHubInternal::class.java, PublicKey::class.java).apply { isAccessible = true }
|
||||
constructor.newInstance(services, myNotaryIdentity.owningKey )
|
||||
} catch (ex: NoSuchMethodException) {
|
||||
val constructor = serviceClass.getDeclaredConstructor(AppServiceHub::class.java, PublicKey::class.java).apply { isAccessible = true }
|
||||
val serviceContext = AppServiceHubImpl<T>(services, flowStarter)
|
||||
val service = constructor.newInstance(serviceContext, myNotaryIdentity.owningKey)
|
||||
serviceContext.serviceInstance = service
|
||||
service
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
val serviceContext = AppServiceHubImpl<T>(services, flowStarter)
|
||||
val extendedServiceConstructor = serviceClass.getDeclaredConstructor(AppServiceHub::class.java).apply { isAccessible = true }
|
||||
val service = extendedServiceConstructor.newInstance(serviceContext)
|
||||
serviceContext.serviceInstance = service
|
||||
service
|
||||
} catch (ex: NoSuchMethodException) {
|
||||
val constructor = serviceClass.getDeclaredConstructor(ServiceHub::class.java).apply { isAccessible = true }
|
||||
log.warn("${serviceClass.name} is using legacy CordaService constructor with ServiceHub parameter. " +
|
||||
"Upgrade to an AppServiceHub parameter to enable updated API features.")
|
||||
constructor.newInstance(services)
|
||||
}
|
||||
}
|
||||
val serviceContext = AppServiceHubImpl<T>(services, flowStarter)
|
||||
val extendedServiceConstructor = serviceClass.getDeclaredConstructor(AppServiceHub::class.java).apply { isAccessible = true }
|
||||
val service = extendedServiceConstructor.newInstance(serviceContext)
|
||||
serviceContext.serviceInstance = service
|
||||
service
|
||||
} catch (ex: NoSuchMethodException) {
|
||||
val constructor = serviceClass.getDeclaredConstructor(ServiceHub::class.java).apply { isAccessible = true }
|
||||
log.warn("${serviceClass.name} is using legacy CordaService constructor with ServiceHub parameter. " +
|
||||
"Upgrade to an AppServiceHub parameter to enable updated API features.")
|
||||
constructor.newInstance(services)
|
||||
} catch (e: InvocationTargetException) {
|
||||
throw ServiceInstantiationException(e.cause)
|
||||
}
|
||||
|
||||
cordappServices.putInstance(serviceClass, service)
|
||||
|
||||
if (service is NotaryService) handleCustomNotaryService(service)
|
||||
service.tokenize()
|
||||
log.info("Installed ${serviceClass.name} Corda service")
|
||||
}
|
||||
|
||||
private fun handleCustomNotaryService(service: NotaryService) {
|
||||
runOnStop += service::stop
|
||||
installCoreFlow(NotaryFlow.Client::class, service::createServiceFlow)
|
||||
service.start()
|
||||
}
|
||||
|
||||
private fun registerCordappFlows() {
|
||||
cordappLoader.cordapps.flatMap { it.initiatedFlows }
|
||||
.forEach {
|
||||
|
@ -122,13 +122,12 @@ fun NodeConfiguration.shouldInitCrashShell() = shouldStartLocalShell() || should
|
||||
data class NotaryConfig(val validating: Boolean,
|
||||
val raft: RaftConfig? = null,
|
||||
val bftSMaRt: BFTSMaRtConfiguration? = null,
|
||||
val custom: Boolean = false,
|
||||
val serviceLegalName: CordaX500Name? = null,
|
||||
val className: String = "net.corda.node.services.transactions.SimpleNotaryService"
|
||||
) {
|
||||
init {
|
||||
require(raft == null || bftSMaRt == null || !custom) {
|
||||
"raft, bftSMaRt, and custom configs cannot be specified together"
|
||||
require(raft == null || bftSMaRt == null) {
|
||||
"raft and bftSMaRt configs cannot be specified together"
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -88,7 +88,6 @@ class TimedFlowTests {
|
||||
|
||||
val networkParameters = NetworkParametersCopier(testNetworkParameters(listOf(NotaryInfo(notaryIdentity, true))))
|
||||
val notaryConfig = mock<NotaryConfig> {
|
||||
whenever(it.custom).thenReturn(true)
|
||||
whenever(it.isClusterConfig).thenReturn(true)
|
||||
whenever(it.validating).thenReturn(true)
|
||||
whenever(it.className).thenReturn(TestNotaryService::class.java.name)
|
||||
|
@ -76,7 +76,10 @@ task deployNodesCustom(type: Cordform, dependsOn: 'jar') {
|
||||
address "localhost:10010"
|
||||
adminAddress "localhost:10110"
|
||||
}
|
||||
notary = [validating: true, "custom": true]
|
||||
notary = [
|
||||
validating: true,
|
||||
className: "net.corda.notarydemo.MyCustomValidatingNotaryService"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -8,8 +8,6 @@ import net.corda.core.internal.ResolveTransactionsFlow
|
||||
import net.corda.core.internal.notary.NotaryInternalException
|
||||
import net.corda.core.internal.notary.NotaryServiceFlow
|
||||
import net.corda.core.internal.notary.TrustedAuthorityNotaryService
|
||||
import net.corda.core.node.AppServiceHub
|
||||
import net.corda.core.node.services.CordaService
|
||||
import net.corda.core.transactions.SignedTransaction
|
||||
import net.corda.core.transactions.TransactionWithSignatures
|
||||
import net.corda.core.transactions.WireTransaction
|
||||
@ -19,13 +17,12 @@ import java.security.PublicKey
|
||||
import java.security.SignatureException
|
||||
|
||||
/**
|
||||
* A custom notary service should provide a constructor that accepts two parameters of types [AppServiceHub] and [PublicKey].
|
||||
* A custom notary service should provide a constructor that accepts two parameters of types [ServiceHubInternal] and [PublicKey].
|
||||
*
|
||||
* Note that the support for custom notaries is still experimental – at present only a single-node notary service can be customised.
|
||||
* The notary-related APIs might change in the future.
|
||||
*/
|
||||
// START 1
|
||||
@CordaService
|
||||
class MyCustomValidatingNotaryService(override val services: ServiceHubInternal, override val notaryIdentityKey: PublicKey) : TrustedAuthorityNotaryService() {
|
||||
override val uniquenessProvider = PersistentUniquenessProvider(services.clock, services.database, services.cacheFactory)
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user