diff --git a/.idea/compiler.xml b/.idea/compiler.xml index 2ab20fde69..3f3b539ad8 100644 --- a/.idea/compiler.xml +++ b/.idea/compiler.xml @@ -218,6 +218,8 @@ + + @@ -232,6 +234,8 @@ + + @@ -328,4 +332,4 @@ - + \ No newline at end of file diff --git a/build.gradle b/build.gradle index d92c4ea889..9f4a085b55 100644 --- a/build.gradle +++ b/build.gradle @@ -403,7 +403,9 @@ bintrayConfig { 'corda-notary-healthcheck-contract', 'corda-notary-healthcheck-cordapp', 'corda-notary-healthcheck-client', - '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 f6fa0fe452..2fc3806206 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 @@ -94,7 +92,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 4b215aabce..05858e5805 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 6668f8c373..cfd7df9cdc 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 com.typesafe.config.ConfigFactory import io.atomix.catalyst.transport.Address @@ -17,7 +17,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 @@ -156,7 +156,7 @@ class RaftTransactionCommitLogTests { val storage = Storage.builder().withStorageLevel(StorageLevel.MEMORY).build() val address = Address(myAddress.host, myAddress.port) // Enterprise - OS difference: below configureDatabase parameters differs with OS intentionally to be able run test in remote database - val database = configureDatabase(makeInternalTestDataSourceProperties( configSupplier = { ConfigFactory.empty() }), DatabaseConfig(runMigration = true), { null }, { null }, NodeSchemaService(includeNotarySchemas = true)) + val database = configureDatabase(makeInternalTestDataSourceProperties( configSupplier = { ConfigFactory.empty() }), DatabaseConfig(runMigration = true), { 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 40876c3373..d69f58623f 100644 --- a/node/build.gradle +++ b/node/build.gradle @@ -155,18 +155,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 014e1ce453..ec3b92f392 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 @@ -52,7 +52,7 @@ class DistributedServiceTests : IntegrationTest() { 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 d652517a2e..2a63d16066 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 @@ -16,7 +16,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 @@ -30,6 +29,8 @@ import net.corda.testing.node.internal.MOCK_VERSION_INFO import net.corda.testing.node.internal.makeInternalTestDataSourceProperties import org.apache.activemq.artemis.api.core.Message.HDR_VALIDATED_USER import org.apache.activemq.artemis.api.core.SimpleString +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 import org.junit.After 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 a4768c1e28..7a061840aa 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,7 +5,6 @@ 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.CordaPersistence import net.corda.nodeapi.internal.persistence.DatabaseConfig @@ -14,6 +13,7 @@ import net.corda.testing.internal.IntegrationTest import net.corda.testing.internal.IntegrationTestSchemas import net.corda.testing.internal.toDatabaseSchemaName import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties +import net.corda.testing.internal.TestingNamedCacheFactory import net.corda.testing.node.internal.makeTestDatabaseProperties import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThatIllegalArgumentException 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 8e560bc538..b2d0a0f290 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 @@ -48,8 +48,9 @@ class P2PMessagingTest : IntegrationTest() { private fun startDriverWithDistributedService(dsl: DriverDSL.(List) -> Unit) { driver(DriverParameters( - startNodesInProcess = true, - notarySpecs = listOf(NotarySpec(DISTRIBUTED_SERVICE_NAME, cluster = ClusterSpec.Raft(clusterSize = 2))) + 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 32e1c25bb4..0f53ff55e4 100644 --- a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt +++ b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt @@ -118,7 +118,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, protected val busyNodeLatch: ReusableLatch = ReusableLatch()) : SingletonSerializeAsToken() { @@ -144,7 +143,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, @@ -515,6 +515,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?) { @@ -810,17 +828,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 @@ -828,35 +861,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)) - } - mysql != null -> { - (if (validating) ::MySQLValidatingNotaryService else ::MySQLNonValidatingNotaryService)(services, notaryKey, mysql, configuration.devMode) - } - 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 df8807330f..1723aec99e 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 @@ -88,14 +86,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) ) { @@ -133,10 +129,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 ea6745247e..caf89a6f69 100644 --- a/node/src/main/kotlin/net/corda/node/internal/NodeStartup.kt +++ b/node/src/main/kotlin/net/corda/node/internal/NodeStartup.kt @@ -22,7 +22,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 @@ -259,7 +258,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. @@ -426,6 +426,16 @@ open class NodeStartup : CordaCliWrapper("corda", "Runs a Corda Node") { SerialFilter.install(filter) } + /** 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 3fe0ee2875..a7d557911e 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 @@ -137,7 +137,8 @@ data class NotaryConfig(val validating: Boolean, val bftSMaRt: BFTSMaRtConfiguration? = null, val custom: Boolean = false, val mysql: MySQLConfiguration? = null, - val serviceLegalName: CordaX500Name? = null + val serviceLegalName: CordaX500Name? = null, + val className: String = "net.corda.node.services.transactions.SimpleNotaryService" ) { init { require(raft == null || bftSMaRt == null || !custom || mysql == null) { 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 0855cd80a5..2367ac5fa6 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 @@ -14,12 +14,10 @@ import net.corda.node.services.identity.PersistentIdentityService import net.corda.node.services.keys.PersistentKeyManagementService import net.corda.node.services.messaging.P2PMessageDeduplicator import net.corda.node.services.persistence.DBCheckpointStorage +import net.corda.node.services.persistence.RunOnceService import net.corda.node.services.persistence.DBTransactionStorage import net.corda.node.services.persistence.NodeAttachmentService -import net.corda.node.services.persistence.RunOnceService -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 @@ -30,7 +28,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 @@ -49,26 +47,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(VaultSchemaV1, SchemaOptions()), + Pair(NodeInfoSchemaV1, SchemaOptions()), + 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 605338f8c3..88ffd0711e 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 @@ -60,12 +61,13 @@ class TimedFlowTestRule(val clusterSize: Int) : ExternalResource() { replicaIds.map { mockNet.baseDirectory(mockNet.nextNodeId + it) }, CordaX500Name("Custom Notary", "Zurich", "CH")) - val networkParameters = NetworkParametersCopier(testNetworkParameters(listOf(NotaryInfo(notaryIdentity, true)))) - val notaryConfig = mock { - whenever(it.custom).thenReturn(true) - whenever(it.isClusterConfig).thenReturn(true) - whenever(it.validating).thenReturn(true) - } + val networkParameters = NetworkParametersCopier(testNetworkParameters(listOf(NotaryInfo(notaryIdentity, true)))) + val notaryConfig = mock { + 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 clusterSize).map { mockNet.createUnstartedNode(InternalMockNodeParameters(configOverrides = { @@ -191,8 +193,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 9fe578de44..37cffad6b9 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 d0fc54e934..1b9d96c792 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 52b42ba913..6583677215 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 2f620c79cf..b9ffd74f30 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 aaca7927d5..463a4120d9 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(runMigration = true), { null }, { null }, NodeSchemaService(includeNotarySchemas = true)) + database = configureDatabase(makeTestDataSourceProperties(), DatabaseConfig(runMigration = true), { 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 f9e560574a..a6f0f4b173 100644 --- a/settings.gradle +++ b/settings.gradle @@ -30,6 +30,8 @@ include 'experimental:flow-worker' include 'experimental:ha-testing' include 'experimental:corda-utils' include 'experimental:rpc-worker' +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 347adcbe39..82efa3749f 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 @@ -497,6 +497,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 73e7a0ed0b..88b22b7f70 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,9 +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.services.transactions.InMemoryTransactionVerifierService import net.corda.node.utilities.AffinityExecutor.ServiceAffinityExecutor import net.corda.node.utilities.DefaultNamedCacheFactory import net.corda.nodeapi.internal.DevIdentityGenerator @@ -69,7 +67,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 +146,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 +273,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 +420,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 +435,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" @@ -475,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() @@ -483,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)