Remove notaryIdentityKey from ServiceHub (#1541)

* Remove notaryIdentityKey from ServiceHub

It was redundant, as we have notary field on a transaction. Notaries can
use this field to check if the transaction was meant for them and then
use that information while choosing a key to sign a transaction.

* Move notaryIdentityKey to NotaryService

* Address comments

* Fixes after rebase
This commit is contained in:
Katarzyna Streich 2017-09-20 17:47:45 +01:00 committed by josecoll
parent adb8c5ead2
commit 002c6c4687
19 changed files with 76 additions and 57 deletions

View File

@ -13,6 +13,7 @@ import net.corda.core.node.services.NotaryService
import net.corda.core.node.services.TrustedAuthorityNotaryService
import net.corda.core.node.services.UniquenessProvider
import net.corda.core.serialization.CordaSerializable
import net.corda.core.transactions.FilteredTransaction
import net.corda.core.transactions.SignedTransaction
import net.corda.core.utilities.ProgressTracker
import net.corda.core.utilities.UntrustworthyData
@ -71,7 +72,7 @@ class NotaryFlow {
val tx: Any = if (stx.isNotaryChangeTransaction()) {
stx.notaryChangeTx
} else {
stx.buildFilteredTransaction(Predicate { it is StateRef || it is TimeWindow })
stx.buildFilteredTransaction(Predicate { it is StateRef || it is TimeWindow || it == notaryParty })
}
sendAndReceiveWithRetry(notaryParty, tx)
}
@ -118,7 +119,8 @@ class NotaryFlow {
@Suspendable
override fun call(): Void? {
val (id, inputs, timeWindow) = receiveAndVerifyTx()
val (id, inputs, timeWindow, notary) = receiveAndVerifyTx()
checkNotary(notary)
service.validateTimeWindow(timeWindow)
service.commitInputStates(inputs, id, otherSide)
signAndSendResponse(id)
@ -132,6 +134,13 @@ class NotaryFlow {
@Suspendable
abstract fun receiveAndVerifyTx(): TransactionParts
// Check if transaction is intended to be signed by this notary.
@Suspendable
protected fun checkNotary(notary: Party?) {
if (notary !in serviceHub.myInfo.legalIdentities)
throw NotaryException(NotaryError.WrongNotary)
}
@Suspendable
private fun signAndSendResponse(txId: SecureHash) {
val signature = service.sign(txId)
@ -144,7 +153,7 @@ class NotaryFlow {
* The minimum amount of information needed to notarise a transaction. Note that this does not include
* any sensitive transaction details.
*/
data class TransactionParts(val id: SecureHash, val inputs: List<StateRef>, val timestamp: TimeWindow?)
data class TransactionParts(val id: SecureHash, val inputs: List<StateRef>, val timestamp: TimeWindow?, val notary: Party?)
class NotaryException(val error: NotaryError) : FlowException("Error response from Notary - $error")
@ -160,4 +169,6 @@ sealed class NotaryError {
data class TransactionInvalid(val cause: Throwable) : NotaryError() {
override fun toString() = cause.toString()
}
object WrongNotary: NotaryError()
}

View File

@ -133,20 +133,6 @@ interface ServiceHub : ServicesForResolution {
private val legalIdentityKey: PublicKey get() = this.myInfo.legalIdentitiesAndCerts.first().owningKey
/**
* Helper property to shorten code for fetching the the [PublicKey] portion of the
* Node's Notary signing identity. It is required that the Node hosts a notary service,
* otherwise an [IllegalArgumentException] will be thrown.
* Typical use is during signing in flows and for unit test signing.
* When this [PublicKey] is passed into the signing methods below, or on the KeyManagementService
* the matching [java.security.PrivateKey] will be looked up internally and used to sign.
* If the key is actually a [net.corda.core.crypto.CompositeKey], the first leaf key hosted on this node
* will be used to create the signature.
*/
// TODO Remove that from ServiceHub, we could take that information from a transaction notary field and figure out what key to use from that.
// But, it's separate PR.
val notaryIdentityKey: PublicKey
// Helper method to construct an initial partially signed transaction from a [TransactionBuilder].
private fun signInitialTransaction(builder: TransactionBuilder, publicKey: PublicKey, signatureMetadata: SignatureMetadata): SignedTransaction {
return builder.toSignedTransaction(keyManagementService, publicKey, signatureMetadata)

View File

@ -13,9 +13,11 @@ import net.corda.core.serialization.SingletonSerializeAsToken
import net.corda.core.serialization.serialize
import net.corda.core.utilities.loggerFor
import org.slf4j.Logger
import java.security.PublicKey
abstract class NotaryService : SingletonSerializeAsToken() {
abstract val services: ServiceHub
abstract val notaryIdentityKey: PublicKey
abstract fun start()
abstract fun stop()
@ -70,11 +72,11 @@ abstract class TrustedAuthorityNotaryService : NotaryService() {
}
fun sign(bits: ByteArray): DigitalSignature.WithKey {
return services.keyManagementService.sign(bits, services.notaryIdentityKey)
return services.keyManagementService.sign(bits, notaryIdentityKey)
}
fun sign(txId: SecureHash): TransactionSignature {
val signableData = SignableData(txId, SignatureMetadata(services.myInfo.platformVersion, Crypto.findSignatureScheme(services.notaryIdentityKey).schemeNumberID))
return services.keyManagementService.sign(signableData, services.notaryIdentityKey)
val signableData = SignableData(txId, SignatureMetadata(services.myInfo.platformVersion, Crypto.findSignatureScheme(notaryIdentityKey).schemeNumberID))
return services.keyManagementService.sign(signableData, notaryIdentityKey)
}
}

View File

@ -10,11 +10,12 @@ import net.corda.core.node.services.TimeWindowChecker
import net.corda.core.node.services.TrustedAuthorityNotaryService
import net.corda.core.transactions.SignedTransaction
import net.corda.node.services.transactions.PersistentUniquenessProvider
import java.security.PublicKey
import java.security.SignatureException
// START 1
@CordaService
class MyCustomValidatingNotaryService(override val services: ServiceHub) : TrustedAuthorityNotaryService() {
class MyCustomValidatingNotaryService(override val services: ServiceHub, override val notaryIdentityKey: PublicKey) : TrustedAuthorityNotaryService() {
override val timeWindowChecker = TimeWindowChecker(services.clock)
override val uniquenessProvider = PersistentUniquenessProvider()
@ -35,9 +36,10 @@ class MyValidatingNotaryFlow(otherSide: Party, service: MyCustomValidatingNotary
override fun receiveAndVerifyTx(): TransactionParts {
try {
val stx = subFlow(ReceiveTransactionFlow(otherSide, checkSufficientSignatures = false))
checkNotary(stx.notary)
checkSignatures(stx)
val wtx = stx.tx
return TransactionParts(wtx.id, wtx.inputs, wtx.timeWindow)
return TransactionParts(wtx.id, wtx.inputs, wtx.timeWindow, wtx.notary)
} catch (e: Exception) {
throw when (e) {
is TransactionVerificationException,
@ -49,7 +51,7 @@ class MyValidatingNotaryFlow(otherSide: Party, service: MyCustomValidatingNotary
private fun checkSignatures(stx: SignedTransaction) {
try {
stx.verifySignaturesExcept(serviceHub.notaryIdentityKey)
stx.verifySignaturesExcept(service.notaryIdentityKey)
} catch (e: SignatureException) {
throw NotaryException(NotaryError.TransactionInvalid(e))
}

View File

@ -255,9 +255,16 @@ abstract class AbstractNode(open val configuration: NodeConfiguration,
*/
fun <T : SerializeAsToken> installCordaService(serviceClass: Class<T>): T {
serviceClass.requireAnnotation<CordaService>()
val constructor = serviceClass.getDeclaredConstructor(ServiceHub::class.java).apply { isAccessible = true }
val service = try {
constructor.newInstance(services)
if (NotaryService::class.java.isAssignableFrom(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)
}
else {
val constructor = serviceClass.getDeclaredConstructor(ServiceHub::class.java).apply { isAccessible = true }
constructor.newInstance(services)
}
} catch (e: InvocationTargetException) {
throw ServiceInstantiationException(e.cause)
}
@ -576,12 +583,13 @@ abstract class AbstractNode(open val configuration: NodeConfiguration,
}
open protected fun makeCoreNotaryService(type: ServiceType): NotaryService? {
check(myNotaryIdentity != null) { "No notary identity initialized when creating a notary service" }
return when (type) {
SimpleNotaryService.type -> SimpleNotaryService(services)
ValidatingNotaryService.type -> ValidatingNotaryService(services)
RaftNonValidatingNotaryService.type -> RaftNonValidatingNotaryService(services)
RaftValidatingNotaryService.type -> RaftValidatingNotaryService(services)
BFTNonValidatingNotaryService.type -> BFTNonValidatingNotaryService(services)
SimpleNotaryService.type -> SimpleNotaryService(services, myNotaryIdentity!!.owningKey)
ValidatingNotaryService.type -> ValidatingNotaryService(services, myNotaryIdentity!!.owningKey)
RaftNonValidatingNotaryService.type -> RaftNonValidatingNotaryService(services, myNotaryIdentity!!.owningKey)
RaftValidatingNotaryService.type -> RaftValidatingNotaryService(services, myNotaryIdentity!!.owningKey)
BFTNonValidatingNotaryService.type -> BFTNonValidatingNotaryService(services, myNotaryIdentity!!.owningKey)
else -> null
}
}
@ -713,7 +721,6 @@ abstract class AbstractNode(open val configuration: NodeConfiguration,
override val myInfo: NodeInfo get() = info
override val database: CordaPersistence get() = this@AbstractNode.database
override val configuration: NodeConfiguration get() = this@AbstractNode.configuration
override val notaryIdentityKey: PublicKey get() = myNotaryIdentity?.owningKey ?: throw IllegalArgumentException("Node doesn't have notary identity key")
override fun <T : SerializeAsToken> cordaService(type: Class<T>): T {
require(type.isAnnotationPresent(CordaService::class.java)) { "${type.name} is not a Corda service" }

View File

@ -6,6 +6,7 @@ import net.corda.core.contracts.StateRef
import net.corda.core.crypto.DigitalSignature
import net.corda.core.crypto.SecureHash
import net.corda.core.flows.FlowLogic
import net.corda.core.flows.NotaryError
import net.corda.core.flows.NotaryException
import net.corda.core.identity.CordaX500Name
import net.corda.core.identity.Party
@ -20,6 +21,7 @@ import net.corda.core.utilities.*
import net.corda.node.services.api.ServiceHubInternal
import net.corda.node.utilities.AppendOnlyPersistentMap
import net.corda.node.utilities.NODE_DATABASE_PREFIX
import java.security.PublicKey
import javax.persistence.Entity
import kotlin.concurrent.thread
@ -28,7 +30,9 @@ import kotlin.concurrent.thread
*
* A transaction is notarised when the consensus is reached by the cluster on its uniqueness, and time-window validity.
*/
class BFTNonValidatingNotaryService(override val services: ServiceHubInternal, cluster: BFTSMaRt.Cluster = distributedCluster) : NotaryService() {
class BFTNonValidatingNotaryService(override val services: ServiceHubInternal,
override val notaryIdentityKey: PublicKey,
cluster: BFTSMaRt.Cluster = distributedCluster) : NotaryService() {
companion object {
val type = SimpleNotaryService.type.getSubType("bft")
private val log = loggerFor<BFTNonValidatingNotaryService>()
@ -51,7 +55,7 @@ class BFTNonValidatingNotaryService(override val services: ServiceHubInternal, c
thread(name = "BFT SMaRt replica $replicaId init", isDaemon = true) {
configHandle.use {
val timeWindowChecker = TimeWindowChecker(services.clock)
val replica = Replica(it, replicaId, { createMap() }, services, timeWindowChecker)
val replica = Replica(it, replicaId, { createMap() }, services, notaryIdentityKey, timeWindowChecker)
replicaHolder.set(replica)
log.info("BFT SMaRt replica $replicaId is running.")
}
@ -100,8 +104,8 @@ class BFTNonValidatingNotaryService(override val services: ServiceHubInternal, c
toPersistentEntityKey = { PersistentStateRef(it.txhash.toString(), it.index) },
fromPersistentEntity = {
//TODO null check will become obsolete after making DB/JPA columns not nullable
var txId = it.id.txId ?: throw IllegalStateException("DB returned null SecureHash transactionId")
var index = it.id.index ?: throw IllegalStateException("DB returned null SecureHash index")
val txId = it.id.txId ?: throw IllegalStateException("DB returned null SecureHash transactionId")
val index = it.id.index ?: throw IllegalStateException("DB returned null SecureHash index")
Pair(StateRef(txhash = SecureHash.parse(txId), index = index),
UniquenessProvider.ConsumingTx(
id = SecureHash.parse(it.consumingTxHash),
@ -126,7 +130,8 @@ class BFTNonValidatingNotaryService(override val services: ServiceHubInternal, c
replicaId: Int,
createMap: () -> AppendOnlyPersistentMap<StateRef, UniquenessProvider.ConsumingTx, PersistedCommittedState, PersistentStateRef>,
services: ServiceHubInternal,
timeWindowChecker: TimeWindowChecker) : BFTSMaRt.Replica(config, replicaId, createMap, services, timeWindowChecker) {
notaryIdentityKey: PublicKey,
timeWindowChecker: TimeWindowChecker) : BFTSMaRt.Replica(config, replicaId, createMap, services, notaryIdentityKey, timeWindowChecker) {
override fun executeCommand(command: ByteArray): ByteArray {
val request = command.deserialize<BFTSMaRt.CommitRequest>()
@ -139,8 +144,9 @@ class BFTNonValidatingNotaryService(override val services: ServiceHubInternal, c
return try {
val id = ftx.id
val inputs = ftx.inputs
val notary = ftx.notary
validateTimeWindow(ftx.timeWindow)
if (notary !in services.myInfo.legalIdentities) throw NotaryException(NotaryError.WrongNotary)
commitInputStates(inputs, id, callerIdentity)
log.debug { "Inputs committed successfully, signing $id" }
BFTSMaRt.ReplicaResponse.Signature(sign(ftx))

View File

@ -36,6 +36,7 @@ import net.corda.node.services.transactions.BFTSMaRt.Client
import net.corda.node.services.transactions.BFTSMaRt.Replica
import net.corda.node.utilities.AppendOnlyPersistentMap
import java.nio.file.Path
import java.security.PublicKey
import java.util.*
/**
@ -176,6 +177,7 @@ object BFTSMaRt {
createMap: () -> AppendOnlyPersistentMap<StateRef, UniquenessProvider.ConsumingTx,
BFTNonValidatingNotaryService.PersistedCommittedState, PersistentStateRef>,
protected val services: ServiceHubInternal,
protected val notaryIdentityKey: PublicKey,
private val timeWindowChecker: TimeWindowChecker) : DefaultRecoverable() {
companion object {
private val log = loggerFor<Replica>()
@ -248,11 +250,11 @@ object BFTSMaRt {
}
protected fun sign(bytes: ByteArray): DigitalSignature.WithKey {
return services.database.transaction { services.keyManagementService.sign(bytes, services.notaryIdentityKey) }
return services.database.transaction { services.keyManagementService.sign(bytes, notaryIdentityKey) }
}
protected fun sign(filteredTransaction: FilteredTransaction): TransactionSignature {
return services.database.transaction { services.createSignature(filteredTransaction, services.notaryIdentityKey) }
return services.database.transaction { services.createSignature(filteredTransaction, notaryIdentityKey) }
}
// TODO:

View File

@ -27,9 +27,10 @@ class NonValidatingNotaryFlow(otherSide: Party, service: TrustedAuthorityNotaryS
it.verify()
it.checkAllComponentsVisible(ComponentGroupEnum.INPUTS_GROUP)
it.checkAllComponentsVisible(ComponentGroupEnum.TIMEWINDOW_GROUP)
TransactionParts(it.id, it.inputs, it.timeWindow)
val notary = it.notary
TransactionParts(it.id, it.inputs, it.timeWindow, notary)
}
is NotaryChangeWireTransaction -> TransactionParts(it.id, it.inputs, null)
is NotaryChangeWireTransaction -> TransactionParts(it.id, it.inputs, null, it.notary)
else -> {
throw IllegalArgumentException("Received unexpected transaction type: ${it::class.java.simpleName}," +
"expected either ${FilteredTransaction::class.java.simpleName} or ${NotaryChangeWireTransaction::class.java.simpleName}")

View File

@ -5,9 +5,10 @@ import net.corda.core.identity.Party
import net.corda.core.node.services.TimeWindowChecker
import net.corda.core.node.services.TrustedAuthorityNotaryService
import net.corda.node.services.api.ServiceHubInternal
import java.security.PublicKey
/** A non-validating notary service operated by a group of mutually trusting parties, uses the Raft algorithm to achieve consensus. */
class RaftNonValidatingNotaryService(override val services: ServiceHubInternal) : TrustedAuthorityNotaryService() {
class RaftNonValidatingNotaryService(override val services: ServiceHubInternal, override val notaryIdentityKey: PublicKey) : TrustedAuthorityNotaryService() {
companion object {
val type = SimpleNotaryService.type.getSubType("raft")
}

View File

@ -5,9 +5,10 @@ import net.corda.core.identity.Party
import net.corda.core.node.services.TimeWindowChecker
import net.corda.core.node.services.TrustedAuthorityNotaryService
import net.corda.node.services.api.ServiceHubInternal
import java.security.PublicKey
/** A validating notary service operated by a group of mutually trusting parties, uses the Raft algorithm to achieve consensus. */
class RaftValidatingNotaryService(override val services: ServiceHubInternal) : TrustedAuthorityNotaryService() {
class RaftValidatingNotaryService(override val services: ServiceHubInternal, override val notaryIdentityKey: PublicKey) : TrustedAuthorityNotaryService() {
companion object {
val type = ValidatingNotaryService.type.getSubType("raft")
}

View File

@ -6,9 +6,10 @@ import net.corda.core.node.services.TimeWindowChecker
import net.corda.core.node.services.TrustedAuthorityNotaryService
import net.corda.nodeapi.ServiceType
import net.corda.node.services.api.ServiceHubInternal
import java.security.PublicKey
/** A simple Notary service that does not perform transaction validation */
class SimpleNotaryService(override val services: ServiceHubInternal) : TrustedAuthorityNotaryService() {
class SimpleNotaryService(override val services: ServiceHubInternal, override val notaryIdentityKey: PublicKey) : TrustedAuthorityNotaryService() {
companion object {
val type = ServiceType.notary.getSubType("simple")
}

View File

@ -24,9 +24,11 @@ class ValidatingNotaryFlow(otherSide: Party, service: TrustedAuthorityNotaryServ
override fun receiveAndVerifyTx(): TransactionParts {
try {
val stx = subFlow(ReceiveTransactionFlow(otherSide, checkSufficientSignatures = false))
val notary = stx.notary
checkNotary(notary)
checkSignatures(stx)
val wtx = stx.tx
return TransactionParts(wtx.id, wtx.inputs, wtx.timeWindow)
return TransactionParts(wtx.id, wtx.inputs, wtx.timeWindow, notary!!)
} catch (e: Exception) {
throw when (e) {
is TransactionVerificationException,
@ -38,7 +40,7 @@ class ValidatingNotaryFlow(otherSide: Party, service: TrustedAuthorityNotaryServ
private fun checkSignatures(stx: SignedTransaction) {
try {
stx.verifySignaturesExcept(serviceHub.notaryIdentityKey)
stx.verifySignaturesExcept(service.notaryIdentityKey)
} catch(e: SignatureException) {
throw NotaryException(NotaryError.TransactionInvalid(e))
}

View File

@ -6,9 +6,10 @@ import net.corda.core.node.services.TimeWindowChecker
import net.corda.core.node.services.TrustedAuthorityNotaryService
import net.corda.nodeapi.ServiceType
import net.corda.node.services.api.ServiceHubInternal
import java.security.PublicKey
/** A Notary service that validates the transaction chain of the submitted transaction before committing it */
class ValidatingNotaryService(override val services: ServiceHubInternal) : TrustedAuthorityNotaryService() {
class ValidatingNotaryService(override val services: ServiceHubInternal, override val notaryIdentityKey: PublicKey) : TrustedAuthorityNotaryService() {
companion object {
val type = ServiceType.notary.getSubType("validating")
}

View File

@ -646,7 +646,8 @@ class TwoPartyTradeFlowTests {
val sigs = mutableListOf<TransactionSignature>()
val nodeKey = node.info.chooseIdentity().owningKey
sigs.add(node.services.keyManagementService.sign(SignableData(id, SignatureMetadata(1, Crypto.findSignatureScheme(nodeKey).schemeNumberID)), nodeKey))
sigs.add(notaryNode.services.keyManagementService.sign(SignableData(id, SignatureMetadata(1, Crypto.findSignatureScheme(notaryNode.services.notaryIdentityKey).schemeNumberID)), notaryNode.services.notaryIdentityKey))
sigs.add(notaryNode.services.keyManagementService.sign(SignableData(id, SignatureMetadata(1,
Crypto.findSignatureScheme(notaryNode.info.legalIdentities[1].owningKey).schemeNumberID)), notaryNode.info.legalIdentities[1].owningKey))
extraSigningNodes.forEach { currentNode ->
sigs.add(currentNode.services.keyManagementService.sign(
SignableData(id, SignatureMetadata(1, Crypto.findSignatureScheme(currentNode.info.chooseIdentity().owningKey).schemeNumberID)),

View File

@ -161,7 +161,7 @@ class NotaryServiceTests {
fun issueState(node: StartedNode<*>): StateAndRef<*> {
val tx = DummyContract.generateInitial(Random().nextInt(), notary, node.info.chooseIdentity().ref(0))
val signedByNode = node.services.signInitialTransaction(tx)
val stx = notaryNode.services.addSignature(signedByNode, notaryNode.services.notaryIdentityKey)
val stx = notaryNode.services.addSignature(signedByNode, notary.owningKey)
node.services.recordTransactions(stx)
return StateAndRef(tx.outputStates().first(), StateRef(stx.id, 0))
}

View File

@ -104,7 +104,7 @@ class ValidatingNotaryServiceTests {
fun issueState(node: StartedNode<*>): StateAndRef<*> {
val tx = DummyContract.generateInitial(Random().nextInt(), notary, node.info.chooseIdentity().ref(0))
val signedByNode = node.services.signInitialTransaction(tx)
val stx = notaryNode.services.addSignature(signedByNode, notaryNode.services.notaryIdentityKey)
val stx = notaryNode.services.addSignature(signedByNode, notary.owningKey)
node.services.recordTransactions(stx)
return StateAndRef(tx.outputStates().first(), StateRef(stx.id, 0))
}

View File

@ -68,8 +68,6 @@ open class MockServiceHubInternal(
get() = overrideClock ?: throw UnsupportedOperationException()
override val myInfo: NodeInfo
get() = NodeInfo(listOf(MOCK_HOST_AND_PORT), listOf(DUMMY_IDENTITY_1), 1, serial = 1L) // Required to get a dummy platformVersion when required for tests.
override val notaryIdentityKey: PublicKey
get() = throw IllegalStateException("No notary identity in MockServiceHubInternal")
override val monitoringService: MonitoringService = MonitoringService(MetricRegistry())
override val rpcFlows: List<Class<out FlowLogic<*>>>
get() = throw UnsupportedOperationException()

View File

@ -257,11 +257,9 @@ class MockNetwork(private val networkSendManuallyPumped: Boolean = false,
override fun makeCoreNotaryService(type: ServiceType): NotaryService? {
if (type != BFTNonValidatingNotaryService.type) return super.makeCoreNotaryService(type)
return BFTNonValidatingNotaryService(services, object : BFTSMaRt.Cluster {
return BFTNonValidatingNotaryService(services, myNotaryIdentity!!.owningKey, object : BFTSMaRt.Cluster {
override fun waitUntilAllReplicasHaveInitialized() {
val clusterNodes = mockNet.nodes.filter {
services.notaryIdentityKey in it.info.legalIdentitiesAndCerts.map { it.owningKey }
}
val clusterNodes = mockNet.nodes.filter { myNotaryIdentity!!.owningKey in it.started!!.info.legalIdentities.map { it.owningKey } }
if (clusterNodes.size != configuration.notaryClusterAddresses.size) {
throw IllegalStateException("Unable to enumerate all nodes in BFT cluster.")
}

View File

@ -156,7 +156,6 @@ open class MockServices(vararg val keys: KeyPair) : ServiceHub {
val identity = getTestPartyAndCertificate(MEGA_CORP.name, key.public)
return NodeInfo(emptyList(), listOf(identity), 1, serial = 1L)
}
override val notaryIdentityKey: PublicKey get() = throw UnsupportedOperationException()
override val transactionVerifierService: TransactionVerifierService get() = InMemoryTransactionVerifierService(2)
lateinit var hibernatePersister: HibernateObserver