From 9ebeac1ad87448a17e45519161e76e756ea0f854 Mon Sep 17 00:00:00 2001 From: Andrius Dagys Date: Wed, 10 Oct 2018 10:04:22 +0100 Subject: [PATCH] CORDA-535: Extract notary implementations into CorDapps (#3978) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Move Raft and BFT notaries into separate modules * Move schemas * Fix tests & demos * Modified logic for creating notary services: Added a new field 'className' to the notary configuration. The node now loads the specified implementation via reflection. The default className value points to the simple notary implementation for backwards compatibility. Relevant schemas are loaded in a similar fashion. For backwards compatibility purposes the default SimpleNotaryService will remain built-in to node, but its cordapp will be generated on startup – so the loading of notary services is streamlined. * Move test namedcache factory to test utils --- .idea/compiler.xml | 6 +- build.gradle | 4 +- .../kotlin/net/corda/core/cordapp/Cordapp.kt | 2 + .../core/internal/cordapp/CordappImpl.kt | 13 ++- .../net/corda/core/flows/AttachmentTests.kt | 2 +- .../AttachmentSerializationTest.kt | 2 +- experimental/notary-bft-smart/build.gradle | 35 ++++++++ .../net/corda/notary/bftsmart}/BFTSMaRt.kt | 11 +-- .../corda/notary/bftsmart}/BFTSMaRtConfig.kt | 3 +- .../notary/bftsmart/BftSmartNotaryService.kt | 28 +++++-- .../net/corda/notary/bftsmart/Schema.kt | 20 +++++ .../notary-bft-smart.changelog-init.xml | 45 +++++++++++ .../notary-bft-smart.changelog-master.xml | 9 +++ .../notary-bft-smart.changelog-pkey.xml | 11 +++ .../notary-bft-smart.changelog-v1.xml | 10 +++ .../notary/bftsmart}/system.config.printf | 0 .../notary/bftsmart}/BFTNotaryServiceTests.kt | 10 ++- .../notary/bftsmart}/BFTSMaRtConfigTests.kt | 4 +- experimental/notary-raft/build.gradle | 35 ++++++++ .../corda/notary/raft/RaftNotaryService.kt | 46 +++++++++++ .../notary/raft}/RaftTransactionCommitLog.kt | 9 ++- .../notary/raft}/RaftUniquenessProvider.kt | 4 +- .../kotlin/net/corda/notary/raft/Schema.kt | 19 +++++ .../migration/notary-raft.changelog-init.xml | 46 +++++++++++ .../notary-raft.changelog-master.xml | 11 +++ .../migration/notary-raft.changelog-pkey.xml | 11 +++ .../migration/notary-raft.changelog-v1.xml | 11 +++ .../notary/raft}/RaftNotaryServiceTests.kt | 2 +- .../raft}/RaftTransactionCommitLogTests.kt | 6 +- node/build.gradle | 9 --- .../distributed/DistributedServiceTests.kt | 2 +- .../messaging/ArtemisMessagingTest.kt | 2 +- .../network/PersistentNetworkMapCacheTest.kt | 2 +- .../services/messaging/P2PMessagingTest.kt | 1 + .../net/corda/node/internal/AbstractNode.kt | 81 ++++++++++--------- .../kotlin/net/corda/node/internal/Node.kt | 8 -- .../net/corda/node/internal/NodeStartup.kt | 14 +++- .../cordapp/JarScanningCordappLoader.kt | 69 ++++++++-------- .../node/internal/cordapp/VirtualCordapps.kt | 60 ++++++++++++++ .../node/services/config/NodeConfiguration.kt | 3 +- .../node/services/schema/NodeSchemaService.kt | 20 +---- .../RaftNonValidatingNotaryService.kt | 26 ------ .../RaftValidatingNotaryService.kt | 26 ------ .../transactions/SimpleNotaryService.kt | 27 ++++++- .../transactions/ValidatingNotaryService.kt | 17 ---- .../migration/node-notary.changelog-init.xml | 21 +---- .../migration/node-notary.changelog-pkey.xml | 5 -- .../migration/node-notary.changelog-v1.xml | 4 - .../cordapp/JarScanningCordappLoaderTest.kt | 24 +++--- .../node/messaging/TwoPartyTradeFlowTests.kt | 21 ++--- .../net/corda/node/services/TimedFlowTests.kt | 5 +- .../PersistentIdentityServiceTests.kt | 2 +- .../AppendOnlyPersistentMapTest.kt | 2 +- .../persistence/DBTransactionStorageTests.kt | 5 +- .../HibernateColumnConverterTests.kt | 2 +- .../persistence/NodeAttachmentServiceTest.kt | 2 +- .../services/schema/NodeSchemaServiceTest.kt | 12 +-- .../transactions/MaxTransactionSizeTests.kt | 2 +- .../PersistentUniquenessProviderTests.kt | 4 +- .../vault/VaultSoftLockManagerTest.kt | 2 +- samples/notary-demo/build.gradle | 57 ++++++------- settings.gradle | 2 + testing/node-driver/build.gradle | 2 + .../testing/node/internal/DriverDSLImpl.kt | 1 + .../node/internal/InternalMockNetwork.kt | 39 +++------ .../internal}/TestingNamedCacheFactory.kt | 3 +- 66 files changed, 637 insertions(+), 362 deletions(-) create mode 100644 experimental/notary-bft-smart/build.gradle rename {node/src/main/kotlin/net/corda/node/services/transactions => experimental/notary-bft-smart/src/main/kotlin/net/corda/notary/bftsmart}/BFTSMaRt.kt (97%) rename {node/src/main/kotlin/net/corda/node/services/transactions => experimental/notary-bft-smart/src/main/kotlin/net/corda/notary/bftsmart}/BFTSMaRtConfig.kt (97%) rename node/src/main/kotlin/net/corda/node/services/transactions/BFTNonValidatingNotaryService.kt => experimental/notary-bft-smart/src/main/kotlin/net/corda/notary/bftsmart/BftSmartNotaryService.kt (87%) create mode 100644 experimental/notary-bft-smart/src/main/kotlin/net/corda/notary/bftsmart/Schema.kt create mode 100644 experimental/notary-bft-smart/src/main/resources/migration/notary-bft-smart.changelog-init.xml create mode 100644 experimental/notary-bft-smart/src/main/resources/migration/notary-bft-smart.changelog-master.xml create mode 100644 experimental/notary-bft-smart/src/main/resources/migration/notary-bft-smart.changelog-pkey.xml create mode 100644 experimental/notary-bft-smart/src/main/resources/migration/notary-bft-smart.changelog-v1.xml rename {node/src/main/resources/net/corda/node/services/transactions => experimental/notary-bft-smart/src/main/resources/net/corda/notary/bftsmart}/system.config.printf (100%) rename {node/src/integration-test/kotlin/net/corda/node/services => experimental/notary-bft-smart/src/test/kotlin/net/corda/notary/bftsmart}/BFTNotaryServiceTests.kt (96%) rename {node/src/test/kotlin/net/corda/node/services/transactions => experimental/notary-bft-smart/src/test/kotlin/net/corda/notary/bftsmart}/BFTSMaRtConfigTests.kt (92%) create mode 100644 experimental/notary-raft/build.gradle create mode 100644 experimental/notary-raft/src/main/kotlin/net/corda/notary/raft/RaftNotaryService.kt rename {node/src/main/kotlin/net/corda/node/services/transactions => experimental/notary-raft/src/main/kotlin/net/corda/notary/raft}/RaftTransactionCommitLog.kt (96%) rename {node/src/main/kotlin/net/corda/node/services/transactions => experimental/notary-raft/src/main/kotlin/net/corda/notary/raft}/RaftUniquenessProvider.kt (98%) create mode 100644 experimental/notary-raft/src/main/kotlin/net/corda/notary/raft/Schema.kt create mode 100644 experimental/notary-raft/src/main/resources/migration/notary-raft.changelog-init.xml create mode 100644 experimental/notary-raft/src/main/resources/migration/notary-raft.changelog-master.xml create mode 100644 experimental/notary-raft/src/main/resources/migration/notary-raft.changelog-pkey.xml create mode 100644 experimental/notary-raft/src/main/resources/migration/notary-raft.changelog-v1.xml rename {node/src/integration-test/kotlin/net/corda/node/services => experimental/notary-raft/src/test/kotlin/net/corda/notary/raft}/RaftNotaryServiceTests.kt (99%) rename {node/src/integration-test/kotlin/net/corda/node/services/transactions => experimental/notary-raft/src/test/kotlin/net/corda/notary/raft}/RaftTransactionCommitLogTests.kt (98%) create mode 100644 node/src/main/kotlin/net/corda/node/internal/cordapp/VirtualCordapps.kt delete mode 100644 node/src/main/kotlin/net/corda/node/services/transactions/RaftNonValidatingNotaryService.kt delete mode 100644 node/src/main/kotlin/net/corda/node/services/transactions/RaftValidatingNotaryService.kt rename {node/src/test/kotlin/net/corda/node/utilities => testing/test-utils/src/main/kotlin/net/corda/testing/internal}/TestingNamedCacheFactory.kt (95%) diff --git a/.idea/compiler.xml b/.idea/compiler.xml index d981d5fd35..4cfe9a6d84 100644 --- a/.idea/compiler.xml +++ b/.idea/compiler.xml @@ -157,8 +157,12 @@ + + + + @@ -235,4 +239,4 @@ - + \ No newline at end of file diff --git a/build.gradle b/build.gradle index 36872b5fef..f3cc3c97ca 100644 --- a/build.gradle +++ b/build.gradle @@ -361,7 +361,9 @@ bintrayConfig { 'corda-tools-blob-inspector', 'corda-tools-explorer', 'corda-tools-network-bootstrapper', - 'corda-tools-cliutils' + 'corda-tools-cliutils', + 'corda-notary-raft', + 'corda-notary-bft-smart' ] license { name = 'Apache-2.0' diff --git a/core/src/main/kotlin/net/corda/core/cordapp/Cordapp.kt b/core/src/main/kotlin/net/corda/core/cordapp/Cordapp.kt index ddf369b668..9ca4e04e36 100644 --- a/core/src/main/kotlin/net/corda/core/cordapp/Cordapp.kt +++ b/core/src/main/kotlin/net/corda/core/cordapp/Cordapp.kt @@ -4,6 +4,7 @@ import net.corda.core.DeleteForDJVM import net.corda.core.DoNotImplement import net.corda.core.crypto.SecureHash import net.corda.core.flows.FlowLogic +import net.corda.core.internal.notary.NotaryService import net.corda.core.schemas.MappedSchema import net.corda.core.serialization.SerializationCustomSerializer import net.corda.core.serialization.SerializationWhitelist @@ -48,4 +49,5 @@ interface Cordapp { val jarPath: URL val cordappClasses: List val jarHash: SecureHash.SHA256 + val notaryService: Class? } diff --git a/core/src/main/kotlin/net/corda/core/internal/cordapp/CordappImpl.kt b/core/src/main/kotlin/net/corda/core/internal/cordapp/CordappImpl.kt index 2e1761afa8..8ab16a5190 100644 --- a/core/src/main/kotlin/net/corda/core/internal/cordapp/CordappImpl.kt +++ b/core/src/main/kotlin/net/corda/core/internal/cordapp/CordappImpl.kt @@ -4,6 +4,7 @@ import net.corda.core.DeleteForDJVM import net.corda.core.cordapp.Cordapp import net.corda.core.crypto.SecureHash import net.corda.core.flows.FlowLogic +import net.corda.core.internal.notary.NotaryService import net.corda.core.internal.toPath import net.corda.core.schemas.MappedSchema import net.corda.core.serialization.SerializationCustomSerializer @@ -25,7 +26,10 @@ data class CordappImpl( override val allFlows: List>>, override val jarPath: URL, val info: Info, - override val jarHash: SecureHash.SHA256) : Cordapp { + override val jarHash: SecureHash.SHA256, + override val notaryService: Class? = null, + /** Indicates whether the CorDapp is loaded from external sources, or generated on node startup (virtual). */ + val isLoaded: Boolean = true) : Cordapp { override val name: String = jarName(jarPath) companion object { @@ -37,7 +41,10 @@ data class CordappImpl( * * TODO: Also add [SchedulableFlow] as a Cordapp class */ - override val cordappClasses: List = (rpcFlows + initiatedFlows + services + serializationWhitelists.map { javaClass }).map { it.name } + contractClassNames + override val cordappClasses: List = run { + val classList = rpcFlows + initiatedFlows + services + serializationWhitelists.map { javaClass } + notaryService + classList.mapNotNull { it?.name } + contractClassNames + } // TODO Why a seperate Info class and not just have the fields directly in CordappImpl? data class Info(val shortName: String, val vendor: String, val version: String, val minimumPlatformVersion: Int, val targetPlatformVersion: Int) { @@ -48,4 +55,4 @@ data class CordappImpl( fun hasUnknownFields(): Boolean = arrayOf(shortName, vendor, version).any { it == UNKNOWN_VALUE } } -} +} \ No newline at end of file diff --git a/core/src/test/kotlin/net/corda/core/flows/AttachmentTests.kt b/core/src/test/kotlin/net/corda/core/flows/AttachmentTests.kt index eaabb2d987..e2a2e22b71 100644 --- a/core/src/test/kotlin/net/corda/core/flows/AttachmentTests.kt +++ b/core/src/test/kotlin/net/corda/core/flows/AttachmentTests.kt @@ -128,7 +128,7 @@ class AttachmentTests : WithMockNet { // Makes a node that doesn't do sanity checking at load time. private fun makeBadNode(name: CordaX500Name) = mockNet.createNode( InternalMockNodeParameters(legalName = makeUnique(name)), - nodeFactory = { args, _ -> + nodeFactory = { args -> object : InternalMockNetwork.MockNode(args) { override fun start() = super.start().apply { attachments.checkAttachmentsOnLoad = false } } diff --git a/core/src/test/kotlin/net/corda/core/serialization/AttachmentSerializationTest.kt b/core/src/test/kotlin/net/corda/core/serialization/AttachmentSerializationTest.kt index 43c35ca0dd..3ba769ce88 100644 --- a/core/src/test/kotlin/net/corda/core/serialization/AttachmentSerializationTest.kt +++ b/core/src/test/kotlin/net/corda/core/serialization/AttachmentSerializationTest.kt @@ -161,7 +161,7 @@ class AttachmentSerializationTest { } private fun rebootClientAndGetAttachmentContent(checkAttachmentsOnLoad: Boolean = true): String { - client = mockNet.restartNode(client) { args, _ -> + client = mockNet.restartNode(client) { args -> object : InternalMockNetwork.MockNode(args) { override fun start() = super.start().apply { attachments.checkAttachmentsOnLoad = checkAttachmentsOnLoad } } diff --git a/experimental/notary-bft-smart/build.gradle b/experimental/notary-bft-smart/build.gradle new file mode 100644 index 0000000000..b4ff48b444 --- /dev/null +++ b/experimental/notary-bft-smart/build.gradle @@ -0,0 +1,35 @@ +apply plugin: 'kotlin' +apply plugin: 'kotlin-jpa' +apply plugin: 'idea' +apply plugin: 'net.corda.plugins.cordapp' +apply plugin: 'net.corda.plugins.publish-utils' + +configurations { + integrationTestCompile.extendsFrom testCompile + integrationTestRuntime.extendsFrom testRuntime +} + +dependencies { + cordaCompile "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" + + // Corda integration dependencies + cordaCompile project(':node') + + // BFT-SMaRt + compile 'commons-codec:commons-codec:1.10' + compile 'com.github.bft-smart:library:master-v1.1-beta-g6215ec8-87' + + testCompile "junit:junit:$junit_version" + testCompile project(':node-driver') +} + +idea { + module { + downloadJavadoc = true // defaults to false + downloadSources = true + } +} + +publish { + name 'corda-notary-bft-smart' +} diff --git a/node/src/main/kotlin/net/corda/node/services/transactions/BFTSMaRt.kt b/experimental/notary-bft-smart/src/main/kotlin/net/corda/notary/bftsmart/BFTSMaRt.kt similarity index 97% rename from node/src/main/kotlin/net/corda/node/services/transactions/BFTSMaRt.kt rename to experimental/notary-bft-smart/src/main/kotlin/net/corda/notary/bftsmart/BFTSMaRt.kt index 885746ecb6..a825b3c1a5 100644 --- a/node/src/main/kotlin/net/corda/node/services/transactions/BFTSMaRt.kt +++ b/experimental/notary-bft-smart/src/main/kotlin/net/corda/notary/bftsmart/BFTSMaRt.kt @@ -1,4 +1,4 @@ -package net.corda.node.services.transactions +package net.corda.notary.bftsmart import bftsmart.communication.ServerCommunicationSystem import bftsmart.communication.client.netty.NettyClientServerCommunicationSystemClientSide @@ -34,10 +34,11 @@ import net.corda.core.serialization.serialize import net.corda.core.utilities.contextLogger import net.corda.core.utilities.debug import net.corda.node.services.api.ServiceHubInternal -import net.corda.node.services.transactions.BFTSMaRt.Client -import net.corda.node.services.transactions.BFTSMaRt.Replica +import net.corda.node.services.transactions.PersistentUniquenessProvider import net.corda.node.utilities.AppendOnlyPersistentMap import net.corda.nodeapi.internal.persistence.currentDBSession +import net.corda.notary.bftsmart.BFTSMaRt.Client +import net.corda.notary.bftsmart.BFTSMaRt.Replica import java.nio.file.Path import java.security.PublicKey import java.util.* @@ -78,7 +79,7 @@ object BFTSMaRt { fun waitUntilAllReplicasHaveInitialized() } - class Client(config: BFTSMaRtConfig, private val clientId: Int, private val cluster: Cluster, private val notaryService: BFTNonValidatingNotaryService) : SingletonSerializeAsToken() { + class Client(config: BFTSMaRtConfig, private val clientId: Int, private val cluster: Cluster, private val notaryService: BftSmartNotaryService) : SingletonSerializeAsToken() { companion object { private val log = contextLogger() } @@ -178,7 +179,7 @@ object BFTSMaRt { abstract class Replica(config: BFTSMaRtConfig, replicaId: Int, createMap: () -> AppendOnlyPersistentMap, + BftSmartNotaryService.CommittedState, PersistentStateRef>, protected val services: ServiceHubInternal, protected val notaryIdentityKey: PublicKey) : DefaultRecoverable() { companion object { diff --git a/node/src/main/kotlin/net/corda/node/services/transactions/BFTSMaRtConfig.kt b/experimental/notary-bft-smart/src/main/kotlin/net/corda/notary/bftsmart/BFTSMaRtConfig.kt similarity index 97% rename from node/src/main/kotlin/net/corda/node/services/transactions/BFTSMaRtConfig.kt rename to experimental/notary-bft-smart/src/main/kotlin/net/corda/notary/bftsmart/BFTSMaRtConfig.kt index e642dc858f..489f190d6f 100644 --- a/node/src/main/kotlin/net/corda/node/services/transactions/BFTSMaRtConfig.kt +++ b/experimental/notary-bft-smart/src/main/kotlin/net/corda/notary/bftsmart/BFTSMaRtConfig.kt @@ -1,10 +1,11 @@ -package net.corda.node.services.transactions +package net.corda.notary.bftsmart import net.corda.core.internal.div import net.corda.core.internal.writer import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.contextLogger import net.corda.core.utilities.debug +import net.corda.node.services.transactions.PathManager import java.io.PrintWriter import java.net.InetAddress import java.net.Socket diff --git a/node/src/main/kotlin/net/corda/node/services/transactions/BFTNonValidatingNotaryService.kt b/experimental/notary-bft-smart/src/main/kotlin/net/corda/notary/bftsmart/BftSmartNotaryService.kt similarity index 87% rename from node/src/main/kotlin/net/corda/node/services/transactions/BFTNonValidatingNotaryService.kt rename to experimental/notary-bft-smart/src/main/kotlin/net/corda/notary/bftsmart/BftSmartNotaryService.kt index 156640c443..57560699cb 100644 --- a/node/src/main/kotlin/net/corda/node/services/transactions/BFTNonValidatingNotaryService.kt +++ b/experimental/notary-bft-smart/src/main/kotlin/net/corda/notary/bftsmart/BftSmartNotaryService.kt @@ -1,4 +1,4 @@ -package net.corda.node.services.transactions +package net.corda.notary.bftsmart import co.paralleluniverse.fibers.Suspendable import com.google.common.util.concurrent.SettableFuture @@ -10,6 +10,7 @@ import net.corda.core.identity.Party import net.corda.core.internal.notary.NotaryInternalException import net.corda.core.internal.notary.NotaryService import net.corda.core.internal.notary.verifySignature +import net.corda.core.schemas.MappedSchema import net.corda.core.schemas.PersistentStateRef import net.corda.core.serialization.deserialize import net.corda.core.serialization.serialize @@ -21,6 +22,7 @@ import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.unwrap import net.corda.node.services.api.ServiceHubInternal import net.corda.node.services.config.BFTSMaRtConfiguration +import net.corda.node.services.transactions.PersistentUniquenessProvider import net.corda.node.utilities.AppendOnlyPersistentMap import net.corda.nodeapi.internal.persistence.NODE_DATABASE_PREFIX import java.security.PublicKey @@ -33,16 +35,30 @@ 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( +class BftSmartNotaryService( override val services: ServiceHubInternal, - override val notaryIdentityKey: PublicKey, - private val bftSMaRtConfig: BFTSMaRtConfiguration, - cluster: BFTSMaRt.Cluster + override val notaryIdentityKey: PublicKey ) : NotaryService() { companion object { private val log = contextLogger() } + private val notaryConfig = services.configuration.notary + ?: throw IllegalArgumentException("Failed to register ${this::class.java}: notary configuration not present") + + private val bftSMaRtConfig = notaryConfig.bftSMaRt + ?: throw IllegalArgumentException("Failed to register ${this::class.java}: raft configuration not present") + + private val cluster: BFTSMaRt.Cluster = makeBFTCluster(notaryIdentityKey, bftSMaRtConfig) + + protected open fun makeBFTCluster(notaryKey: PublicKey, bftSMaRtConfig: BFTSMaRtConfiguration): BFTSMaRt.Cluster { + return object : BFTSMaRt.Cluster { + override fun waitUntilAllReplicasHaveInitialized() { + log.warn("A BFT replica may still be initializing, in which case the upcoming consensus change may cause it to spin.") + } + } + } + private val client: BFTSMaRt.Client private val replicaHolder = SettableFuture.create() @@ -71,7 +87,7 @@ class BFTNonValidatingNotaryService( override fun createServiceFlow(otherPartySession: FlowSession): FlowLogic = ServiceFlow(otherPartySession, this) - private class ServiceFlow(val otherSideSession: FlowSession, val service: BFTNonValidatingNotaryService) : FlowLogic() { + private class ServiceFlow(val otherSideSession: FlowSession, val service: BftSmartNotaryService) : FlowLogic() { @Suspendable override fun call(): Void? { val payload = otherSideSession.receive().unwrap { it } diff --git a/experimental/notary-bft-smart/src/main/kotlin/net/corda/notary/bftsmart/Schema.kt b/experimental/notary-bft-smart/src/main/kotlin/net/corda/notary/bftsmart/Schema.kt new file mode 100644 index 0000000000..360c1af5df --- /dev/null +++ b/experimental/notary-bft-smart/src/main/kotlin/net/corda/notary/bftsmart/Schema.kt @@ -0,0 +1,20 @@ +package net.corda.notary.bftsmart + +import net.corda.core.schemas.MappedSchema +import net.corda.node.services.transactions.PersistentUniquenessProvider +import net.corda.notary.bftsmart.BftSmartNotaryService + +object BftSmartNotarySchema + +object BftSmartNotarySchemaV1 : MappedSchema( + schemaFamily = BftSmartNotarySchema.javaClass, + version = 1, + mappedTypes = listOf( + PersistentUniquenessProvider.BaseComittedState::class.java, + PersistentUniquenessProvider.Request::class.java, + BftSmartNotaryService.CommittedState::class.java + ) +) { + override val migrationResource: String? + get() = "notary-bft-smart.changelog-master" +} \ No newline at end of file diff --git a/experimental/notary-bft-smart/src/main/resources/migration/notary-bft-smart.changelog-init.xml b/experimental/notary-bft-smart/src/main/resources/migration/notary-bft-smart.changelog-init.xml new file mode 100644 index 0000000000..1b8dbf7464 --- /dev/null +++ b/experimental/notary-bft-smart/src/main/resources/migration/notary-bft-smart.changelog-init.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/experimental/notary-bft-smart/src/main/resources/migration/notary-bft-smart.changelog-master.xml b/experimental/notary-bft-smart/src/main/resources/migration/notary-bft-smart.changelog-master.xml new file mode 100644 index 0000000000..fdf0632cf8 --- /dev/null +++ b/experimental/notary-bft-smart/src/main/resources/migration/notary-bft-smart.changelog-master.xml @@ -0,0 +1,9 @@ + + + + + + diff --git a/experimental/notary-bft-smart/src/main/resources/migration/notary-bft-smart.changelog-pkey.xml b/experimental/notary-bft-smart/src/main/resources/migration/notary-bft-smart.changelog-pkey.xml new file mode 100644 index 0000000000..59a5b1fb50 --- /dev/null +++ b/experimental/notary-bft-smart/src/main/resources/migration/notary-bft-smart.changelog-pkey.xml @@ -0,0 +1,11 @@ + + + + + + + \ No newline at end of file diff --git a/experimental/notary-bft-smart/src/main/resources/migration/notary-bft-smart.changelog-v1.xml b/experimental/notary-bft-smart/src/main/resources/migration/notary-bft-smart.changelog-v1.xml new file mode 100644 index 0000000000..57b9adbf93 --- /dev/null +++ b/experimental/notary-bft-smart/src/main/resources/migration/notary-bft-smart.changelog-v1.xml @@ -0,0 +1,10 @@ + + + + + + \ No newline at end of file diff --git a/node/src/main/resources/net/corda/node/services/transactions/system.config.printf b/experimental/notary-bft-smart/src/main/resources/net/corda/notary/bftsmart/system.config.printf similarity index 100% rename from node/src/main/resources/net/corda/node/services/transactions/system.config.printf rename to experimental/notary-bft-smart/src/main/resources/net/corda/notary/bftsmart/system.config.printf diff --git a/node/src/integration-test/kotlin/net/corda/node/services/BFTNotaryServiceTests.kt b/experimental/notary-bft-smart/src/test/kotlin/net/corda/notary/bftsmart/BFTNotaryServiceTests.kt similarity index 96% rename from node/src/integration-test/kotlin/net/corda/node/services/BFTNotaryServiceTests.kt rename to experimental/notary-bft-smart/src/test/kotlin/net/corda/notary/bftsmart/BFTNotaryServiceTests.kt index c906474274..1924470588 100644 --- a/node/src/integration-test/kotlin/net/corda/node/services/BFTNotaryServiceTests.kt +++ b/experimental/notary-bft-smart/src/test/kotlin/net/corda/notary/bftsmart/BFTNotaryServiceTests.kt @@ -1,4 +1,4 @@ -package net.corda.node.services +package net.corda.notary.bftsmart import com.nhaarman.mockito_kotlin.doReturn import com.nhaarman.mockito_kotlin.whenever @@ -23,8 +23,6 @@ import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.seconds import net.corda.node.services.config.BFTSMaRtConfiguration import net.corda.node.services.config.NotaryConfig -import net.corda.node.services.transactions.minClusterSize -import net.corda.node.services.transactions.minCorrectReplicas import net.corda.nodeapi.internal.DevIdentityGenerator import net.corda.nodeapi.internal.network.NetworkParametersCopier import net.corda.testing.common.internal.testNetworkParameters @@ -84,7 +82,11 @@ class BFTNotaryServiceTests { val nodes = replicaIds.map { replicaId -> mockNet.createUnstartedNode(InternalMockNodeParameters(configOverrides = { - val notary = NotaryConfig(validating = false, bftSMaRt = BFTSMaRtConfiguration(replicaId, clusterAddresses, exposeRaces = exposeRaces)) + val notary = NotaryConfig( + validating = false, + bftSMaRt = BFTSMaRtConfiguration(replicaId, clusterAddresses, exposeRaces = exposeRaces), + className = "net.corda.notary.bftsmart.BftSmartNotaryService" + ) doReturn(notary).whenever(it).notary })) } + mockNet.createUnstartedNode() diff --git a/node/src/test/kotlin/net/corda/node/services/transactions/BFTSMaRtConfigTests.kt b/experimental/notary-bft-smart/src/test/kotlin/net/corda/notary/bftsmart/BFTSMaRtConfigTests.kt similarity index 92% rename from node/src/test/kotlin/net/corda/node/services/transactions/BFTSMaRtConfigTests.kt rename to experimental/notary-bft-smart/src/test/kotlin/net/corda/notary/bftsmart/BFTSMaRtConfigTests.kt index 1837953266..cdf5364b83 100644 --- a/node/src/test/kotlin/net/corda/node/services/transactions/BFTSMaRtConfigTests.kt +++ b/experimental/notary-bft-smart/src/test/kotlin/net/corda/notary/bftsmart/BFTSMaRtConfigTests.kt @@ -1,7 +1,7 @@ -package net.corda.node.services.transactions +package net.corda.notary.bftsmart import net.corda.core.utilities.NetworkHostAndPort -import net.corda.node.services.transactions.BFTSMaRtConfig.Companion.portIsClaimedFormat +import net.corda.notary.bftsmart.BFTSMaRtConfig.Companion.portIsClaimedFormat import org.assertj.core.api.Assertions.assertThatThrownBy import org.junit.Test import kotlin.test.assertEquals diff --git a/experimental/notary-raft/build.gradle b/experimental/notary-raft/build.gradle new file mode 100644 index 0000000000..b07659ef2f --- /dev/null +++ b/experimental/notary-raft/build.gradle @@ -0,0 +1,35 @@ +apply plugin: 'kotlin' +apply plugin: 'idea' +apply plugin: 'net.corda.plugins.cordapp' +apply plugin: 'net.corda.plugins.publish-utils' + +configurations { + integrationTestCompile.extendsFrom testCompile + integrationTestRuntime.extendsFrom testRuntime +} + +dependencies { + cordaCompile "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" + + // Corda integration dependencies + cordaCompile project(':node') + + // Java Atomix: RAFT library + compile 'io.atomix.copycat:copycat-client:1.2.8' + compile 'io.atomix.copycat:copycat-server:1.2.8' + compile 'io.atomix.catalyst:catalyst-netty:1.2.1' + + testCompile "junit:junit:$junit_version" + testCompile project(':node-driver') +} + +idea { + module { + downloadJavadoc = true // defaults to false + downloadSources = true + } +} + +publish { + name 'corda-notary-raft' +} diff --git a/experimental/notary-raft/src/main/kotlin/net/corda/notary/raft/RaftNotaryService.kt b/experimental/notary-raft/src/main/kotlin/net/corda/notary/raft/RaftNotaryService.kt new file mode 100644 index 0000000000..3913551802 --- /dev/null +++ b/experimental/notary-raft/src/main/kotlin/net/corda/notary/raft/RaftNotaryService.kt @@ -0,0 +1,46 @@ +package net.corda.notary.raft + +import net.corda.core.flows.FlowSession +import net.corda.core.internal.notary.NotaryServiceFlow +import net.corda.core.internal.notary.TrustedAuthorityNotaryService +import net.corda.node.services.api.ServiceHubInternal +import net.corda.node.services.transactions.NonValidatingNotaryFlow +import net.corda.node.services.transactions.ValidatingNotaryFlow +import java.security.PublicKey + +/** A highly available notary service using the Raft algorithm to achieve consensus. */ +class RaftNotaryService( + override val services: ServiceHubInternal, + override val notaryIdentityKey: PublicKey +) : TrustedAuthorityNotaryService() { + private val notaryConfig = services.configuration.notary + ?: throw IllegalArgumentException("Failed to register ${this::class.java}: notary configuration not present") + + override val uniquenessProvider = with(services) { + val raftConfig = notaryConfig.raft + ?: throw IllegalArgumentException("Failed to register ${this::class.java}: raft configuration not present") + RaftUniquenessProvider( + configuration.baseDirectory, + configuration.p2pSslOptions, + database, + clock, + monitoringService.metrics, + services.cacheFactory, + raftConfig + ) + } + + override fun createServiceFlow(otherPartySession: FlowSession): NotaryServiceFlow { + return if (notaryConfig.validating) { + ValidatingNotaryFlow(otherPartySession, this) + } else NonValidatingNotaryFlow(otherPartySession, this) + } + + override fun start() { + uniquenessProvider.start() + } + + override fun stop() { + uniquenessProvider.stop() + } +} diff --git a/node/src/main/kotlin/net/corda/node/services/transactions/RaftTransactionCommitLog.kt b/experimental/notary-raft/src/main/kotlin/net/corda/notary/raft/RaftTransactionCommitLog.kt similarity index 96% rename from node/src/main/kotlin/net/corda/node/services/transactions/RaftTransactionCommitLog.kt rename to experimental/notary-raft/src/main/kotlin/net/corda/notary/raft/RaftTransactionCommitLog.kt index c35ae146ab..a6fcd0b8a3 100644 --- a/node/src/main/kotlin/net/corda/node/services/transactions/RaftTransactionCommitLog.kt +++ b/experimental/notary-raft/src/main/kotlin/net/corda/notary/raft/RaftTransactionCommitLog.kt @@ -1,4 +1,4 @@ -package net.corda.node.services.transactions +package net.corda.notary.raft import io.atomix.catalyst.buffer.BufferInput import io.atomix.catalyst.buffer.BufferOutput @@ -27,6 +27,7 @@ import net.corda.core.serialization.internal.checkpointSerialize import net.corda.core.utilities.ByteSequence import net.corda.core.utilities.contextLogger import net.corda.core.utilities.debug +import net.corda.node.services.transactions.PersistentUniquenessProvider import net.corda.node.utilities.AppendOnlyPersistentMap import net.corda.nodeapi.internal.persistence.CordaPersistence import net.corda.nodeapi.internal.persistence.currentDBSession @@ -111,7 +112,7 @@ class RaftTransactionCommitLog( } } - private fun logRequest(commitCommand: RaftTransactionCommitLog.Commands.CommitTransaction) { + private fun logRequest(commitCommand: Commands.CommitTransaction) { val request = PersistentUniquenessProvider.Request( consumingTxHash = commitCommand.txId.toString(), partyName = commitCommand.requestingParty, @@ -192,8 +193,8 @@ class RaftTransactionCommitLog( registerAbstract(SecureHash::class.java, CordaKryoSerializer::class.java) registerAbstract(TimeWindow::class.java, CordaKryoSerializer::class.java) registerAbstract(NotaryError::class.java, CordaKryoSerializer::class.java) - register(RaftTransactionCommitLog.Commands.CommitTransaction::class.java, CordaKryoSerializer::class.java) - register(RaftTransactionCommitLog.Commands.Get::class.java, CordaKryoSerializer::class.java) + register(Commands.CommitTransaction::class.java, CordaKryoSerializer::class.java) + register(Commands.Get::class.java, CordaKryoSerializer::class.java) register(StateRef::class.java, CordaKryoSerializer::class.java) register(LinkedHashMap::class.java, CordaKryoSerializer::class.java) } diff --git a/node/src/main/kotlin/net/corda/node/services/transactions/RaftUniquenessProvider.kt b/experimental/notary-raft/src/main/kotlin/net/corda/notary/raft/RaftUniquenessProvider.kt similarity index 98% rename from node/src/main/kotlin/net/corda/node/services/transactions/RaftUniquenessProvider.kt rename to experimental/notary-raft/src/main/kotlin/net/corda/notary/raft/RaftUniquenessProvider.kt index 2192343fa1..c7670d0c18 100644 --- a/node/src/main/kotlin/net/corda/node/services/transactions/RaftUniquenessProvider.kt +++ b/experimental/notary-raft/src/main/kotlin/net/corda/notary/raft/RaftUniquenessProvider.kt @@ -1,4 +1,4 @@ -package net.corda.node.services.transactions +package net.corda.notary.raft import com.codahale.metrics.Gauge import com.codahale.metrics.MetricRegistry @@ -26,12 +26,12 @@ import net.corda.core.serialization.serialize import net.corda.core.utilities.contextLogger import net.corda.core.utilities.debug import net.corda.node.services.config.RaftConfig -import net.corda.node.services.transactions.RaftTransactionCommitLog.Commands.CommitTransaction import net.corda.node.utilities.AppendOnlyPersistentMap import net.corda.node.utilities.NamedCacheFactory import net.corda.nodeapi.internal.config.MutualSslConfiguration import net.corda.nodeapi.internal.persistence.CordaPersistence import net.corda.nodeapi.internal.persistence.NODE_DATABASE_PREFIX +import net.corda.notary.raft.RaftTransactionCommitLog.Commands.CommitTransaction import java.nio.file.Path import java.time.Clock import java.util.concurrent.CompletableFuture diff --git a/experimental/notary-raft/src/main/kotlin/net/corda/notary/raft/Schema.kt b/experimental/notary-raft/src/main/kotlin/net/corda/notary/raft/Schema.kt new file mode 100644 index 0000000000..3a93164875 --- /dev/null +++ b/experimental/notary-raft/src/main/kotlin/net/corda/notary/raft/Schema.kt @@ -0,0 +1,19 @@ +package net.corda.notary.raft + +import net.corda.core.schemas.MappedSchema +import net.corda.node.services.transactions.PersistentUniquenessProvider + +object RaftNotarySchema + +object RaftNotarySchemaV1 : MappedSchema( + schemaFamily = RaftNotarySchema.javaClass, + version = 1, + mappedTypes = listOf( + PersistentUniquenessProvider.BaseComittedState::class.java, + PersistentUniquenessProvider.Request::class.java, + RaftUniquenessProvider.CommittedState::class.java + ) +) { + override val migrationResource: String? + get() = "notary-raft.changelog-master" +} \ No newline at end of file diff --git a/experimental/notary-raft/src/main/resources/migration/notary-raft.changelog-init.xml b/experimental/notary-raft/src/main/resources/migration/notary-raft.changelog-init.xml new file mode 100644 index 0000000000..5fe85ce568 --- /dev/null +++ b/experimental/notary-raft/src/main/resources/migration/notary-raft.changelog-init.xml @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/experimental/notary-raft/src/main/resources/migration/notary-raft.changelog-master.xml b/experimental/notary-raft/src/main/resources/migration/notary-raft.changelog-master.xml new file mode 100644 index 0000000000..555344167e --- /dev/null +++ b/experimental/notary-raft/src/main/resources/migration/notary-raft.changelog-master.xml @@ -0,0 +1,11 @@ + + + + + + + + diff --git a/experimental/notary-raft/src/main/resources/migration/notary-raft.changelog-pkey.xml b/experimental/notary-raft/src/main/resources/migration/notary-raft.changelog-pkey.xml new file mode 100644 index 0000000000..24cf747d58 --- /dev/null +++ b/experimental/notary-raft/src/main/resources/migration/notary-raft.changelog-pkey.xml @@ -0,0 +1,11 @@ + + + + + + + \ No newline at end of file diff --git a/experimental/notary-raft/src/main/resources/migration/notary-raft.changelog-v1.xml b/experimental/notary-raft/src/main/resources/migration/notary-raft.changelog-v1.xml new file mode 100644 index 0000000000..f95c8e7923 --- /dev/null +++ b/experimental/notary-raft/src/main/resources/migration/notary-raft.changelog-v1.xml @@ -0,0 +1,11 @@ + + + + + + + \ No newline at end of file diff --git a/node/src/integration-test/kotlin/net/corda/node/services/RaftNotaryServiceTests.kt b/experimental/notary-raft/src/test/kotlin/net/corda/notary/raft/RaftNotaryServiceTests.kt similarity index 99% rename from node/src/integration-test/kotlin/net/corda/node/services/RaftNotaryServiceTests.kt rename to experimental/notary-raft/src/test/kotlin/net/corda/notary/raft/RaftNotaryServiceTests.kt index 43090937cc..35f6fc3f89 100644 --- a/node/src/integration-test/kotlin/net/corda/node/services/RaftNotaryServiceTests.kt +++ b/experimental/notary-raft/src/test/kotlin/net/corda/notary/raft/RaftNotaryServiceTests.kt @@ -1,4 +1,4 @@ -package net.corda.node.services +package net.corda.notary.raft import net.corda.core.contracts.StateAndRef import net.corda.core.contracts.StateRef diff --git a/node/src/integration-test/kotlin/net/corda/node/services/transactions/RaftTransactionCommitLogTests.kt b/experimental/notary-raft/src/test/kotlin/net/corda/notary/raft/RaftTransactionCommitLogTests.kt similarity index 98% rename from node/src/integration-test/kotlin/net/corda/node/services/transactions/RaftTransactionCommitLogTests.kt rename to experimental/notary-raft/src/test/kotlin/net/corda/notary/raft/RaftTransactionCommitLogTests.kt index 38389ef595..8b0f31f12d 100644 --- a/node/src/integration-test/kotlin/net/corda/node/services/transactions/RaftTransactionCommitLogTests.kt +++ b/experimental/notary-raft/src/test/kotlin/net/corda/notary/raft/RaftTransactionCommitLogTests.kt @@ -1,4 +1,4 @@ -package net.corda.node.services.transactions +package net.corda.notary.raft import io.atomix.catalyst.transport.Address import io.atomix.copycat.client.ConnectionStrategies @@ -16,7 +16,7 @@ import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.getOrThrow import net.corda.node.internal.configureDatabase import net.corda.node.services.schema.NodeSchemaService -import net.corda.node.utilities.TestingNamedCacheFactory +import net.corda.testing.internal.TestingNamedCacheFactory import net.corda.nodeapi.internal.persistence.CordaPersistence import net.corda.nodeapi.internal.persistence.DatabaseConfig import net.corda.testing.core.ALICE_NAME @@ -154,7 +154,7 @@ class RaftTransactionCommitLogTests { private fun createReplica(myAddress: NetworkHostAndPort, clusterAddress: NetworkHostAndPort? = null): CompletableFuture { val storage = Storage.builder().withStorageLevel(StorageLevel.MEMORY).build() val address = Address(myAddress.host, myAddress.port) - val database = configureDatabase(makeTestDataSourceProperties(), DatabaseConfig(), { null }, { null }, NodeSchemaService(includeNotarySchemas = true)) + val database = configureDatabase(makeTestDataSourceProperties(), DatabaseConfig(), { null }, { null }, NodeSchemaService(extraSchemas = setOf(RaftNotarySchemaV1))) databases.add(database) val stateMachineFactory = { RaftTransactionCommitLog(database, Clock.systemUTC(), { RaftUniquenessProvider.createMap(TestingNamedCacheFactory()) }) } diff --git a/node/build.gradle b/node/build.gradle index ff7be256dc..b100797903 100644 --- a/node/build.gradle +++ b/node/build.gradle @@ -153,18 +153,9 @@ dependencies { // We only need this dependency to compile our Caplet against. compileOnly "co.paralleluniverse:capsule:$capsule_version" - // Java Atomix: RAFT library - compile 'io.atomix.copycat:copycat-client:1.2.8' - compile 'io.atomix.copycat:copycat-server:1.2.8' - compile 'io.atomix.catalyst:catalyst-netty:1.2.1' - // OkHTTP: Simple HTTP library. compile "com.squareup.okhttp3:okhttp:$okhttp_version" - // BFT-SMaRt - compile 'commons-codec:commons-codec:1.10' - compile 'com.github.bft-smart:library:master-v1.1-beta-g6215ec8-87' - // Apache Shiro: authentication, authorization and session management. compile "org.apache.shiro:shiro-core:${shiro_version}" diff --git a/node/src/integration-test/kotlin/net/corda/node/services/distributed/DistributedServiceTests.kt b/node/src/integration-test/kotlin/net/corda/node/services/distributed/DistributedServiceTests.kt index 9bebdc794c..1bb2148e96 100644 --- a/node/src/integration-test/kotlin/net/corda/node/services/distributed/DistributedServiceTests.kt +++ b/node/src/integration-test/kotlin/net/corda/node/services/distributed/DistributedServiceTests.kt @@ -42,7 +42,7 @@ class DistributedServiceTests { invokeRpc(CordaRPCOps::stateMachinesFeed)) ) driver(DriverParameters( - extraCordappPackagesToScan = listOf("net.corda.finance.contracts", "net.corda.finance.schemas"), + extraCordappPackagesToScan = listOf("net.corda.finance.contracts", "net.corda.finance.schemas", "net.corda.notary.raft"), notarySpecs = listOf( NotarySpec( DUMMY_NOTARY_NAME, diff --git a/node/src/integration-test/kotlin/net/corda/node/services/messaging/ArtemisMessagingTest.kt b/node/src/integration-test/kotlin/net/corda/node/services/messaging/ArtemisMessagingTest.kt index fef890d2ec..4a427940cf 100644 --- a/node/src/integration-test/kotlin/net/corda/node/services/messaging/ArtemisMessagingTest.kt +++ b/node/src/integration-test/kotlin/net/corda/node/services/messaging/ArtemisMessagingTest.kt @@ -14,7 +14,6 @@ import net.corda.node.services.config.configureWithDevSSLCertificate import net.corda.node.services.network.PersistentNetworkMapCache import net.corda.node.services.transactions.PersistentUniquenessProvider import net.corda.node.utilities.AffinityExecutor.ServiceAffinityExecutor -import net.corda.node.utilities.TestingNamedCacheFactory import net.corda.nodeapi.internal.persistence.CordaPersistence import net.corda.nodeapi.internal.persistence.DatabaseConfig import net.corda.testing.core.ALICE_NAME @@ -26,6 +25,7 @@ import net.corda.testing.internal.rigorousMock import net.corda.testing.internal.stubs.CertificateStoreStubs import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties import net.corda.testing.node.internal.MOCK_VERSION_INFO +import net.corda.testing.internal.TestingNamedCacheFactory import org.apache.activemq.artemis.api.core.ActiveMQConnectionTimedOutException import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThatThrownBy diff --git a/node/src/integration-test/kotlin/net/corda/node/services/network/PersistentNetworkMapCacheTest.kt b/node/src/integration-test/kotlin/net/corda/node/services/network/PersistentNetworkMapCacheTest.kt index 1eea4f49db..8f35b7a543 100644 --- a/node/src/integration-test/kotlin/net/corda/node/services/network/PersistentNetworkMapCacheTest.kt +++ b/node/src/integration-test/kotlin/net/corda/node/services/network/PersistentNetworkMapCacheTest.kt @@ -5,11 +5,11 @@ import net.corda.core.utilities.NetworkHostAndPort import net.corda.node.internal.configureDatabase import net.corda.node.internal.schemas.NodeInfoSchemaV1 import net.corda.node.services.identity.InMemoryIdentityService -import net.corda.node.utilities.TestingNamedCacheFactory import net.corda.nodeapi.internal.DEV_ROOT_CA import net.corda.nodeapi.internal.persistence.DatabaseConfig import net.corda.testing.core.* import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties +import net.corda.testing.internal.TestingNamedCacheFactory import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThatIllegalArgumentException import org.junit.After diff --git a/node/src/integration-test/kotlin/net/corda/services/messaging/P2PMessagingTest.kt b/node/src/integration-test/kotlin/net/corda/services/messaging/P2PMessagingTest.kt index 004f27a026..f23aeb1a4d 100644 --- a/node/src/integration-test/kotlin/net/corda/services/messaging/P2PMessagingTest.kt +++ b/node/src/integration-test/kotlin/net/corda/services/messaging/P2PMessagingTest.kt @@ -40,6 +40,7 @@ class P2PMessagingTest { private fun startDriverWithDistributedService(dsl: DriverDSL.(List) -> Unit) { driver(DriverParameters( startNodesInProcess = true, + extraCordappPackagesToScan = listOf("net.corda.notary.raft"), notarySpecs = listOf(NotarySpec(DISTRIBUTED_SERVICE_NAME, cluster = ClusterSpec.Raft(clusterSize = 2))) )) { dsl(defaultNotaryHandle.nodeHandles.getOrThrow().map { (it as InProcess) }) diff --git a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt index 3c0be454c7..bea2d435ae 100644 --- a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt +++ b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt @@ -34,9 +34,7 @@ import net.corda.node.CordaClock import net.corda.node.VersionInfo import net.corda.node.cordapp.CordappLoader import net.corda.node.internal.classloading.requireAnnotation -import net.corda.node.internal.cordapp.CordappConfigFileProvider -import net.corda.node.internal.cordapp.CordappProviderImpl -import net.corda.node.internal.cordapp.CordappProviderInternal +import net.corda.node.internal.cordapp.* import net.corda.node.internal.rpc.proxies.AuthenticatedRpcOpsProxy import net.corda.node.internal.rpc.proxies.ExceptionMaskingRpcOpsProxy import net.corda.node.internal.rpc.proxies.ExceptionSerialisingRpcOpsProxy @@ -115,7 +113,6 @@ abstract class AbstractNode(val configuration: NodeConfiguration, val platformClock: CordaClock, cacheFactoryPrototype: NamedCacheFactory, protected val versionInfo: VersionInfo, - protected val cordappLoader: CordappLoader, protected val serverThread: AffinityExecutor.ServiceAffinityExecutor, private val busyNodeLatch: ReusableLatch = ReusableLatch()) : SingletonSerializeAsToken() { @@ -141,7 +138,8 @@ abstract class AbstractNode(val configuration: NodeConfiguration, } } - val schemaService = NodeSchemaService(cordappLoader.cordappSchemas, configuration.notary != null).tokenize() + protected val cordappLoader: CordappLoader = makeCordappLoader(configuration, versionInfo) + val schemaService = NodeSchemaService(cordappLoader.cordappSchemas).tokenize() val identityService = PersistentIdentityService(cacheFactory).tokenize() val database: CordaPersistence = createCordaPersistence( configuration.database, @@ -498,6 +496,24 @@ abstract class AbstractNode(val configuration: NodeConfiguration, ) } + private fun makeCordappLoader(configuration: NodeConfiguration, versionInfo: VersionInfo): CordappLoader { + val generatedCordapps = mutableListOf(VirtualCordapp.generateCoreCordapp(versionInfo)) + if (isRunningSimpleNotaryService(configuration)) { + // For backwards compatibility purposes the single node notary implementation is built-in: a virtual + // CorDapp will be generated. + generatedCordapps += VirtualCordapp.generateSimpleNotaryCordapp(versionInfo) + } + return JarScanningCordappLoader.fromDirectories( + configuration.cordappDirectories, + versionInfo, + extraCordapps = generatedCordapps + ) + } + + private fun isRunningSimpleNotaryService(configuration: NodeConfiguration): Boolean { + return configuration.notary != null && configuration.notary?.className == SimpleNotaryService::class.java.name + } + private class ServiceInstantiationException(cause: Throwable?) : CordaException("Service Instantiation Error", cause) private fun installCordaServices(myNotaryIdentity: PartyAndCertificate?) { @@ -780,17 +796,32 @@ abstract class AbstractNode(val configuration: NodeConfiguration, } private fun makeNotaryService(myNotaryIdentity: PartyAndCertificate?): NotaryService? { - return configuration.notary?.let { - makeCoreNotaryService(it, myNotaryIdentity).also { - it.tokenize() - runOnStop += it::stop - installCoreFlow(NotaryFlow.Client::class, it::createServiceFlow) - log.info("Running core notary: ${it.javaClass.name}") - it.start() + return configuration.notary?.let { notaryConfig -> + val serviceClass = getNotaryServiceClass(notaryConfig.className) + log.info("Starting notary service: $serviceClass") + + val notaryKey = myNotaryIdentity?.owningKey + ?: throw IllegalArgumentException("Unable to start notary service $serviceClass: notary identity not found") + val constructor = serviceClass.getDeclaredConstructor(ServiceHubInternal::class.java, PublicKey::class.java).apply { isAccessible = true } + val service = constructor.newInstance(services, notaryKey) as NotaryService + + service.run { + tokenize() + runOnStop += ::stop + installCoreFlow(NotaryFlow.Client::class, ::createServiceFlow) + start() } + return service } } + private fun getNotaryServiceClass(className: String): Class { + val loadedImplementations = cordappLoader.cordapps.mapNotNull { it.notaryService } + log.debug("Notary service implementations found: ${loadedImplementations.joinToString(", ")}") + return loadedImplementations.firstOrNull { it.name == className } + ?: throw IllegalArgumentException("The notary service implementation specified in the configuration: $className is not found. Available implementations: ${loadedImplementations.joinToString(", ")}}") + } + protected open fun makeKeyManagementService(identityService: PersistentIdentityService): KeyManagementServiceInternal { // Place the long term identity key in the KMS. Eventually, this is likely going to be separated again because // the KMS is meant for derived temporary keys used in transactions, and we're not supposed to sign things with @@ -798,32 +829,6 @@ abstract class AbstractNode(val configuration: NodeConfiguration, return PersistentKeyManagementService(cacheFactory, identityService, database) } - private fun makeCoreNotaryService(notaryConfig: NotaryConfig, myNotaryIdentity: PartyAndCertificate?): NotaryService { - val notaryKey = myNotaryIdentity?.owningKey - ?: throw IllegalArgumentException("No notary identity initialized when creating a notary service") - return notaryConfig.run { - when { - raft != null -> { - val uniquenessProvider = RaftUniquenessProvider(configuration.baseDirectory, configuration.p2pSslOptions, database, platformClock, monitoringService.metrics, cacheFactory, raft) - (if (validating) ::RaftValidatingNotaryService else ::RaftNonValidatingNotaryService)(services, notaryKey, uniquenessProvider) - } - bftSMaRt != null -> { - if (validating) throw IllegalArgumentException("Validating BFTSMaRt notary not supported") - BFTNonValidatingNotaryService(services, notaryKey, bftSMaRt, makeBFTCluster(notaryKey, bftSMaRt)) - } - else -> (if (validating) ::ValidatingNotaryService else ::SimpleNotaryService)(services, notaryKey) - } - } - } - - protected open fun makeBFTCluster(notaryKey: PublicKey, bftSMaRtConfig: BFTSMaRtConfiguration): BFTSMaRt.Cluster { - return object : BFTSMaRt.Cluster { - override fun waitUntilAllReplicasHaveInitialized() { - log.warn("A BFT replica may still be initializing, in which case the upcoming consensus change may cause it to spin.") - } - } - } - open fun stop() { // TODO: We need a good way of handling "nice to have" shutdown events, especially those that deal with the // network, including unsubscribing from updates from remote services. Possibly some sort of parameter to stop() diff --git a/node/src/main/kotlin/net/corda/node/internal/Node.kt b/node/src/main/kotlin/net/corda/node/internal/Node.kt index 3db80bdf25..0af843dd5b 100644 --- a/node/src/main/kotlin/net/corda/node/internal/Node.kt +++ b/node/src/main/kotlin/net/corda/node/internal/Node.kt @@ -28,10 +28,8 @@ import net.corda.core.utilities.contextLogger import net.corda.node.CordaClock import net.corda.node.SimpleClock import net.corda.node.VersionInfo -import net.corda.node.cordapp.CordappLoader import net.corda.node.internal.artemis.ArtemisBroker import net.corda.node.internal.artemis.BrokerAddresses -import net.corda.node.internal.cordapp.JarScanningCordappLoader import net.corda.node.internal.security.RPCSecurityManager import net.corda.node.internal.security.RPCSecurityManagerImpl import net.corda.node.internal.security.RPCSecurityManagerWithAdditionalUser @@ -87,14 +85,12 @@ class NodeWithInfo(val node: Node, val info: NodeInfo) { open class Node(configuration: NodeConfiguration, versionInfo: VersionInfo, private val initialiseSerialization: Boolean = true, - cordappLoader: CordappLoader = makeCordappLoader(configuration, versionInfo), cacheFactoryPrototype: NamedCacheFactory = DefaultNamedCacheFactory() ) : AbstractNode( configuration, createClock(configuration), cacheFactoryPrototype, versionInfo, - cordappLoader, // Under normal (non-test execution) it will always be "1" AffinityExecutor.ServiceAffinityExecutor("Node thread-${sameVmNodeCounter.incrementAndGet()}", 1) ) { @@ -132,10 +128,6 @@ open class Node(configuration: NodeConfiguration, private val sameVmNodeCounter = AtomicInteger() - private fun makeCordappLoader(configuration: NodeConfiguration, versionInfo: VersionInfo): CordappLoader { - return JarScanningCordappLoader.fromDirectories(configuration.cordappDirectories, versionInfo) - } - // TODO: make this configurable. const val MAX_RPC_MESSAGE_SIZE = 10485760 diff --git a/node/src/main/kotlin/net/corda/node/internal/NodeStartup.kt b/node/src/main/kotlin/net/corda/node/internal/NodeStartup.kt index 1b089a7c63..110a29cdca 100644 --- a/node/src/main/kotlin/net/corda/node/internal/NodeStartup.kt +++ b/node/src/main/kotlin/net/corda/node/internal/NodeStartup.kt @@ -21,7 +21,6 @@ import net.corda.node.services.config.NodeConfiguration import net.corda.node.services.config.NodeConfigurationImpl import net.corda.node.services.config.shouldStartLocalShell import net.corda.node.services.config.shouldStartSSHDaemon -import net.corda.node.services.transactions.bftSMaRtSerialFilter import net.corda.node.utilities.createKeyPairAndSelfSignedTLSCertificate import net.corda.node.utilities.registration.HTTPNetworkRegistrationService import net.corda.node.utilities.registration.NodeRegistrationException @@ -256,7 +255,8 @@ open class NodeStartup : CordaCliWrapper("corda", "Runs a Corda Node") { } val nodeInfo = node.start() - logLoadedCorDapps(node.services.cordappProvider.cordapps) + val loadedCodapps = node.services.cordappProvider.cordapps.filter { it.isLoaded } + logLoadedCorDapps(loadedCodapps) node.nodeReadyFuture.thenMatch({ // Elapsed time in seconds. We used 10 / 100.0 and not directly / 1000.0 to only keep two decimal digits. @@ -411,6 +411,16 @@ open class NodeStartup : CordaCliWrapper("corda", "Runs a Corda Node") { SerialFilter.install(if (conf.notary?.bftSMaRt != null) ::bftSMaRtSerialFilter else ::defaultSerialFilter) } + /** This filter is required for BFT-Smart to work as it only supports Java serialization. */ + // TODO: move this filter out of the node, allow Cordapps to specify filters. + private fun bftSMaRtSerialFilter(clazz: Class<*>): Boolean = clazz.name.let { + it.startsWith("bftsmart.") + || it.startsWith("java.security.") + || it.startsWith("java.util.") + || it.startsWith("java.lang.") + || it.startsWith("java.net.") + } + protected open fun getVersionInfo(): VersionInfo { return VersionInfo( PLATFORM_VERSION, diff --git a/node/src/main/kotlin/net/corda/node/internal/cordapp/JarScanningCordappLoader.kt b/node/src/main/kotlin/net/corda/node/internal/cordapp/JarScanningCordappLoader.kt index 96d1e4f435..e47badd63e 100644 --- a/node/src/main/kotlin/net/corda/node/internal/cordapp/JarScanningCordappLoader.kt +++ b/node/src/main/kotlin/net/corda/node/internal/cordapp/JarScanningCordappLoader.kt @@ -9,6 +9,8 @@ import net.corda.core.flows.* import net.corda.core.internal.* import net.corda.core.internal.cordapp.CordappImpl import net.corda.core.internal.cordapp.CordappInfoResolver +import net.corda.core.internal.notary.NotaryService +import net.corda.core.internal.notary.TrustedAuthorityNotaryService import net.corda.core.node.services.CordaService import net.corda.core.schemas.MappedSchema import net.corda.core.serialization.SerializationCustomSerializer @@ -36,9 +38,12 @@ import kotlin.streams.toList * @property cordappJarPaths The classpath of cordapp JARs */ class JarScanningCordappLoader private constructor(private val cordappJarPaths: List, - private val versionInfo: VersionInfo = VersionInfo.UNKNOWN) : CordappLoaderTemplate() { + private val versionInfo: VersionInfo = VersionInfo.UNKNOWN, + extraCordapps: List) : CordappLoaderTemplate() { - override val cordapps: List by lazy { loadCordapps() + coreCordapp } + override val cordapps: List by lazy { + loadCordapps() + extraCordapps + } override val appClassLoader: ClassLoader = URLClassLoader(cordappJarPaths.stream().map { it.url }.toTypedArray(), javaClass.classLoader) @@ -58,9 +63,10 @@ class JarScanningCordappLoader private constructor(private val cordappJarPaths: * * @param corDappDirectories Directories used to scan for CorDapp JARs. */ - fun fromDirectories(corDappDirectories: Iterable, versionInfo: VersionInfo = VersionInfo.UNKNOWN): JarScanningCordappLoader { + fun fromDirectories(corDappDirectories: Iterable, versionInfo: VersionInfo = VersionInfo.UNKNOWN, extraCordapps: List = emptyList()): JarScanningCordappLoader { logger.info("Looking for CorDapps in ${corDappDirectories.distinct().joinToString(", ", "[", "]")}") - return JarScanningCordappLoader(corDappDirectories.distinct().flatMap(this::jarUrlsInDirectory).map { it.restricted() }, versionInfo) + val paths = corDappDirectories.distinct().flatMap(this::jarUrlsInDirectory).map { it.restricted() } + return JarScanningCordappLoader(paths, versionInfo, extraCordapps) } /** @@ -68,11 +74,12 @@ class JarScanningCordappLoader private constructor(private val cordappJarPaths: * * @param scanJars Uses the JAR URLs provided for classpath scanning and Cordapp detection. */ - fun fromJarUrls(scanJars: List, versionInfo: VersionInfo = VersionInfo.UNKNOWN): JarScanningCordappLoader { - return JarScanningCordappLoader(scanJars.map { it.restricted() }, versionInfo) + fun fromJarUrls(scanJars: List, versionInfo: VersionInfo = VersionInfo.UNKNOWN, extraCordapps: List = emptyList()): JarScanningCordappLoader { + val paths = scanJars.map { it.restricted() } + return JarScanningCordappLoader(paths, versionInfo, extraCordapps) } - private fun URL.restricted(rootPackageName: String? = null) = RestrictedURL(this, rootPackageName) + private fun URL.restricted(rootPackageName: String? = null) = RestrictedURL(this, rootPackageName) private fun jarUrlsInDirectory(directory: Path): List { @@ -85,32 +92,7 @@ class JarScanningCordappLoader private constructor(private val cordappJarPaths: } } } - - /** A list of the core RPC flows present in Corda */ - private val coreRPCFlows = listOf( - ContractUpgradeFlow.Initiate::class.java, - ContractUpgradeFlow.Authorise::class.java, - ContractUpgradeFlow.Deauthorise::class.java) } - - /** A Cordapp representing the core package which is not scanned automatically. */ - @VisibleForTesting - internal val coreCordapp = CordappImpl( - contractClassNames = listOf(), - initiatedFlows = listOf(), - rpcFlows = coreRPCFlows, - serviceFlows = listOf(), - schedulableFlows = listOf(), - services = listOf(), - serializationWhitelists = listOf(), - serializationCustomSerializers = listOf(), - customSchemas = setOf(), - info = CordappImpl.Info("corda-core", versionInfo.vendor, versionInfo.releaseVersion, 1, versionInfo.platformVersion), - allFlows = listOf(), - jarPath = ContractUpgradeFlow.javaClass.location, // Core JAR location - jarHash = SecureHash.allOnesHash - ) - private fun loadCordapps(): List { val cordapps = cordappJarPaths .map { scanCordapp(it).toCordapp(it) } @@ -126,7 +108,6 @@ class JarScanningCordappLoader private constructor(private val cordappJarPaths: cordapps.forEach { CordappInfoResolver.register(it.cordappClasses, it.info) } return cordapps } - private fun RestrictedScanResult.toCordapp(url: RestrictedURL): CordappImpl { val info = url.url.openStream().let(::JarInputStream).use { it.manifest?.toCordappInfo(CordappImpl.jarName(url.url)) ?: CordappImpl.Info.UNKNOWN } return CordappImpl( @@ -142,10 +123,21 @@ class JarScanningCordappLoader private constructor(private val cordappJarPaths: findAllFlows(this), url.url, info, - getJarHash(url.url) + getJarHash(url.url), + findNotaryService(this) ) } + private fun findNotaryService(scanResult: RestrictedScanResult): Class? { + // Note: we search for implementations of both NotaryService and TrustedAuthorityNotaryService as + // the scanner won't find subclasses deeper down the hierarchy if any intermediate class is not + // present in the CorDapp. + val result = scanResult.getClassesWithSuperclass(NotaryService::class) + + scanResult.getClassesWithSuperclass(TrustedAuthorityNotaryService::class) + logger.info("Found notary service CorDapp implementations: " + result.joinToString(", ")) + return result.firstOrNull() + } + private fun getJarHash(url: URL): SecureHash.SHA256 = url.openStream().readFully().sha256() private fun findServices(scanResult: RestrictedScanResult): List> { @@ -202,7 +194,7 @@ class JarScanningCordappLoader private constructor(private val cordappJarPaths: } private fun findCustomSchemas(scanResult: RestrictedScanResult): Set { - return scanResult.getClassesWithSuperclass(MappedSchema::class).toSet() + return scanResult.getClassesWithSuperclass(MappedSchema::class).instances().toSet() } private val cachedScanResult = LRUMap(1000) @@ -243,18 +235,21 @@ class JarScanningCordappLoader private constructor(private val cordappJarPaths: val qualifiedNamePrefix: String get() = rootPackageName?.let { "$it." } ?: "" } + private fun List>.instances(): List { + return map { it.kotlin.objectOrNewInstance() } + } + private inner class RestrictedScanResult(private val scanResult: ScanResult, private val qualifiedNamePrefix: String) { fun getNamesOfClassesImplementing(type: KClass<*>): List { return scanResult.getNamesOfClassesImplementing(type.java) .filter { it.startsWith(qualifiedNamePrefix) } } - fun getClassesWithSuperclass(type: KClass): List { + fun getClassesWithSuperclass(type: KClass): List> { return scanResult.getNamesOfSubclassesOf(type.java) .filter { it.startsWith(qualifiedNamePrefix) } .mapNotNull { loadClass(it, type) } .filterNot { Modifier.isAbstract(it.modifiers) } - .map { it.kotlin.objectOrNewInstance() } } fun getClassesImplementing(type: KClass): List { diff --git a/node/src/main/kotlin/net/corda/node/internal/cordapp/VirtualCordapps.kt b/node/src/main/kotlin/net/corda/node/internal/cordapp/VirtualCordapps.kt new file mode 100644 index 0000000000..8fa3d24222 --- /dev/null +++ b/node/src/main/kotlin/net/corda/node/internal/cordapp/VirtualCordapps.kt @@ -0,0 +1,60 @@ +package net.corda.node.internal.cordapp + +import net.corda.core.crypto.SecureHash +import net.corda.core.flows.ContractUpgradeFlow +import net.corda.core.internal.cordapp.CordappImpl +import net.corda.core.internal.location +import net.corda.node.VersionInfo +import net.corda.node.services.transactions.NodeNotarySchemaV1 +import net.corda.node.services.transactions.SimpleNotaryService + +internal object VirtualCordapp { + /** A list of the core RPC flows present in Corda */ + private val coreRpcFlows = listOf( + ContractUpgradeFlow.Initiate::class.java, + ContractUpgradeFlow.Authorise::class.java, + ContractUpgradeFlow.Deauthorise::class.java + ) + + /** A Cordapp representing the core package which is not scanned automatically. */ + fun generateCoreCordapp(versionInfo: VersionInfo): CordappImpl { + return CordappImpl( + contractClassNames = listOf(), + initiatedFlows = listOf(), + rpcFlows = coreRpcFlows, + serviceFlows = listOf(), + schedulableFlows = listOf(), + services = listOf(), + serializationWhitelists = listOf(), + serializationCustomSerializers = listOf(), + customSchemas = setOf(), + info = CordappImpl.Info("corda-core", versionInfo.vendor, versionInfo.releaseVersion, 1, versionInfo.platformVersion), + allFlows = listOf(), + jarPath = ContractUpgradeFlow.javaClass.location, // Core JAR location + jarHash = SecureHash.allOnesHash, + notaryService = null, + isLoaded = false + ) + } + + /** A Cordapp for the built-in notary service implementation. */ + fun generateSimpleNotaryCordapp(versionInfo: VersionInfo): CordappImpl { + return CordappImpl( + contractClassNames = listOf(), + initiatedFlows = listOf(), + rpcFlows = listOf(), + serviceFlows = listOf(), + schedulableFlows = listOf(), + services = listOf(), + serializationWhitelists = listOf(), + serializationCustomSerializers = listOf(), + customSchemas = setOf(NodeNotarySchemaV1), + info = CordappImpl.Info("corda-notary", versionInfo.vendor, versionInfo.releaseVersion, 1, versionInfo.platformVersion), + allFlows = listOf(), + jarPath = SimpleNotaryService::class.java.location, + jarHash = SecureHash.allOnesHash, + notaryService = SimpleNotaryService::class.java, + isLoaded = false + ) + } +} \ No newline at end of file diff --git a/node/src/main/kotlin/net/corda/node/services/config/NodeConfiguration.kt b/node/src/main/kotlin/net/corda/node/services/config/NodeConfiguration.kt index 1d7f89f7bc..28e73e4600 100644 --- a/node/src/main/kotlin/net/corda/node/services/config/NodeConfiguration.kt +++ b/node/src/main/kotlin/net/corda/node/services/config/NodeConfiguration.kt @@ -123,7 +123,8 @@ data class NotaryConfig(val validating: Boolean, val raft: RaftConfig? = null, val bftSMaRt: BFTSMaRtConfiguration? = null, val custom: Boolean = false, - val serviceLegalName: CordaX500Name? = null + val serviceLegalName: CordaX500Name? = null, + val className: String = "net.corda.node.services.transactions.SimpleNotaryService" ) { init { require(raft == null || bftSMaRt == null || !custom) { diff --git a/node/src/main/kotlin/net/corda/node/services/schema/NodeSchemaService.kt b/node/src/main/kotlin/net/corda/node/services/schema/NodeSchemaService.kt index 3fdaa4b798..f75a6ad83a 100644 --- a/node/src/main/kotlin/net/corda/node/services/schema/NodeSchemaService.kt +++ b/node/src/main/kotlin/net/corda/node/services/schema/NodeSchemaService.kt @@ -16,9 +16,7 @@ import net.corda.node.services.messaging.P2PMessageDeduplicator import net.corda.node.services.persistence.DBCheckpointStorage import net.corda.node.services.persistence.DBTransactionStorage import net.corda.node.services.persistence.NodeAttachmentService -import net.corda.node.services.transactions.BFTNonValidatingNotaryService import net.corda.node.services.transactions.PersistentUniquenessProvider -import net.corda.node.services.transactions.RaftUniquenessProvider import net.corda.node.services.upgrade.ContractUpgradeServiceImpl import net.corda.node.services.vault.VaultSchemaV1 @@ -29,7 +27,7 @@ import net.corda.node.services.vault.VaultSchemaV1 * TODO: support plugins for schema version upgrading or custom mapping not supported by original [QueryableState]. * TODO: create whitelisted tables when a CorDapp is first installed */ -class NodeSchemaService(private val extraSchemas: Set = emptySet(), includeNotarySchemas: Boolean = false) : SchemaService, SingletonSerializeAsToken() { +class NodeSchemaService(private val extraSchemas: Set = emptySet()) : SchemaService, SingletonSerializeAsToken() { // Core Entities used by a Node object NodeCore @@ -47,26 +45,12 @@ class NodeSchemaService(private val extraSchemas: Set = emptySet() override val migrationResource = "node-core.changelog-master" } - // Entities used by a Notary - object NodeNotary - - object NodeNotaryV1 : MappedSchema(schemaFamily = NodeNotary.javaClass, version = 1, - mappedTypes = listOf(PersistentUniquenessProvider.BaseComittedState::class.java, - PersistentUniquenessProvider.Request::class.java, - PersistentUniquenessProvider.CommittedState::class.java, - RaftUniquenessProvider.CommittedState::class.java, - BFTNonValidatingNotaryService.CommittedState::class.java - )) { - override val migrationResource = "node-notary.changelog-master" - } - // Required schemas are those used by internal Corda services private val requiredSchemas: Map = mapOf(Pair(CommonSchemaV1, SchemaOptions()), Pair(VaultSchemaV1, SchemaOptions()), Pair(NodeInfoSchemaV1, SchemaOptions()), - Pair(NodeCoreV1, SchemaOptions())) + - if (includeNotarySchemas) mapOf(Pair(NodeNotaryV1, SchemaOptions())) else emptyMap() + Pair(NodeCoreV1, SchemaOptions())) fun internalSchemas() = requiredSchemas.keys + extraSchemas.filter { schema -> // when mapped schemas from the finance module are present, they are considered as internal ones schema::class.qualifiedName == "net.corda.finance.schemas.CashSchemaV1" || schema::class.qualifiedName == "net.corda.finance.schemas.CommercialPaperSchemaV1" } diff --git a/node/src/main/kotlin/net/corda/node/services/transactions/RaftNonValidatingNotaryService.kt b/node/src/main/kotlin/net/corda/node/services/transactions/RaftNonValidatingNotaryService.kt deleted file mode 100644 index 158d86b3c7..0000000000 --- a/node/src/main/kotlin/net/corda/node/services/transactions/RaftNonValidatingNotaryService.kt +++ /dev/null @@ -1,26 +0,0 @@ -package net.corda.node.services.transactions - -import net.corda.core.flows.FlowSession -import net.corda.core.internal.notary.NotaryServiceFlow -import net.corda.core.internal.notary.TrustedAuthorityNotaryService -import net.corda.core.node.ServiceHub -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: ServiceHub, - override val notaryIdentityKey: PublicKey, - override val uniquenessProvider: RaftUniquenessProvider -) : TrustedAuthorityNotaryService() { - override fun createServiceFlow(otherPartySession: FlowSession): NotaryServiceFlow { - return NonValidatingNotaryFlow(otherPartySession, this) - } - - override fun start() { - uniquenessProvider.start() - } - - override fun stop() { - uniquenessProvider.stop() - } -} \ No newline at end of file diff --git a/node/src/main/kotlin/net/corda/node/services/transactions/RaftValidatingNotaryService.kt b/node/src/main/kotlin/net/corda/node/services/transactions/RaftValidatingNotaryService.kt deleted file mode 100644 index f0cb8c1f8e..0000000000 --- a/node/src/main/kotlin/net/corda/node/services/transactions/RaftValidatingNotaryService.kt +++ /dev/null @@ -1,26 +0,0 @@ -package net.corda.node.services.transactions - -import net.corda.core.flows.FlowSession -import net.corda.core.internal.notary.NotaryServiceFlow -import net.corda.core.internal.notary.TrustedAuthorityNotaryService -import net.corda.core.node.ServiceHub -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: ServiceHub, - override val notaryIdentityKey: PublicKey, - override val uniquenessProvider: RaftUniquenessProvider -) : TrustedAuthorityNotaryService() { - override fun createServiceFlow(otherPartySession: FlowSession): NotaryServiceFlow { - return ValidatingNotaryFlow(otherPartySession, this) - } - - override fun start() { - uniquenessProvider.start() - } - - override fun stop() { - uniquenessProvider.stop() - } -} diff --git a/node/src/main/kotlin/net/corda/node/services/transactions/SimpleNotaryService.kt b/node/src/main/kotlin/net/corda/node/services/transactions/SimpleNotaryService.kt index 51909ea5b1..d382b2feae 100644 --- a/node/src/main/kotlin/net/corda/node/services/transactions/SimpleNotaryService.kt +++ b/node/src/main/kotlin/net/corda/node/services/transactions/SimpleNotaryService.kt @@ -3,15 +3,38 @@ package net.corda.node.services.transactions import net.corda.core.flows.FlowSession import net.corda.core.internal.notary.NotaryServiceFlow import net.corda.core.internal.notary.TrustedAuthorityNotaryService +import net.corda.core.schemas.MappedSchema import net.corda.node.services.api.ServiceHubInternal import java.security.PublicKey -/** A simple Notary service that does not perform transaction validation */ +/** An embedded notary service that uses the node's database to store committed states. */ class SimpleNotaryService(override val services: ServiceHubInternal, override val notaryIdentityKey: PublicKey) : TrustedAuthorityNotaryService() { + private val notaryConfig = services.configuration.notary + ?: throw IllegalArgumentException("Failed to register ${this::class.java}: notary configuration not present") + override val uniquenessProvider = PersistentUniquenessProvider(services.clock, services.database, services.cacheFactory) - override fun createServiceFlow(otherPartySession: FlowSession): NotaryServiceFlow = NonValidatingNotaryFlow(otherPartySession, this) + override fun createServiceFlow(otherPartySession: FlowSession): NotaryServiceFlow { + return if (notaryConfig.validating) { + log.info("Starting in validating mode") + ValidatingNotaryFlow(otherPartySession, this) + } else { + log.info("Starting in non-validating mode") + NonValidatingNotaryFlow(otherPartySession, this) + } + } override fun start() {} override fun stop() {} } + +// Entities used by a Notary +object NodeNotarySchema + +object NodeNotarySchemaV1 : MappedSchema(schemaFamily = NodeNotarySchema.javaClass, version = 1, + mappedTypes = listOf(PersistentUniquenessProvider.BaseComittedState::class.java, + PersistentUniquenessProvider.Request::class.java, + PersistentUniquenessProvider.CommittedState::class.java + )) { + override val migrationResource = "node-notary.changelog-master" +} \ No newline at end of file diff --git a/node/src/main/kotlin/net/corda/node/services/transactions/ValidatingNotaryService.kt b/node/src/main/kotlin/net/corda/node/services/transactions/ValidatingNotaryService.kt index 6e39a3ea1e..e69de29bb2 100644 --- a/node/src/main/kotlin/net/corda/node/services/transactions/ValidatingNotaryService.kt +++ b/node/src/main/kotlin/net/corda/node/services/transactions/ValidatingNotaryService.kt @@ -1,17 +0,0 @@ -package net.corda.node.services.transactions - -import net.corda.core.flows.FlowSession -import net.corda.core.internal.notary.NotaryServiceFlow -import net.corda.core.internal.notary.TrustedAuthorityNotaryService -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, override val notaryIdentityKey: PublicKey) : TrustedAuthorityNotaryService() { - override val uniquenessProvider = PersistentUniquenessProvider(services.clock, services.database, services.cacheFactory) - - override fun createServiceFlow(otherPartySession: FlowSession): NotaryServiceFlow = ValidatingNotaryFlow(otherPartySession, this) - - override fun start() {} - override fun stop() {} -} diff --git a/node/src/main/resources/migration/node-notary.changelog-init.xml b/node/src/main/resources/migration/node-notary.changelog-init.xml index fed5c691b8..8d0f1bcb6f 100644 --- a/node/src/main/resources/migration/node-notary.changelog-init.xml +++ b/node/src/main/resources/migration/node-notary.changelog-init.xml @@ -27,18 +27,6 @@ - - - - - - - - - - - - @@ -58,18 +46,11 @@ - - - + - - - diff --git a/node/src/main/resources/migration/node-notary.changelog-pkey.xml b/node/src/main/resources/migration/node-notary.changelog-pkey.xml index 64a99cc978..c4d7c59376 100644 --- a/node/src/main/resources/migration/node-notary.changelog-pkey.xml +++ b/node/src/main/resources/migration/node-notary.changelog-pkey.xml @@ -13,9 +13,4 @@ - - - - \ No newline at end of file diff --git a/node/src/main/resources/migration/node-notary.changelog-v1.xml b/node/src/main/resources/migration/node-notary.changelog-v1.xml index 0856d543b4..3002133bad 100644 --- a/node/src/main/resources/migration/node-notary.changelog-v1.xml +++ b/node/src/main/resources/migration/node-notary.changelog-v1.xml @@ -7,10 +7,6 @@ - - - - \ No newline at end of file diff --git a/node/src/test/kotlin/net/corda/node/internal/cordapp/JarScanningCordappLoaderTest.kt b/node/src/test/kotlin/net/corda/node/internal/cordapp/JarScanningCordappLoaderTest.kt index 927852e1e8..4dd255f414 100644 --- a/node/src/test/kotlin/net/corda/node/internal/cordapp/JarScanningCordappLoaderTest.kt +++ b/node/src/test/kotlin/net/corda/node/internal/cordapp/JarScanningCordappLoaderTest.kt @@ -47,7 +47,7 @@ class JarScanningCordappLoaderTest { fun `classes that aren't in cordapps aren't loaded`() { // Basedir will not be a corda node directory so the dummy flow shouldn't be recognised as a part of a cordapp val loader = JarScanningCordappLoader.fromDirectories(listOf(Paths.get("."))) - assertThat(loader.cordapps).containsOnly(loader.coreCordapp) + assertThat(loader.cordapps).isEmpty() } @Test @@ -55,9 +55,9 @@ class JarScanningCordappLoaderTest { val isolatedJAR = JarScanningCordappLoaderTest::class.java.getResource("isolated.jar")!! val loader = JarScanningCordappLoader.fromJarUrls(listOf(isolatedJAR)) - assertThat(loader.cordapps).hasSize(2) + assertThat(loader.cordapps).hasSize(1) - val actualCordapp = loader.cordapps.single { it != loader.coreCordapp } + val actualCordapp = loader.cordapps.single() assertThat(actualCordapp.contractClassNames).isEqualTo(listOf(isolatedContractId)) assertThat(actualCordapp.initiatedFlows.single().name).isEqualTo("net.corda.finance.contracts.isolated.IsolatedDummyFlow\$Acceptor") assertThat(actualCordapp.rpcFlows).isEmpty() @@ -73,8 +73,8 @@ class JarScanningCordappLoaderTest { val loader = cordappLoaderForPackages(listOf(testScanPackage)) val actual = loader.cordapps.toTypedArray() - // One core cordapp, one cordapp from this source tree. In gradle it will also pick up the node jar. - assertThat(actual.size == 2 || actual.size == 3).isTrue() + // One cordapp from this source tree. In gradle it will also pick up the node jar. + assertThat(actual.size == 0 || actual.size == 1).isTrue() val actualCordapp = actual.single { !it.initiatedFlows.isEmpty() } assertThat(actualCordapp.initiatedFlows).first().hasSameClassAs(DummyFlow::class.java) @@ -111,7 +111,7 @@ class JarScanningCordappLoaderTest { fun `cordapp classloader sets target and min version to 1 if not specified`() { val jar = JarScanningCordappLoaderTest::class.java.getResource("versions/no-min-or-target-version.jar")!! val loader = JarScanningCordappLoader.fromJarUrls(listOf(jar), VersionInfo.UNKNOWN) - loader.cordapps.filter { it.info.shortName != "corda-core" }.forEach { + loader.cordapps.forEach { assertThat(it.info.targetPlatformVersion).isEqualTo(1) assertThat(it.info.minimumPlatformVersion).isEqualTo(1) } @@ -123,8 +123,7 @@ class JarScanningCordappLoaderTest { // make sure classloader extracts correct values val jar = JarScanningCordappLoaderTest::class.java.getResource("versions/min-2-target-3.jar")!! val loader = JarScanningCordappLoader.fromJarUrls(listOf(jar), VersionInfo.UNKNOWN) - // exclude the core cordapp - val cordapp = loader.cordapps.single { it.cordappClasses.contains("net.corda.core.internal.cordapp.CordappImpl") } + val cordapp = loader.cordapps.first() assertThat(cordapp.info.targetPlatformVersion).isEqualTo(3) assertThat(cordapp.info.minimumPlatformVersion).isEqualTo(2) } @@ -144,24 +143,21 @@ class JarScanningCordappLoaderTest { fun `cordapp classloader does not load apps when their min platform version is greater than the node platform version`() { val jar = JarScanningCordappLoaderTest::class.java.getResource("versions/min-2-no-target.jar")!! val loader = JarScanningCordappLoader.fromJarUrls(listOf(jar), VersionInfo.UNKNOWN.copy(platformVersion = 1)) - // exclude the core cordapp - assertThat(loader.cordapps).hasSize(1) + assertThat(loader.cordapps).hasSize(0) } @Test fun `cordapp classloader does load apps when their min platform version is less than the platform version`() { val jar = JarScanningCordappLoaderTest::class.java.getResource("versions/min-2-target-3.jar")!! val loader = JarScanningCordappLoader.fromJarUrls(listOf(jar), VersionInfo.UNKNOWN.copy(platformVersion = 1000)) - // exclude the core cordapp - assertThat(loader.cordapps).hasSize(2) + assertThat(loader.cordapps).hasSize(1) } @Test fun `cordapp classloader does load apps when their min platform version is equal to the platform version`() { val jar = JarScanningCordappLoaderTest::class.java.getResource("versions/min-2-target-3.jar")!! val loader = JarScanningCordappLoader.fromJarUrls(listOf(jar), VersionInfo.UNKNOWN.copy(platformVersion = 2)) - // exclude the core cordapp - assertThat(loader.cordapps).hasSize(2) + assertThat(loader.cordapps).hasSize(1) } private fun cordappLoaderForPackages(packages: Iterable): CordappLoader { diff --git a/node/src/test/kotlin/net/corda/node/messaging/TwoPartyTradeFlowTests.kt b/node/src/test/kotlin/net/corda/node/messaging/TwoPartyTradeFlowTests.kt index 8303cacefd..30e4a518ba 100644 --- a/node/src/test/kotlin/net/corda/node/messaging/TwoPartyTradeFlowTests.kt +++ b/node/src/test/kotlin/net/corda/node/messaging/TwoPartyTradeFlowTests.kt @@ -313,20 +313,11 @@ class TwoPartyTradeFlowTests(private val anonymous: Boolean) { // of gets and puts. private fun makeNodeWithTracking(name: CordaX500Name): TestStartedNode { // Create a node in the mock network ... - return mockNet.createNode(InternalMockNodeParameters(legalName = name), nodeFactory = { args, cordappLoader -> - if (cordappLoader != null) { - object : InternalMockNetwork.MockNode(args, cordappLoader) { - // That constructs a recording tx storage - override fun makeTransactionStorage(transactionCacheSizeBytes: Long): WritableTransactionStorage { - return RecordingTransactionStorage(database, super.makeTransactionStorage(transactionCacheSizeBytes)) - } - } - } else { - object : InternalMockNetwork.MockNode(args) { - // That constructs a recording tx storage - override fun makeTransactionStorage(transactionCacheSizeBytes: Long): WritableTransactionStorage { - return RecordingTransactionStorage(database, super.makeTransactionStorage(transactionCacheSizeBytes)) - } + return mockNet.createNode(InternalMockNodeParameters(legalName = name), nodeFactory = { args -> + object : InternalMockNetwork.MockNode(args) { + // That constructs a recording tx storage + override fun makeTransactionStorage(transactionCacheSizeBytes: Long): WritableTransactionStorage { + return RecordingTransactionStorage(database, super.makeTransactionStorage(transactionCacheSizeBytes)) } } }) @@ -611,7 +602,7 @@ class TwoPartyTradeFlowTests(private val anonymous: Boolean) { fillUpForBuyer(bobError, issuer, bob, notary).second } val alicesFakePaper = aliceNode.database.transaction { - fillUpForSeller(aliceError, issuer, alice,1200.DOLLARS `issued by` issuer, null, notary).second + fillUpForSeller(aliceError, issuer, alice, 1200.DOLLARS `issued by` issuer, null, notary).second } insertFakeTransactions(bobsBadCash, bobNode, bob, notaryNode, bankNode) diff --git a/node/src/test/kotlin/net/corda/node/services/TimedFlowTests.kt b/node/src/test/kotlin/net/corda/node/services/TimedFlowTests.kt index 1edf477911..71eef309ad 100644 --- a/node/src/test/kotlin/net/corda/node/services/TimedFlowTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/TimedFlowTests.kt @@ -22,6 +22,7 @@ import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.TransactionBuilder import net.corda.core.utilities.ProgressTracker import net.corda.core.utilities.seconds +import net.corda.node.services.api.ServiceHubInternal import net.corda.node.services.config.FlowTimeoutConfiguration import net.corda.node.services.config.NodeConfiguration import net.corda.node.services.config.NotaryConfig @@ -90,6 +91,7 @@ class TimedFlowTests { whenever(it.custom).thenReturn(true) whenever(it.isClusterConfig).thenReturn(true) whenever(it.validating).thenReturn(true) + whenever(it.className).thenReturn(TestNotaryService::class.java.name) } val notaryNodes = (0 until CLUSTER_SIZE).map { @@ -176,8 +178,7 @@ class TimedFlowTests { }.bufferUntilSubscribed().toBlocking().toFuture() } - @CordaService - private class TestNotaryService(override val services: AppServiceHub, override val notaryIdentityKey: PublicKey) : TrustedAuthorityNotaryService() { + private class TestNotaryService(override val services: ServiceHubInternal, override val notaryIdentityKey: PublicKey) : TrustedAuthorityNotaryService() { override val uniquenessProvider = mock() override fun createServiceFlow(otherPartySession: FlowSession): FlowLogic = TestNotaryFlow(otherPartySession, this) override fun start() {} diff --git a/node/src/test/kotlin/net/corda/node/services/identity/PersistentIdentityServiceTests.kt b/node/src/test/kotlin/net/corda/node/services/identity/PersistentIdentityServiceTests.kt index 091a47d168..186cfb13cc 100644 --- a/node/src/test/kotlin/net/corda/node/services/identity/PersistentIdentityServiceTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/identity/PersistentIdentityServiceTests.kt @@ -8,7 +8,7 @@ import net.corda.core.identity.Party import net.corda.core.identity.PartyAndCertificate import net.corda.core.node.services.UnknownAnonymousPartyException import net.corda.node.internal.configureDatabase -import net.corda.node.utilities.TestingNamedCacheFactory +import net.corda.testing.internal.TestingNamedCacheFactory import net.corda.nodeapi.internal.crypto.CertificateType import net.corda.nodeapi.internal.crypto.X509Utilities import net.corda.nodeapi.internal.crypto.x509Certificates diff --git a/node/src/test/kotlin/net/corda/node/services/persistence/AppendOnlyPersistentMapTest.kt b/node/src/test/kotlin/net/corda/node/services/persistence/AppendOnlyPersistentMapTest.kt index 5ed16e74a8..b52d8f39a8 100644 --- a/node/src/test/kotlin/net/corda/node/services/persistence/AppendOnlyPersistentMapTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/persistence/AppendOnlyPersistentMapTest.kt @@ -5,7 +5,7 @@ import net.corda.core.utilities.loggerFor import net.corda.node.internal.configureDatabase import net.corda.node.services.schema.NodeSchemaService import net.corda.node.utilities.AppendOnlyPersistentMap -import net.corda.node.utilities.TestingNamedCacheFactory +import net.corda.testing.internal.TestingNamedCacheFactory import net.corda.nodeapi.internal.persistence.DatabaseConfig import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties import org.junit.After diff --git a/node/src/test/kotlin/net/corda/node/services/persistence/DBTransactionStorageTests.kt b/node/src/test/kotlin/net/corda/node/services/persistence/DBTransactionStorageTests.kt index f0c7c95859..44c92b6630 100644 --- a/node/src/test/kotlin/net/corda/node/services/persistence/DBTransactionStorageTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/persistence/DBTransactionStorageTests.kt @@ -9,7 +9,7 @@ import net.corda.core.toFuture import net.corda.core.transactions.SignedTransaction import net.corda.node.internal.configureDatabase import net.corda.node.services.transactions.PersistentUniquenessProvider -import net.corda.node.utilities.TestingNamedCacheFactory +import net.corda.testing.internal.TestingNamedCacheFactory import net.corda.nodeapi.internal.persistence.CordaPersistence import net.corda.nodeapi.internal.persistence.DatabaseConfig import net.corda.testing.core.* @@ -154,7 +154,8 @@ class DBTransactionStorageTests { } private fun newTransactionStorage(cacheSizeBytesOverride: Long? = null) { - transactionStorage = DBTransactionStorage(database, TestingNamedCacheFactory(cacheSizeBytesOverride ?: 1024)) + transactionStorage = DBTransactionStorage(database, TestingNamedCacheFactory(cacheSizeBytesOverride + ?: 1024)) } private fun assertTransactionIsRetrievable(transaction: SignedTransaction) { diff --git a/node/src/test/kotlin/net/corda/node/services/persistence/HibernateColumnConverterTests.kt b/node/src/test/kotlin/net/corda/node/services/persistence/HibernateColumnConverterTests.kt index 0c5213eb63..4ce3b354e5 100644 --- a/node/src/test/kotlin/net/corda/node/services/persistence/HibernateColumnConverterTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/persistence/HibernateColumnConverterTests.kt @@ -9,7 +9,7 @@ import net.corda.finance.contracts.asset.Cash import net.corda.finance.flows.CashIssueFlow import net.corda.node.services.identity.PersistentIdentityService import net.corda.node.services.keys.E2ETestKeyManagementService -import net.corda.node.utilities.TestingNamedCacheFactory +import net.corda.testing.internal.TestingNamedCacheFactory import net.corda.testing.core.BOC_NAME import net.corda.testing.node.InMemoryMessagingNetwork import net.corda.testing.node.MockNetwork diff --git a/node/src/test/kotlin/net/corda/node/services/persistence/NodeAttachmentServiceTest.kt b/node/src/test/kotlin/net/corda/node/services/persistence/NodeAttachmentServiceTest.kt index 70c827b496..e4f6549e48 100644 --- a/node/src/test/kotlin/net/corda/node/services/persistence/NodeAttachmentServiceTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/persistence/NodeAttachmentServiceTest.kt @@ -15,7 +15,7 @@ import net.corda.core.node.services.vault.Sort import net.corda.core.utilities.getOrThrow import net.corda.node.internal.configureDatabase import net.corda.node.services.transactions.PersistentUniquenessProvider -import net.corda.node.utilities.TestingNamedCacheFactory +import net.corda.testing.internal.TestingNamedCacheFactory import net.corda.nodeapi.internal.persistence.CordaPersistence import net.corda.nodeapi.internal.persistence.DatabaseConfig import net.corda.testing.internal.LogHelper diff --git a/node/src/test/kotlin/net/corda/node/services/schema/NodeSchemaServiceTest.kt b/node/src/test/kotlin/net/corda/node/services/schema/NodeSchemaServiceTest.kt index e258334897..2dad47df2c 100644 --- a/node/src/test/kotlin/net/corda/node/services/schema/NodeSchemaServiceTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/schema/NodeSchemaServiceTest.kt @@ -10,20 +10,18 @@ import net.corda.core.schemas.PersistentState import net.corda.core.utilities.getOrThrow import net.corda.node.services.api.ServiceHubInternal import net.corda.node.services.schema.NodeSchemaService.NodeCoreV1 -import net.corda.node.services.schema.NodeSchemaService.NodeNotaryV1 import net.corda.testing.driver.DriverParameters import net.corda.testing.driver.driver import net.corda.testing.driver.internal.InProcessImpl import net.corda.testing.internal.vault.DummyLinearStateSchemaV1 -import net.corda.testing.node.internal.cordappsForPackages import net.corda.testing.node.internal.InternalMockNetwork +import net.corda.testing.node.internal.cordappsForPackages import org.hibernate.annotations.Cascade import org.hibernate.annotations.CascadeType import org.junit.Ignore import org.junit.Test import javax.persistence.* import kotlin.test.assertEquals -import kotlin.test.assertFalse import kotlin.test.assertTrue class NodeSchemaServiceTest { @@ -47,7 +45,6 @@ class NodeSchemaServiceTest { // check against NodeCore schemas assertTrue(schemaService.schemaOptions.containsKey(NodeCoreV1)) - assertFalse(schemaService.schemaOptions.containsKey(NodeNotaryV1)) mockNet.stopNodes() } @@ -57,9 +54,8 @@ class NodeSchemaServiceTest { val mockNotaryNode = mockNet.notaryNodes.first() val schemaService = mockNotaryNode.services.schemaService - // check against NodeCore + NodeNotary Schemas + // check against NodeCore Schema assertTrue(schemaService.schemaOptions.containsKey(NodeCoreV1)) - assertTrue(schemaService.schemaOptions.containsKey(NodeNotaryV1)) mockNet.stopNodes() } @@ -97,7 +93,6 @@ class NodeSchemaServiceTest { val mappedSchemas = result.returnValue.getOrThrow() // check against NodeCore schemas assertTrue(mappedSchemas.contains(NodeCoreV1.name)) - assertFalse(mappedSchemas.contains(NodeNotaryV1.name)) // still gets loaded due TODO restriction } } @@ -107,9 +102,8 @@ class NodeSchemaServiceTest { driver(DriverParameters(startNodesInProcess = true)) { val notary = defaultNotaryNode.getOrThrow() val mappedSchemas = notary.rpc.startFlow(::MappedSchemasFlow).returnValue.getOrThrow() - // check against NodeCore + NodeNotary Schemas + // check against NodeCore Schema assertTrue(mappedSchemas.contains(NodeCoreV1.name)) - assertTrue(mappedSchemas.contains(NodeNotaryV1.name)) } } diff --git a/node/src/test/kotlin/net/corda/node/services/transactions/MaxTransactionSizeTests.kt b/node/src/test/kotlin/net/corda/node/services/transactions/MaxTransactionSizeTests.kt index 14ff42b13e..28db2d7d13 100644 --- a/node/src/test/kotlin/net/corda/node/services/transactions/MaxTransactionSizeTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/transactions/MaxTransactionSizeTests.kt @@ -37,7 +37,7 @@ class MaxTransactionSizeTests { @Before fun setup() { - mockNet = MockNetwork(listOf("net.corda.testing.contracts", "net.corda.node.services.transactions"), networkParameters = testNetworkParameters(maxTransactionSize = 3_000_000)) + mockNet = MockNetwork(listOf("net.corda.testing.contracts"), networkParameters = testNetworkParameters(maxTransactionSize = 3_000_000)) aliceNode = mockNet.createNode(MockNodeParameters(legalName = ALICE_NAME)) bobNode = mockNet.createNode(MockNodeParameters(legalName = BOB_NAME)) notaryNode = mockNet.defaultNotaryNode diff --git a/node/src/test/kotlin/net/corda/node/services/transactions/PersistentUniquenessProviderTests.kt b/node/src/test/kotlin/net/corda/node/services/transactions/PersistentUniquenessProviderTests.kt index 4bf28c246c..65401708d4 100644 --- a/node/src/test/kotlin/net/corda/node/services/transactions/PersistentUniquenessProviderTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/transactions/PersistentUniquenessProviderTests.kt @@ -10,7 +10,7 @@ import net.corda.core.identity.CordaX500Name import net.corda.core.internal.notary.NotaryInternalException import net.corda.node.internal.configureDatabase import net.corda.node.services.schema.NodeSchemaService -import net.corda.node.utilities.TestingNamedCacheFactory +import net.corda.testing.internal.TestingNamedCacheFactory import net.corda.nodeapi.internal.persistence.CordaPersistence import net.corda.nodeapi.internal.persistence.DatabaseConfig import net.corda.testing.core.SerializationEnvironmentRule @@ -39,7 +39,7 @@ class PersistentUniquenessProviderTests { @Before fun setUp() { LogHelper.setLevel(PersistentUniquenessProvider::class) - database = configureDatabase(makeTestDataSourceProperties(), DatabaseConfig(), { null }, { null }, NodeSchemaService(includeNotarySchemas = true)) + database = configureDatabase(makeTestDataSourceProperties(), DatabaseConfig(), { null }, { null }, NodeSchemaService(extraSchemas = setOf(NodeNotarySchemaV1))) } @After diff --git a/node/src/test/kotlin/net/corda/node/services/vault/VaultSoftLockManagerTest.kt b/node/src/test/kotlin/net/corda/node/services/vault/VaultSoftLockManagerTest.kt index 93e60859fe..073800fd0b 100644 --- a/node/src/test/kotlin/net/corda/node/services/vault/VaultSoftLockManagerTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/vault/VaultSoftLockManagerTest.kt @@ -82,7 +82,7 @@ class VaultSoftLockManagerTest { private val mockVault = rigorousMock().also { doNothing().whenever(it).softLockRelease(any(), anyOrNull()) } - private val mockNet = InternalMockNetwork(cordappsForAllNodes = cordappsForPackages(ContractImpl::class.packageName), defaultFactory = { args, _ -> + private val mockNet = InternalMockNetwork(cordappsForAllNodes = cordappsForPackages(ContractImpl::class.packageName), defaultFactory = { args -> object : InternalMockNetwork.MockNode(args) { override fun makeVaultService(keyManagementService: KeyManagementService, services: ServicesForResolution, database: CordaPersistence): VaultServiceInternal { val node = this diff --git a/samples/notary-demo/build.gradle b/samples/notary-demo/build.gradle index 586d67109b..7f52642b53 100644 --- a/samples/notary-demo/build.gradle +++ b/samples/notary-demo/build.gradle @@ -4,10 +4,8 @@ apply plugin: 'java' apply plugin: 'kotlin' apply plugin: 'idea' apply plugin: 'net.corda.plugins.quasar-utils' -apply plugin: 'net.corda.plugins.publish-utils' apply plugin: 'net.corda.plugins.cordapp' apply plugin: 'net.corda.plugins.cordformation' -apply plugin: 'maven-publish' configurations { integrationTestCompile.extendsFrom testCompile @@ -16,7 +14,6 @@ configurations { dependencies { compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" - testCompile "junit:junit:$junit_version" // Corda integration dependencies cordaCompile project(path: ":node:capsule", configuration: 'runtimeArtifacts') @@ -24,26 +21,11 @@ dependencies { cordaCompile project(':core') cordaCompile project(':client:jfx') cordaCompile project(':client:rpc') - cordaCompile project(':node-driver') -} + cordaCompile project(':test-utils') -idea { - module { - downloadJavadoc = true // defaults to false - downloadSources = true - } -} - -publishing { - publications { - jarAndSources(MavenPublication) { - from components.java - artifactId 'notarydemo' - - artifact sourceJar - artifact javadocJar - } - } + // Notary implementations + cordapp project(':experimental:notary-raft') + cordapp project(':experimental:notary-bft-smart') } task deployNodes(dependsOn: ['deployNodesSingle', 'deployNodesRaft', 'deployNodesBFT', 'deployNodesCustom']) @@ -99,9 +81,11 @@ task deployNodesCustom(type: Cordform, dependsOn: 'jar') { } task deployNodesRaft(type: Cordform, dependsOn: 'jar') { + def className = "net.corda.notary.raft.RaftNotaryService" directory file("$buildDir/nodes/nodesRaft") nodeDefaults { extraConfig = [h2Settings: [address: "localhost:0"]] + cordapp project(':experimental:notary-raft') } node { name "O=Alice Corp,L=Madrid,C=ES" @@ -124,7 +108,8 @@ task deployNodesRaft(type: Cordform, dependsOn: 'jar') { serviceLegalName: "O=Raft,L=Zurich,C=CH", raft: [ nodeAddress: "localhost:10008" - ] + ], + className: className ] } node { @@ -140,7 +125,8 @@ task deployNodesRaft(type: Cordform, dependsOn: 'jar') { raft: [ nodeAddress: "localhost:10012", clusterAddresses: ["localhost:10008"] - ] + ], + className: className ] } node { @@ -156,16 +142,19 @@ task deployNodesRaft(type: Cordform, dependsOn: 'jar') { raft: [ nodeAddress: "localhost:10016", clusterAddresses: ["localhost:10008"] - ] + ], + className: className ] } } task deployNodesBFT(type: Cordform, dependsOn: 'jar') { def clusterAddresses = ["localhost:11000", "localhost:11010", "localhost:11020", "localhost:11030"] + def className = "net.corda.notary.bftsmart.BftSmartNotaryService" directory file("$buildDir/nodes/nodesBFT") nodeDefaults { extraConfig = [h2Settings: [address: "localhost:0"]] + cordapp project(':experimental:notary-bft-smart') } node { name "O=Alice Corp,L=Madrid,C=ES" @@ -189,7 +178,8 @@ task deployNodesBFT(type: Cordform, dependsOn: 'jar') { bftSMaRt: [ replicaId: 0, clusterAddresses: clusterAddresses - ] + ], + className: className ] } node { @@ -203,9 +193,10 @@ task deployNodesBFT(type: Cordform, dependsOn: 'jar') { validating: false, serviceLegalName: "O=BFT,L=Zurich,C=CH", bftSMaRt: [ - replicaId: 0, + replicaId: 1, clusterAddresses: clusterAddresses - ] + ], + className: className ] } node { @@ -219,9 +210,10 @@ task deployNodesBFT(type: Cordform, dependsOn: 'jar') { validating: false, serviceLegalName: "O=BFT,L=Zurich,C=CH", bftSMaRt: [ - replicaId: 0, + replicaId: 2, clusterAddresses: clusterAddresses - ] + ], + className: className ] } node { @@ -235,9 +227,10 @@ task deployNodesBFT(type: Cordform, dependsOn: 'jar') { validating: false, serviceLegalName: "O=BFT,L=Zurich,C=CH", bftSMaRt: [ - replicaId: 0, + replicaId: 3, clusterAddresses: clusterAddresses - ] + ], + className: className ] } } diff --git a/settings.gradle b/settings.gradle index 026a092f27..118033c379 100644 --- a/settings.gradle +++ b/settings.gradle @@ -23,6 +23,8 @@ include 'experimental:behave' include 'experimental:quasar-hook' include 'experimental:kryo-hook' include 'experimental:corda-utils' +include 'experimental:notary-raft' +include 'experimental:notary-bft-smart' include 'jdk8u-deterministic' include 'test-common' include 'test-cli' diff --git a/testing/node-driver/build.gradle b/testing/node-driver/build.gradle index 102249761e..cdcb25d649 100644 --- a/testing/node-driver/build.gradle +++ b/testing/node-driver/build.gradle @@ -25,6 +25,8 @@ sourceSets { } dependencies { + // Bundling in the Raft notary service for tests involving distributed notaries + compile project(':experimental:notary-raft') compile project(':test-utils') // Integration test helpers diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/DriverDSLImpl.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/DriverDSLImpl.kt index 92615f3fd0..bd562e41f5 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/DriverDSLImpl.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/DriverDSLImpl.kt @@ -473,6 +473,7 @@ class DriverDSLImpl( val config = NotaryConfig( validating = spec.validating, serviceLegalName = spec.name, + className = "net.corda.notary.raft.RaftNotaryService", raft = RaftConfig(nodeAddress = nodeAddress, clusterAddresses = clusterAddresses)) return config.toConfigMap() } diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/InternalMockNetwork.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/InternalMockNetwork.kt index 9d6ce49198..05ebe55eae 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/InternalMockNetwork.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/InternalMockNetwork.kt @@ -28,14 +28,15 @@ import net.corda.core.utilities.contextLogger import net.corda.core.utilities.hours import net.corda.core.utilities.seconds import net.corda.node.VersionInfo -import net.corda.node.cordapp.CordappLoader import net.corda.node.internal.AbstractNode import net.corda.node.internal.InitiatedFlowFactory -import net.corda.node.internal.cordapp.JarScanningCordappLoader import net.corda.node.services.api.FlowStarter import net.corda.node.services.api.ServiceHubInternal import net.corda.node.services.api.StartedNodeServices -import net.corda.node.services.config.* +import net.corda.node.services.config.FlowTimeoutConfiguration +import net.corda.node.services.config.NodeConfiguration +import net.corda.node.services.config.NotaryConfig +import net.corda.node.services.config.VerifierType import net.corda.node.services.identity.PersistentIdentityService import net.corda.node.services.keys.E2ETestKeyManagementService import net.corda.node.services.keys.KeyManagementServiceInternal @@ -43,8 +44,6 @@ import net.corda.node.services.messaging.Message import net.corda.node.services.messaging.MessagingService import net.corda.node.services.persistence.NodeAttachmentService import net.corda.node.services.statemachine.StateMachineManager -import net.corda.node.services.transactions.BFTNonValidatingNotaryService -import net.corda.node.services.transactions.BFTSMaRt import net.corda.node.utilities.AffinityExecutor.ServiceAffinityExecutor import net.corda.node.utilities.DefaultNamedCacheFactory import net.corda.nodeapi.internal.DevIdentityGenerator @@ -69,7 +68,6 @@ import java.math.BigInteger import java.nio.file.Path import java.nio.file.Paths import java.security.KeyPair -import java.security.PublicKey import java.time.Clock import java.util.concurrent.TimeUnit import java.util.concurrent.atomic.AtomicInteger @@ -149,7 +147,7 @@ open class InternalMockNetwork(defaultParameters: MockNetworkParameters = MockNe val notarySpecs: List = defaultParameters.notarySpecs, val testDirectory: Path = Paths.get("build", getTimestampAsDirectoryName()), val networkParameters: NetworkParameters = testNetworkParameters(), - val defaultFactory: (MockNodeArgs, CordappLoader?) -> MockNode = { args, cordappLoader -> cordappLoader?.let { MockNode(args, it) } ?: MockNode(args) }, + val defaultFactory: (MockNodeArgs) -> MockNode = { args -> MockNode(args) }, val cordappsForAllNodes: Set = emptySet(), val autoVisibleNodes: Boolean = true) : AutoCloseable { init { @@ -276,12 +274,11 @@ open class InternalMockNetwork(defaultParameters: MockNetworkParameters = MockNe } } - open class MockNode(args: MockNodeArgs, cordappLoader: CordappLoader = JarScanningCordappLoader.fromDirectories(args.config.cordappDirectories, args.version)) : AbstractNode( + open class MockNode(args: MockNodeArgs) : AbstractNode( args.config, TestClock(Clock.systemUTC()), DefaultNamedCacheFactory(), args.version, - cordappLoader, args.network.getServerThread(args.id), args.network.busyLatch ) { @@ -424,27 +421,13 @@ open class InternalMockNetwork(defaultParameters: MockNetworkParameters = MockNe var acceptableLiveFiberCountOnStop: Int = 0 override fun acceptableLiveFiberCountOnStop(): Int = acceptableLiveFiberCountOnStop - - override fun makeBFTCluster(notaryKey: PublicKey, bftSMaRtConfig: BFTSMaRtConfiguration): BFTSMaRt.Cluster { - return object : BFTSMaRt.Cluster { - override fun waitUntilAllReplicasHaveInitialized() { - val clusterNodes = mockNet.nodes.map { it.started!! }.filter { notaryKey in it.info.legalIdentities.map { it.owningKey } } - if (clusterNodes.size != bftSMaRtConfig.clusterAddresses.size) { - throw IllegalStateException("Unable to enumerate all nodes in BFT cluster.") - } - clusterNodes.forEach { - (it.notaryService as BFTNonValidatingNotaryService).waitUntilReplicaHasInitialized() - } - } - } - } } fun createUnstartedNode(parameters: InternalMockNodeParameters = InternalMockNodeParameters()): MockNode { return createUnstartedNode(parameters, defaultFactory) } - fun createUnstartedNode(parameters: InternalMockNodeParameters = InternalMockNodeParameters(), nodeFactory: (MockNodeArgs, CordappLoader?) -> MockNode): MockNode { + fun createUnstartedNode(parameters: InternalMockNodeParameters = InternalMockNodeParameters(), nodeFactory: (MockNodeArgs) -> MockNode): MockNode { return createNodeImpl(parameters, nodeFactory, false) } @@ -453,11 +436,11 @@ open class InternalMockNetwork(defaultParameters: MockNetworkParameters = MockNe } /** Like the other [createNode] but takes a [nodeFactory] and propagates its [MockNode] subtype. */ - fun createNode(parameters: InternalMockNodeParameters = InternalMockNodeParameters(), nodeFactory: (MockNodeArgs, CordappLoader?) -> MockNode): TestStartedNode { + fun createNode(parameters: InternalMockNodeParameters = InternalMockNodeParameters(), nodeFactory: (MockNodeArgs) -> MockNode): TestStartedNode { return uncheckedCast(createNodeImpl(parameters, nodeFactory, true).started)!! } - private fun createNodeImpl(parameters: InternalMockNodeParameters, nodeFactory: (MockNodeArgs, CordappLoader?) -> MockNode, start: Boolean): MockNode { + private fun createNodeImpl(parameters: InternalMockNodeParameters, nodeFactory: (MockNodeArgs) -> MockNode, start: Boolean): MockNode { val id = parameters.forcedID ?: nextNodeId++ val baseDirectory = baseDirectory(id) val certificatesDirectory = baseDirectory / "certificates" @@ -474,7 +457,7 @@ open class InternalMockNetwork(defaultParameters: MockNetworkParameters = MockNe val cordappDirectories = sharedCorDappsDirectories + TestCordappDirectories.cached(cordapps) doReturn(cordappDirectories).whenever(config).cordappDirectories - val node = nodeFactory(MockNodeArgs(config, this, id, parameters.entropyRoot, parameters.version), JarScanningCordappLoader.fromDirectories(cordappDirectories, parameters.version)) + val node = nodeFactory(MockNodeArgs(config, this, id, parameters.entropyRoot, parameters.version)) _nodes += node if (start) { node.start() @@ -482,7 +465,7 @@ open class InternalMockNetwork(defaultParameters: MockNetworkParameters = MockNe return node } - fun restartNode(node: TestStartedNode, nodeFactory: (MockNodeArgs, CordappLoader?) -> MockNode): TestStartedNode { + fun restartNode(node: TestStartedNode, nodeFactory: (MockNodeArgs) -> MockNode): TestStartedNode { node.internals.disableDBCloseOnStop() node.dispose() return createNode( diff --git a/node/src/test/kotlin/net/corda/node/utilities/TestingNamedCacheFactory.kt b/testing/test-utils/src/main/kotlin/net/corda/testing/internal/TestingNamedCacheFactory.kt similarity index 95% rename from node/src/test/kotlin/net/corda/node/utilities/TestingNamedCacheFactory.kt rename to testing/test-utils/src/main/kotlin/net/corda/testing/internal/TestingNamedCacheFactory.kt index 4582246e09..7908d5dd27 100644 --- a/node/src/test/kotlin/net/corda/node/utilities/TestingNamedCacheFactory.kt +++ b/testing/test-utils/src/main/kotlin/net/corda/testing/internal/TestingNamedCacheFactory.kt @@ -1,4 +1,4 @@ -package net.corda.node.utilities +package net.corda.testing.internal import com.codahale.metrics.MetricRegistry import com.github.benmanes.caffeine.cache.Cache @@ -9,6 +9,7 @@ import net.corda.core.internal.buildNamed import net.corda.core.serialization.SingletonSerializeAsToken import net.corda.node.services.config.MB import net.corda.node.services.config.NodeConfiguration +import net.corda.node.utilities.NamedCacheFactory class TestingNamedCacheFactory private constructor(private val sizeOverride: Long, private val metricRegistry: MetricRegistry?, private val nodeConfiguration: NodeConfiguration?) : NamedCacheFactory, SingletonSerializeAsToken() { constructor(sizeOverride: Long = 1024) : this(sizeOverride, null, null)