4.3 KiB
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.
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.
@CordaService
class MyCustomValidatingNotaryService(override val services: AppServiceHub, override val notaryIdentityKey: PublicKey) : TrustedAuthorityNotaryService() {
override val uniquenessProvider = PersistentUniquenessProvider(services.clock)
override fun createServiceFlow(otherPartySession: FlowSession): FlowLogic<Void?> = MyValidatingNotaryFlow(otherPartySession, this)
override fun start() {}
override fun stop() {}
}
The next step is to write a notary service flow. You are free to copy
and modify the existing built-in flows such as
ValidatingNotaryFlow
, NonValidatingNotaryFlow
,
or implement your own from scratch (following the
NotaryFlow.Service
template). Below is an example of a
custom flow for a validating notary service:
class MyValidatingNotaryFlow(otherSide: FlowSession, service: MyCustomValidatingNotaryService) : NotaryServiceFlow(otherSide, service) {
/**
* The received transaction is checked for contract-validity, for which the caller also has to to reveal the whole
* transaction dependency chain.
*/
@Suspendable
override fun validateRequest(requestPayload: NotarisationPayload): TransactionParts {
try {
val stx = requestPayload.signedTransaction
(NotarisationRequest(stx.inputs, stx.id), requestPayload.requestSignature)
validateRequestSignatureval notary = stx.notary
(notary)
checkNotary(stx)
verifySignatures(stx)
resolveAndContractVerifyval timeWindow: TimeWindow? = if (stx.coreTransaction is WireTransaction) stx.tx.timeWindow else null
return TransactionParts(stx.id, stx.inputs, timeWindow, notary!!)
} catch (e: Exception) {
throw when (e) {
is TransactionVerificationException,
is SignatureException -> NotaryInternalException(NotaryError.TransactionInvalid(e))
else -> e
}
}
}
@Suspendable
private fun resolveAndContractVerify(stx: SignedTransaction) {
(ResolveTransactionsFlow(stx, otherSideSession))
subFlow.verify(serviceHub, false)
stx(stx)
customVerify}
private fun verifySignatures(stx: SignedTransaction) {
val transactionWithSignatures = stx.resolveTransactionWithSignatures(serviceHub)
(transactionWithSignatures)
checkSignatures}
private fun checkSignatures(tx: TransactionWithSignatures) {
try {
.verifySignaturesExcept(service.notaryIdentityKey)
tx} catch (e: SignatureException) {
throw NotaryInternalException(NotaryError.TransactionInvalid(e))
}
}
private fun customVerify(stx: SignedTransaction) {
// Add custom verification logic
}
}
To enable the service, add the following to the node configuration:
- notary : {
-
validating : true # Set to false if your service is non-validating className : "net.corda.notarydemo.MyCustomValidatingNotaryService" # The fully qualified name of your service class
}