mirror of
https://github.com/corda/corda.git
synced 2025-05-08 11:38:09 +00:00
Merge pull request #1473 from corda/andrius/merge-10-10
Andrius/merge 10 10
This commit is contained in:
commit
9b5b4b62b1
10
.idea/compiler.xml
generated
10
.idea/compiler.xml
generated
@ -195,6 +195,8 @@
|
|||||||
<module name="loadtest_test" target="1.8" />
|
<module name="loadtest_test" target="1.8" />
|
||||||
<module name="mock_main" target="1.8" />
|
<module name="mock_main" target="1.8" />
|
||||||
<module name="mock_test" target="1.8" />
|
<module name="mock_test" target="1.8" />
|
||||||
|
<module name="mysql_main" target="1.8" />
|
||||||
|
<module name="mysql_test" target="1.8" />
|
||||||
<module name="net.corda_buildSrc_main" target="1.8" />
|
<module name="net.corda_buildSrc_main" target="1.8" />
|
||||||
<module name="net.corda_buildSrc_test" target="1.8" />
|
<module name="net.corda_buildSrc_test" target="1.8" />
|
||||||
<module name="net.corda_canonicalizer_main" target="1.8" />
|
<module name="net.corda_canonicalizer_main" target="1.8" />
|
||||||
@ -218,6 +220,8 @@
|
|||||||
<module name="node_main" target="1.8" />
|
<module name="node_main" target="1.8" />
|
||||||
<module name="node_smokeTest" target="1.8" />
|
<module name="node_smokeTest" target="1.8" />
|
||||||
<module name="node_test" target="1.8" />
|
<module name="node_test" target="1.8" />
|
||||||
|
<module name="notary-bft-smart_main" target="1.8" />
|
||||||
|
<module name="notary-bft-smart_test" target="1.8" />
|
||||||
<module name="notary-demo_main" target="1.8" />
|
<module name="notary-demo_main" target="1.8" />
|
||||||
<module name="notary-demo_test" target="1.8" />
|
<module name="notary-demo_test" target="1.8" />
|
||||||
<module name="notary-healthcheck-client_main" target="1.8" />
|
<module name="notary-healthcheck-client_main" target="1.8" />
|
||||||
@ -227,6 +231,10 @@
|
|||||||
<module name="notary-healthcheck-cordapp_test" target="1.8" />
|
<module name="notary-healthcheck-cordapp_test" target="1.8" />
|
||||||
<module name="notary-healthcheck_main" target="1.8" />
|
<module name="notary-healthcheck_main" target="1.8" />
|
||||||
<module name="notary-healthcheck_test" target="1.8" />
|
<module name="notary-healthcheck_test" target="1.8" />
|
||||||
|
<module name="notary-raft_main" target="1.8" />
|
||||||
|
<module name="notary-raft_test" target="1.8" />
|
||||||
|
<module name="notary_main" target="1.8" />
|
||||||
|
<module name="notary_test" target="1.8" />
|
||||||
<module name="notarytest_main" target="1.8" />
|
<module name="notarytest_main" target="1.8" />
|
||||||
<module name="notarytest_test" target="1.8" />
|
<module name="notarytest_test" target="1.8" />
|
||||||
<module name="perftestcordapp_integrationTest" target="1.8" />
|
<module name="perftestcordapp_integrationTest" target="1.8" />
|
||||||
@ -328,4 +336,4 @@
|
|||||||
<component name="JavacSettings">
|
<component name="JavacSettings">
|
||||||
<option name="ADDITIONAL_OPTIONS_STRING" value="-parameters" />
|
<option name="ADDITIONAL_OPTIONS_STRING" value="-parameters" />
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</project>
|
@ -403,7 +403,9 @@ bintrayConfig {
|
|||||||
'corda-notary-healthcheck-contract',
|
'corda-notary-healthcheck-contract',
|
||||||
'corda-notary-healthcheck-cordapp',
|
'corda-notary-healthcheck-cordapp',
|
||||||
'corda-notary-healthcheck-client',
|
'corda-notary-healthcheck-client',
|
||||||
'corda-tools-cliutils'
|
'corda-tools-cliutils',
|
||||||
|
'corda-notary-raft',
|
||||||
|
'corda-notary-bft-smart'
|
||||||
]
|
]
|
||||||
license {
|
license {
|
||||||
name = 'Apache-2.0'
|
name = 'Apache-2.0'
|
||||||
|
@ -4,6 +4,7 @@ import net.corda.core.DeleteForDJVM
|
|||||||
import net.corda.core.DoNotImplement
|
import net.corda.core.DoNotImplement
|
||||||
import net.corda.core.crypto.SecureHash
|
import net.corda.core.crypto.SecureHash
|
||||||
import net.corda.core.flows.FlowLogic
|
import net.corda.core.flows.FlowLogic
|
||||||
|
import net.corda.core.internal.notary.NotaryService
|
||||||
import net.corda.core.schemas.MappedSchema
|
import net.corda.core.schemas.MappedSchema
|
||||||
import net.corda.core.serialization.SerializationCustomSerializer
|
import net.corda.core.serialization.SerializationCustomSerializer
|
||||||
import net.corda.core.serialization.SerializationWhitelist
|
import net.corda.core.serialization.SerializationWhitelist
|
||||||
@ -48,4 +49,5 @@ interface Cordapp {
|
|||||||
val jarPath: URL
|
val jarPath: URL
|
||||||
val cordappClasses: List<String>
|
val cordappClasses: List<String>
|
||||||
val jarHash: SecureHash.SHA256
|
val jarHash: SecureHash.SHA256
|
||||||
|
val notaryService: Class<out NotaryService>?
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@ import net.corda.core.DeleteForDJVM
|
|||||||
import net.corda.core.cordapp.Cordapp
|
import net.corda.core.cordapp.Cordapp
|
||||||
import net.corda.core.crypto.SecureHash
|
import net.corda.core.crypto.SecureHash
|
||||||
import net.corda.core.flows.FlowLogic
|
import net.corda.core.flows.FlowLogic
|
||||||
|
import net.corda.core.internal.notary.NotaryService
|
||||||
import net.corda.core.internal.toPath
|
import net.corda.core.internal.toPath
|
||||||
import net.corda.core.schemas.MappedSchema
|
import net.corda.core.schemas.MappedSchema
|
||||||
import net.corda.core.serialization.SerializationCustomSerializer
|
import net.corda.core.serialization.SerializationCustomSerializer
|
||||||
@ -25,7 +26,10 @@ data class CordappImpl(
|
|||||||
override val allFlows: List<Class<out FlowLogic<*>>>,
|
override val allFlows: List<Class<out FlowLogic<*>>>,
|
||||||
override val jarPath: URL,
|
override val jarPath: URL,
|
||||||
val info: Info,
|
val info: Info,
|
||||||
override val jarHash: SecureHash.SHA256) : Cordapp {
|
override val jarHash: SecureHash.SHA256,
|
||||||
|
override val notaryService: Class<out NotaryService>? = 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)
|
override val name: String = jarName(jarPath)
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
@ -37,7 +41,10 @@ data class CordappImpl(
|
|||||||
*
|
*
|
||||||
* TODO: Also add [SchedulableFlow] as a Cordapp class
|
* TODO: Also add [SchedulableFlow] as a Cordapp class
|
||||||
*/
|
*/
|
||||||
override val cordappClasses: List<String> = (rpcFlows + initiatedFlows + services + serializationWhitelists.map { javaClass }).map { it.name } + contractClassNames
|
override val cordappClasses: List<String> = 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?
|
// 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) {
|
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 }
|
fun hasUnknownFields(): Boolean = arrayOf(shortName, vendor, version).any { it == UNKNOWN_VALUE }
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -128,7 +128,7 @@ class AttachmentTests : WithMockNet {
|
|||||||
// Makes a node that doesn't do sanity checking at load time.
|
// Makes a node that doesn't do sanity checking at load time.
|
||||||
private fun makeBadNode(name: CordaX500Name) = mockNet.createNode(
|
private fun makeBadNode(name: CordaX500Name) = mockNet.createNode(
|
||||||
InternalMockNodeParameters(legalName = makeUnique(name)),
|
InternalMockNodeParameters(legalName = makeUnique(name)),
|
||||||
nodeFactory = { args, _ ->
|
nodeFactory = { args ->
|
||||||
object : InternalMockNetwork.MockNode(args) {
|
object : InternalMockNetwork.MockNode(args) {
|
||||||
override fun start() = super.start().apply { attachments.checkAttachmentsOnLoad = false }
|
override fun start() = super.start().apply { attachments.checkAttachmentsOnLoad = false }
|
||||||
}
|
}
|
||||||
|
@ -161,7 +161,7 @@ class AttachmentSerializationTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun rebootClientAndGetAttachmentContent(checkAttachmentsOnLoad: Boolean = true): String {
|
private fun rebootClientAndGetAttachmentContent(checkAttachmentsOnLoad: Boolean = true): String {
|
||||||
client = mockNet.restartNode(client) { args, _ ->
|
client = mockNet.restartNode(client) { args ->
|
||||||
object : InternalMockNetwork.MockNode(args) {
|
object : InternalMockNetwork.MockNode(args) {
|
||||||
override fun start() = super.start().apply { attachments.checkAttachmentsOnLoad = checkAttachmentsOnLoad }
|
override fun start() = super.start().apply { attachments.checkAttachmentsOnLoad = checkAttachmentsOnLoad }
|
||||||
}
|
}
|
||||||
|
@ -117,7 +117,7 @@ class FlowWorkerServiceHub(override val configuration: NodeConfiguration, overri
|
|||||||
private val metricRegistry = MetricRegistry()
|
private val metricRegistry = MetricRegistry()
|
||||||
override val cacheFactory = EnterpriseNamedCacheFactory(configuration.enterpriseConfiguration.getTracingConfig()).bindWithConfig(configuration).bindWithMetrics(metricRegistry).tokenize()
|
override val cacheFactory = EnterpriseNamedCacheFactory(configuration.enterpriseConfiguration.getTracingConfig()).bindWithConfig(configuration).bindWithMetrics(metricRegistry).tokenize()
|
||||||
|
|
||||||
override val schemaService = NodeSchemaService(cordappLoader.cordappSchemas, false).tokenize()
|
override val schemaService = NodeSchemaService(cordappLoader.cordappSchemas).tokenize()
|
||||||
override val identityService = PersistentIdentityService(cacheFactory).tokenize()
|
override val identityService = PersistentIdentityService(cacheFactory).tokenize()
|
||||||
override val database: CordaPersistence = createCordaPersistence(
|
override val database: CordaPersistence = createCordaPersistence(
|
||||||
configuration.database,
|
configuration.database,
|
||||||
|
35
experimental/notary-bft-smart/build.gradle
Normal file
35
experimental/notary-bft-smart/build.gradle
Normal file
@ -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'
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package net.corda.node.services.transactions
|
package net.corda.notary.bftsmart
|
||||||
|
|
||||||
import bftsmart.communication.ServerCommunicationSystem
|
import bftsmart.communication.ServerCommunicationSystem
|
||||||
import bftsmart.communication.client.netty.NettyClientServerCommunicationSystemClientSide
|
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.contextLogger
|
||||||
import net.corda.core.utilities.debug
|
import net.corda.core.utilities.debug
|
||||||
import net.corda.node.services.api.ServiceHubInternal
|
import net.corda.node.services.api.ServiceHubInternal
|
||||||
import net.corda.node.services.transactions.BFTSMaRt.Client
|
import net.corda.node.services.transactions.PersistentUniquenessProvider
|
||||||
import net.corda.node.services.transactions.BFTSMaRt.Replica
|
|
||||||
import net.corda.node.utilities.AppendOnlyPersistentMap
|
import net.corda.node.utilities.AppendOnlyPersistentMap
|
||||||
import net.corda.nodeapi.internal.persistence.currentDBSession
|
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.nio.file.Path
|
||||||
import java.security.PublicKey
|
import java.security.PublicKey
|
||||||
import java.util.*
|
import java.util.*
|
||||||
@ -78,7 +79,7 @@ object BFTSMaRt {
|
|||||||
fun waitUntilAllReplicasHaveInitialized()
|
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 {
|
companion object {
|
||||||
private val log = contextLogger()
|
private val log = contextLogger()
|
||||||
}
|
}
|
||||||
@ -178,7 +179,7 @@ object BFTSMaRt {
|
|||||||
abstract class Replica(config: BFTSMaRtConfig,
|
abstract class Replica(config: BFTSMaRtConfig,
|
||||||
replicaId: Int,
|
replicaId: Int,
|
||||||
createMap: () -> AppendOnlyPersistentMap<StateRef, SecureHash,
|
createMap: () -> AppendOnlyPersistentMap<StateRef, SecureHash,
|
||||||
BFTNonValidatingNotaryService.CommittedState, PersistentStateRef>,
|
BftSmartNotaryService.CommittedState, PersistentStateRef>,
|
||||||
protected val services: ServiceHubInternal,
|
protected val services: ServiceHubInternal,
|
||||||
protected val notaryIdentityKey: PublicKey) : DefaultRecoverable() {
|
protected val notaryIdentityKey: PublicKey) : DefaultRecoverable() {
|
||||||
companion object {
|
companion object {
|
@ -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.div
|
||||||
import net.corda.core.internal.writer
|
import net.corda.core.internal.writer
|
||||||
import net.corda.core.utilities.NetworkHostAndPort
|
import net.corda.core.utilities.NetworkHostAndPort
|
||||||
import net.corda.core.utilities.contextLogger
|
import net.corda.core.utilities.contextLogger
|
||||||
import net.corda.core.utilities.debug
|
import net.corda.core.utilities.debug
|
||||||
|
import net.corda.node.services.transactions.PathManager
|
||||||
import java.io.PrintWriter
|
import java.io.PrintWriter
|
||||||
import java.net.InetAddress
|
import java.net.InetAddress
|
||||||
import java.net.Socket
|
import java.net.Socket
|
@ -1,4 +1,4 @@
|
|||||||
package net.corda.node.services.transactions
|
package net.corda.notary.bftsmart
|
||||||
|
|
||||||
import co.paralleluniverse.fibers.Suspendable
|
import co.paralleluniverse.fibers.Suspendable
|
||||||
import com.google.common.util.concurrent.SettableFuture
|
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.NotaryInternalException
|
||||||
import net.corda.core.internal.notary.NotaryService
|
import net.corda.core.internal.notary.NotaryService
|
||||||
import net.corda.core.internal.notary.verifySignature
|
import net.corda.core.internal.notary.verifySignature
|
||||||
|
import net.corda.core.schemas.MappedSchema
|
||||||
import net.corda.core.schemas.PersistentStateRef
|
import net.corda.core.schemas.PersistentStateRef
|
||||||
import net.corda.core.serialization.deserialize
|
import net.corda.core.serialization.deserialize
|
||||||
import net.corda.core.serialization.serialize
|
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.core.utilities.unwrap
|
||||||
import net.corda.node.services.api.ServiceHubInternal
|
import net.corda.node.services.api.ServiceHubInternal
|
||||||
import net.corda.node.services.config.BFTSMaRtConfiguration
|
import net.corda.node.services.config.BFTSMaRtConfiguration
|
||||||
|
import net.corda.node.services.transactions.PersistentUniquenessProvider
|
||||||
import net.corda.node.utilities.AppendOnlyPersistentMap
|
import net.corda.node.utilities.AppendOnlyPersistentMap
|
||||||
import net.corda.nodeapi.internal.persistence.NODE_DATABASE_PREFIX
|
import net.corda.nodeapi.internal.persistence.NODE_DATABASE_PREFIX
|
||||||
import java.security.PublicKey
|
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.
|
* 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 services: ServiceHubInternal,
|
||||||
override val notaryIdentityKey: PublicKey,
|
override val notaryIdentityKey: PublicKey
|
||||||
private val bftSMaRtConfig: BFTSMaRtConfiguration,
|
|
||||||
cluster: BFTSMaRt.Cluster
|
|
||||||
) : NotaryService() {
|
) : NotaryService() {
|
||||||
companion object {
|
companion object {
|
||||||
private val log = contextLogger()
|
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 client: BFTSMaRt.Client
|
||||||
private val replicaHolder = SettableFuture.create<Replica>()
|
private val replicaHolder = SettableFuture.create<Replica>()
|
||||||
|
|
||||||
@ -71,7 +87,7 @@ class BFTNonValidatingNotaryService(
|
|||||||
|
|
||||||
override fun createServiceFlow(otherPartySession: FlowSession): FlowLogic<Void?> = ServiceFlow(otherPartySession, this)
|
override fun createServiceFlow(otherPartySession: FlowSession): FlowLogic<Void?> = ServiceFlow(otherPartySession, this)
|
||||||
|
|
||||||
private class ServiceFlow(val otherSideSession: FlowSession, val service: BFTNonValidatingNotaryService) : FlowLogic<Void?>() {
|
private class ServiceFlow(val otherSideSession: FlowSession, val service: BftSmartNotaryService) : FlowLogic<Void?>() {
|
||||||
@Suspendable
|
@Suspendable
|
||||||
override fun call(): Void? {
|
override fun call(): Void? {
|
||||||
val payload = otherSideSession.receive<NotarisationPayload>().unwrap { it }
|
val payload = otherSideSession.receive<NotarisationPayload>().unwrap { it }
|
@ -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"
|
||||||
|
}
|
@ -0,0 +1,45 @@
|
|||||||
|
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
|
||||||
|
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||||
|
xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog-ext http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-ext.xsd http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.5.xsd"
|
||||||
|
logicalFilePath="migration/node-services.changelog-init.xml">
|
||||||
|
<changeSet author="R3.Corda" id="1511451595465-6">
|
||||||
|
<createTable tableName="node_bft_committed_states">
|
||||||
|
<column name="output_index" type="INT">
|
||||||
|
<constraints nullable="false"/>
|
||||||
|
</column>
|
||||||
|
<column name="transaction_id" type="NVARCHAR(64)">
|
||||||
|
<constraints nullable="false"/>
|
||||||
|
</column>
|
||||||
|
<column name="consuming_transaction_id" type="NVARCHAR(64)"/>
|
||||||
|
</createTable>
|
||||||
|
</changeSet>
|
||||||
|
<changeSet author="R3.Corda" id="1521131680317-17">
|
||||||
|
<createTable tableName="node_notary_request_log">
|
||||||
|
<column name="id" type="INT">
|
||||||
|
<constraints nullable="false"/>
|
||||||
|
</column>
|
||||||
|
<column name="consuming_transaction_id" type="NVARCHAR(64)">
|
||||||
|
<constraints nullable="false"/>
|
||||||
|
</column>
|
||||||
|
<column name="requesting_party_name" type="NVARCHAR(255)">
|
||||||
|
<constraints nullable="false"/>
|
||||||
|
</column>
|
||||||
|
<column name="request_timestamp" type="TIMESTAMP">
|
||||||
|
<constraints nullable="false"/>
|
||||||
|
</column>
|
||||||
|
<column name="request_signature" type="BLOB">
|
||||||
|
<constraints nullable="false"/>
|
||||||
|
</column>
|
||||||
|
</createTable>
|
||||||
|
</changeSet>
|
||||||
|
<changeSet author="R3.Corda" id="1511451595465-31">
|
||||||
|
<addPrimaryKey columnNames="output_index, transaction_id" constraintName="node_bft_states_pkey"
|
||||||
|
tableName="node_bft_committed_states"/>
|
||||||
|
</changeSet>
|
||||||
|
<changeSet author="R3.Corda" id="1521131680317-48">
|
||||||
|
<addPrimaryKey columnNames="id" constraintName="node_notary_request_log_pkey"
|
||||||
|
tableName="node_notary_request_log"/>
|
||||||
|
</changeSet>
|
||||||
|
</databaseChangeLog>
|
@ -0,0 +1,9 @@
|
|||||||
|
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
|
||||||
|
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||||
|
xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog-ext http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-ext.xsd http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.5.xsd">
|
||||||
|
<include file="migration/node-bft-smart.changelog-init.xml"/>
|
||||||
|
<include file="migration/node-bft-smart.changelog-v1.xml"/>
|
||||||
|
<include file="migration/node-bft-smart.changelog-pkey.xml"/>
|
||||||
|
</databaseChangeLog>
|
@ -0,0 +1,11 @@
|
|||||||
|
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
|
||||||
|
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||||
|
xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog-ext http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-ext.xsd http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.5.xsd">
|
||||||
|
<changeSet id="non-clustered_pk-bft_stae" author="R3.Corda" onValidationFail="MARK_RAN">
|
||||||
|
<dropPrimaryKey tableName="node_bft_committed_states" constraintName="node_bft_states_pkey"/>
|
||||||
|
<addPrimaryKey tableName="node_bft_committed_states" columnNames="output_index, transaction_id"
|
||||||
|
constraintName="node_bft_states_pkey" clustered="false"/>
|
||||||
|
</changeSet>
|
||||||
|
</databaseChangeLog>
|
@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
|
||||||
|
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||||
|
xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog-ext http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-ext.xsd http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.5.xsd"
|
||||||
|
logicalFilePath="migration/node-services.changelog-init.xml">
|
||||||
|
<changeSet author="R3.Corda" id="nullability">
|
||||||
|
<addNotNullConstraint tableName="node_bft_committed_states" columnName="consuming_transaction_id" columnDataType="NVARCHAR(64)"/>
|
||||||
|
</changeSet>
|
||||||
|
</databaseChangeLog>
|
@ -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.doReturn
|
||||||
import com.nhaarman.mockito_kotlin.whenever
|
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.core.utilities.seconds
|
||||||
import net.corda.node.services.config.BFTSMaRtConfiguration
|
import net.corda.node.services.config.BFTSMaRtConfiguration
|
||||||
import net.corda.node.services.config.NotaryConfig
|
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.DevIdentityGenerator
|
||||||
import net.corda.nodeapi.internal.network.NetworkParametersCopier
|
import net.corda.nodeapi.internal.network.NetworkParametersCopier
|
||||||
import net.corda.testing.common.internal.testNetworkParameters
|
import net.corda.testing.common.internal.testNetworkParameters
|
||||||
@ -94,7 +92,11 @@ class BFTNotaryServiceTests {
|
|||||||
|
|
||||||
val nodes = replicaIds.map { replicaId ->
|
val nodes = replicaIds.map { replicaId ->
|
||||||
mockNet.createUnstartedNode(InternalMockNodeParameters(configOverrides = {
|
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
|
doReturn(notary).whenever(it).notary
|
||||||
}))
|
}))
|
||||||
} + mockNet.createUnstartedNode()
|
} + mockNet.createUnstartedNode()
|
@ -1,7 +1,7 @@
|
|||||||
package net.corda.node.services.transactions
|
package net.corda.notary.bftsmart
|
||||||
|
|
||||||
import net.corda.core.utilities.NetworkHostAndPort
|
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.assertj.core.api.Assertions.assertThatThrownBy
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
35
experimental/notary-raft/build.gradle
Normal file
35
experimental/notary-raft/build.gradle
Normal file
@ -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'
|
||||||
|
}
|
@ -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()
|
||||||
|
}
|
||||||
|
}
|
@ -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.BufferInput
|
||||||
import io.atomix.catalyst.buffer.BufferOutput
|
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.ByteSequence
|
||||||
import net.corda.core.utilities.contextLogger
|
import net.corda.core.utilities.contextLogger
|
||||||
import net.corda.core.utilities.debug
|
import net.corda.core.utilities.debug
|
||||||
|
import net.corda.node.services.transactions.PersistentUniquenessProvider
|
||||||
import net.corda.node.utilities.AppendOnlyPersistentMap
|
import net.corda.node.utilities.AppendOnlyPersistentMap
|
||||||
import net.corda.nodeapi.internal.persistence.CordaPersistence
|
import net.corda.nodeapi.internal.persistence.CordaPersistence
|
||||||
import net.corda.nodeapi.internal.persistence.currentDBSession
|
import net.corda.nodeapi.internal.persistence.currentDBSession
|
||||||
@ -111,7 +112,7 @@ class RaftTransactionCommitLog<E, EK>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun logRequest(commitCommand: RaftTransactionCommitLog.Commands.CommitTransaction) {
|
private fun logRequest(commitCommand: Commands.CommitTransaction) {
|
||||||
val request = PersistentUniquenessProvider.Request(
|
val request = PersistentUniquenessProvider.Request(
|
||||||
consumingTxHash = commitCommand.txId.toString(),
|
consumingTxHash = commitCommand.txId.toString(),
|
||||||
partyName = commitCommand.requestingParty,
|
partyName = commitCommand.requestingParty,
|
||||||
@ -192,8 +193,8 @@ class RaftTransactionCommitLog<E, EK>(
|
|||||||
registerAbstract(SecureHash::class.java, CordaKryoSerializer::class.java)
|
registerAbstract(SecureHash::class.java, CordaKryoSerializer::class.java)
|
||||||
registerAbstract(TimeWindow::class.java, CordaKryoSerializer::class.java)
|
registerAbstract(TimeWindow::class.java, CordaKryoSerializer::class.java)
|
||||||
registerAbstract(NotaryError::class.java, CordaKryoSerializer::class.java)
|
registerAbstract(NotaryError::class.java, CordaKryoSerializer::class.java)
|
||||||
register(RaftTransactionCommitLog.Commands.CommitTransaction::class.java, CordaKryoSerializer::class.java)
|
register(Commands.CommitTransaction::class.java, CordaKryoSerializer::class.java)
|
||||||
register(RaftTransactionCommitLog.Commands.Get::class.java, CordaKryoSerializer::class.java)
|
register(Commands.Get::class.java, CordaKryoSerializer::class.java)
|
||||||
register(StateRef::class.java, CordaKryoSerializer::class.java)
|
register(StateRef::class.java, CordaKryoSerializer::class.java)
|
||||||
register(LinkedHashMap::class.java, CordaKryoSerializer::class.java)
|
register(LinkedHashMap::class.java, CordaKryoSerializer::class.java)
|
||||||
}
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package net.corda.node.services.transactions
|
package net.corda.notary.raft
|
||||||
|
|
||||||
import com.codahale.metrics.Gauge
|
import com.codahale.metrics.Gauge
|
||||||
import com.codahale.metrics.MetricRegistry
|
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.contextLogger
|
||||||
import net.corda.core.utilities.debug
|
import net.corda.core.utilities.debug
|
||||||
import net.corda.node.services.config.RaftConfig
|
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.AppendOnlyPersistentMap
|
||||||
import net.corda.node.utilities.NamedCacheFactory
|
import net.corda.node.utilities.NamedCacheFactory
|
||||||
import net.corda.nodeapi.internal.config.MutualSslConfiguration
|
import net.corda.nodeapi.internal.config.MutualSslConfiguration
|
||||||
import net.corda.nodeapi.internal.persistence.CordaPersistence
|
import net.corda.nodeapi.internal.persistence.CordaPersistence
|
||||||
import net.corda.nodeapi.internal.persistence.NODE_DATABASE_PREFIX
|
import net.corda.nodeapi.internal.persistence.NODE_DATABASE_PREFIX
|
||||||
|
import net.corda.notary.raft.RaftTransactionCommitLog.Commands.CommitTransaction
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
import java.time.Clock
|
import java.time.Clock
|
||||||
import java.util.concurrent.CompletableFuture
|
import java.util.concurrent.CompletableFuture
|
@ -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"
|
||||||
|
}
|
@ -0,0 +1,46 @@
|
|||||||
|
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
|
||||||
|
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||||
|
xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog-ext http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-ext.xsd http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.5.xsd"
|
||||||
|
logicalFilePath="migration/node-services.changelog-init.xml">
|
||||||
|
<changeSet author="R3.Corda" id="1511451595465-18">
|
||||||
|
<createTable tableName="node_raft_committed_states">
|
||||||
|
<column name="transaction_id" type="NVARCHAR(64)">
|
||||||
|
<constraints nullable="false"/>
|
||||||
|
</column>
|
||||||
|
<column name="output_index" type="INT">
|
||||||
|
<constraints nullable="false"/>
|
||||||
|
</column>
|
||||||
|
<column name="raft_log_index" type="BIGINT"/>
|
||||||
|
<column name="consuming_transaction_id" type="NVARCHAR(64)"/>
|
||||||
|
</createTable>
|
||||||
|
</changeSet>
|
||||||
|
<changeSet author="R3.Corda" id="1521131680317-17">
|
||||||
|
<createTable tableName="node_notary_request_log">
|
||||||
|
<column name="id" type="INT">
|
||||||
|
<constraints nullable="false"/>
|
||||||
|
</column>
|
||||||
|
<column name="consuming_transaction_id" type="NVARCHAR(64)">
|
||||||
|
<constraints nullable="false"/>
|
||||||
|
</column>
|
||||||
|
<column name="requesting_party_name" type="NVARCHAR(255)">
|
||||||
|
<constraints nullable="false"/>
|
||||||
|
</column>
|
||||||
|
<column name="request_timestamp" type="TIMESTAMP">
|
||||||
|
<constraints nullable="false"/>
|
||||||
|
</column>
|
||||||
|
<column name="request_signature" type="BLOB">
|
||||||
|
<constraints nullable="false"/>
|
||||||
|
</column>
|
||||||
|
</createTable>
|
||||||
|
</changeSet>
|
||||||
|
<changeSet author="R3.Corda" id="1511451595465-43">
|
||||||
|
<addPrimaryKey columnNames="output_index, transaction_id" constraintName="node_raft_state_pkey"
|
||||||
|
tableName="node_raft_committed_states"/>
|
||||||
|
</changeSet>
|
||||||
|
<changeSet author="R3.Corda" id="1521131680317-48">
|
||||||
|
<addPrimaryKey columnNames="id" constraintName="node_notary_request_log_pkey"
|
||||||
|
tableName="node_notary_request_log"/>
|
||||||
|
</changeSet>
|
||||||
|
</databaseChangeLog>
|
@ -0,0 +1,11 @@
|
|||||||
|
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
|
||||||
|
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||||
|
xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog-ext http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-ext.xsd http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.5.xsd">
|
||||||
|
|
||||||
|
<include file="migration/node-notary-raft.changelog-init.xml"/>
|
||||||
|
<include file="migration/node-notary-raft.changelog-v1.xml"/>
|
||||||
|
<include file="migration/node-notary-raft.changelog-pkey.xml"/>
|
||||||
|
|
||||||
|
</databaseChangeLog>
|
@ -0,0 +1,11 @@
|
|||||||
|
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
|
||||||
|
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||||
|
xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog-ext http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-ext.xsd http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.5.xsd">
|
||||||
|
<changeSet id="non-clustered_pk-raft_state" author="R3.Corda" onValidationFail="MARK_RAN">
|
||||||
|
<dropPrimaryKey tableName="node_raft_committed_states" constraintName="node_raft_state_pkey"/>
|
||||||
|
<addPrimaryKey tableName="node_raft_committed_states" columnNames="output_index, transaction_id"
|
||||||
|
constraintName="node_raft_state_pkey" clustered="false"/>
|
||||||
|
</changeSet>
|
||||||
|
</databaseChangeLog>
|
@ -0,0 +1,11 @@
|
|||||||
|
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
|
||||||
|
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||||
|
xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog-ext http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-ext.xsd http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.5.xsd"
|
||||||
|
logicalFilePath="migration/node-services.changelog-init.xml">
|
||||||
|
<changeSet author="R3.Corda" id="nullability">
|
||||||
|
<addNotNullConstraint tableName="node_raft_committed_states" columnName="raft_log_index" columnDataType="BIGINT"/>
|
||||||
|
<addNotNullConstraint tableName="node_raft_committed_states" columnName="consuming_transaction_id" columnDataType="NVARCHAR(64)"/>
|
||||||
|
</changeSet>
|
||||||
|
</databaseChangeLog>
|
@ -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.StateAndRef
|
||||||
import net.corda.core.contracts.StateRef
|
import net.corda.core.contracts.StateRef
|
@ -1,4 +1,4 @@
|
|||||||
package net.corda.node.services.transactions
|
package net.corda.notary.raft
|
||||||
|
|
||||||
import com.typesafe.config.ConfigFactory
|
import com.typesafe.config.ConfigFactory
|
||||||
import io.atomix.catalyst.transport.Address
|
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.core.utilities.getOrThrow
|
||||||
import net.corda.node.internal.configureDatabase
|
import net.corda.node.internal.configureDatabase
|
||||||
import net.corda.node.services.schema.NodeSchemaService
|
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.CordaPersistence
|
||||||
import net.corda.nodeapi.internal.persistence.DatabaseConfig
|
import net.corda.nodeapi.internal.persistence.DatabaseConfig
|
||||||
import net.corda.testing.core.ALICE_NAME
|
import net.corda.testing.core.ALICE_NAME
|
||||||
@ -156,7 +156,7 @@ class RaftTransactionCommitLogTests {
|
|||||||
val storage = Storage.builder().withStorageLevel(StorageLevel.MEMORY).build()
|
val storage = Storage.builder().withStorageLevel(StorageLevel.MEMORY).build()
|
||||||
val address = Address(myAddress.host, myAddress.port)
|
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
|
// 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)
|
databases.add(database)
|
||||||
val stateMachineFactory = { RaftTransactionCommitLog(database, Clock.systemUTC(), { RaftUniquenessProvider.createMap(TestingNamedCacheFactory()) }) }
|
val stateMachineFactory = { RaftTransactionCommitLog(database, Clock.systemUTC(), { RaftUniquenessProvider.createMap(TestingNamedCacheFactory()) }) }
|
||||||
|
|
@ -75,7 +75,7 @@ class RpcWorkerServiceHub(override val configuration: NodeConfiguration, overrid
|
|||||||
private val metricRegistry = MetricRegistry()
|
private val metricRegistry = MetricRegistry()
|
||||||
override val cacheFactory = EnterpriseNamedCacheFactory(configuration.enterpriseConfiguration.getTracingConfig()).bindWithConfig(configuration).bindWithMetrics(metricRegistry)
|
override val cacheFactory = EnterpriseNamedCacheFactory(configuration.enterpriseConfiguration.getTracingConfig()).bindWithConfig(configuration).bindWithMetrics(metricRegistry)
|
||||||
|
|
||||||
override val schemaService = NodeSchemaService(cordappLoader.cordappSchemas, false)
|
override val schemaService = NodeSchemaService(cordappLoader.cordappSchemas)
|
||||||
override val identityService = PersistentIdentityService(cacheFactory)
|
override val identityService = PersistentIdentityService(cacheFactory)
|
||||||
override val database: CordaPersistence = createCordaPersistence(
|
override val database: CordaPersistence = createCordaPersistence(
|
||||||
configuration.database,
|
configuration.database,
|
||||||
|
@ -141,9 +141,6 @@ dependencies {
|
|||||||
// For H2 database support in persistence
|
// For H2 database support in persistence
|
||||||
compile "com.h2database:h2:$h2_version"
|
compile "com.h2database:h2:$h2_version"
|
||||||
|
|
||||||
// For the MySQLUniquenessProvider
|
|
||||||
compile group: 'mysql', name: 'mysql-connector-java', version: '6.0.6'
|
|
||||||
|
|
||||||
// SQL connection pooling library
|
// SQL connection pooling library
|
||||||
compile "com.zaxxer:HikariCP:${hikari_version}"
|
compile "com.zaxxer:HikariCP:${hikari_version}"
|
||||||
|
|
||||||
@ -155,18 +152,9 @@ dependencies {
|
|||||||
// We only need this dependency to compile our Caplet against.
|
// We only need this dependency to compile our Caplet against.
|
||||||
compileOnly "co.paralleluniverse:capsule:$capsule_version"
|
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.
|
// OkHTTP: Simple HTTP library.
|
||||||
compile "com.squareup.okhttp3:okhttp:$okhttp_version"
|
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.
|
// Apache Shiro: authentication, authorization and session management.
|
||||||
compile "org.apache.shiro:shiro-core:${shiro_version}"
|
compile "org.apache.shiro:shiro-core:${shiro_version}"
|
||||||
|
|
||||||
|
@ -52,7 +52,7 @@ class DistributedServiceTests : IntegrationTest() {
|
|||||||
invokeRpc(CordaRPCOps::stateMachinesFeed))
|
invokeRpc(CordaRPCOps::stateMachinesFeed))
|
||||||
)
|
)
|
||||||
driver(DriverParameters(
|
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(
|
notarySpecs = listOf(
|
||||||
NotarySpec(
|
NotarySpec(
|
||||||
DUMMY_NOTARY_NAME,
|
DUMMY_NOTARY_NAME,
|
||||||
|
@ -16,7 +16,6 @@ import net.corda.node.services.config.configureWithDevSSLCertificate
|
|||||||
import net.corda.node.services.network.PersistentNetworkMapCache
|
import net.corda.node.services.network.PersistentNetworkMapCache
|
||||||
import net.corda.node.services.transactions.PersistentUniquenessProvider
|
import net.corda.node.services.transactions.PersistentUniquenessProvider
|
||||||
import net.corda.node.utilities.AffinityExecutor.ServiceAffinityExecutor
|
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.CordaPersistence
|
||||||
import net.corda.nodeapi.internal.persistence.DatabaseConfig
|
import net.corda.nodeapi.internal.persistence.DatabaseConfig
|
||||||
import net.corda.testing.core.ALICE_NAME
|
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 net.corda.testing.node.internal.makeInternalTestDataSourceProperties
|
||||||
import org.apache.activemq.artemis.api.core.Message.HDR_VALIDATED_USER
|
import org.apache.activemq.artemis.api.core.Message.HDR_VALIDATED_USER
|
||||||
import org.apache.activemq.artemis.api.core.SimpleString
|
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.assertThat
|
||||||
import org.assertj.core.api.Assertions.assertThatThrownBy
|
import org.assertj.core.api.Assertions.assertThatThrownBy
|
||||||
import org.junit.After
|
import org.junit.After
|
||||||
|
@ -5,7 +5,6 @@ import net.corda.core.utilities.NetworkHostAndPort
|
|||||||
import net.corda.node.internal.configureDatabase
|
import net.corda.node.internal.configureDatabase
|
||||||
import net.corda.node.internal.schemas.NodeInfoSchemaV1
|
import net.corda.node.internal.schemas.NodeInfoSchemaV1
|
||||||
import net.corda.node.services.identity.InMemoryIdentityService
|
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.DEV_ROOT_CA
|
||||||
import net.corda.nodeapi.internal.persistence.CordaPersistence
|
import net.corda.nodeapi.internal.persistence.CordaPersistence
|
||||||
import net.corda.nodeapi.internal.persistence.DatabaseConfig
|
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.IntegrationTestSchemas
|
||||||
import net.corda.testing.internal.toDatabaseSchemaName
|
import net.corda.testing.internal.toDatabaseSchemaName
|
||||||
import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties
|
import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties
|
||||||
|
import net.corda.testing.internal.TestingNamedCacheFactory
|
||||||
import net.corda.testing.node.internal.makeTestDatabaseProperties
|
import net.corda.testing.node.internal.makeTestDatabaseProperties
|
||||||
import org.assertj.core.api.Assertions.assertThat
|
import org.assertj.core.api.Assertions.assertThat
|
||||||
import org.assertj.core.api.Assertions.assertThatIllegalArgumentException
|
import org.assertj.core.api.Assertions.assertThatIllegalArgumentException
|
||||||
|
@ -48,8 +48,9 @@ class P2PMessagingTest : IntegrationTest() {
|
|||||||
|
|
||||||
private fun startDriverWithDistributedService(dsl: DriverDSL.(List<InProcess>) -> Unit) {
|
private fun startDriverWithDistributedService(dsl: DriverDSL.(List<InProcess>) -> Unit) {
|
||||||
driver(DriverParameters(
|
driver(DriverParameters(
|
||||||
startNodesInProcess = true,
|
startNodesInProcess = true,
|
||||||
notarySpecs = listOf(NotarySpec(DISTRIBUTED_SERVICE_NAME, cluster = ClusterSpec.Raft(clusterSize = 2)))
|
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) })
|
dsl(defaultNotaryHandle.nodeHandles.getOrThrow().map { (it as InProcess) })
|
||||||
}
|
}
|
||||||
|
@ -35,9 +35,7 @@ import net.corda.node.CordaClock
|
|||||||
import net.corda.node.VersionInfo
|
import net.corda.node.VersionInfo
|
||||||
import net.corda.node.cordapp.CordappLoader
|
import net.corda.node.cordapp.CordappLoader
|
||||||
import net.corda.node.internal.classloading.requireAnnotation
|
import net.corda.node.internal.classloading.requireAnnotation
|
||||||
import net.corda.node.internal.cordapp.CordappConfigFileProvider
|
import net.corda.node.internal.cordapp.*
|
||||||
import net.corda.node.internal.cordapp.CordappProviderImpl
|
|
||||||
import net.corda.node.internal.cordapp.CordappProviderInternal
|
|
||||||
import net.corda.node.internal.rpc.proxies.AuthenticatedRpcOpsProxy
|
import net.corda.node.internal.rpc.proxies.AuthenticatedRpcOpsProxy
|
||||||
import net.corda.node.internal.rpc.proxies.ExceptionMaskingRpcOpsProxy
|
import net.corda.node.internal.rpc.proxies.ExceptionMaskingRpcOpsProxy
|
||||||
import net.corda.node.internal.rpc.proxies.ExceptionSerialisingRpcOpsProxy
|
import net.corda.node.internal.rpc.proxies.ExceptionSerialisingRpcOpsProxy
|
||||||
@ -118,7 +116,6 @@ abstract class AbstractNode<S>(val configuration: NodeConfiguration,
|
|||||||
val platformClock: CordaClock,
|
val platformClock: CordaClock,
|
||||||
cacheFactoryPrototype: NamedCacheFactory,
|
cacheFactoryPrototype: NamedCacheFactory,
|
||||||
protected val versionInfo: VersionInfo,
|
protected val versionInfo: VersionInfo,
|
||||||
protected val cordappLoader: CordappLoader,
|
|
||||||
protected val serverThread: AffinityExecutor.ServiceAffinityExecutor,
|
protected val serverThread: AffinityExecutor.ServiceAffinityExecutor,
|
||||||
protected val busyNodeLatch: ReusableLatch = ReusableLatch()) : SingletonSerializeAsToken() {
|
protected val busyNodeLatch: ReusableLatch = ReusableLatch()) : SingletonSerializeAsToken() {
|
||||||
|
|
||||||
@ -144,7 +141,8 @@ abstract class AbstractNode<S>(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 identityService = PersistentIdentityService(cacheFactory).tokenize()
|
||||||
val database: CordaPersistence = createCordaPersistence(
|
val database: CordaPersistence = createCordaPersistence(
|
||||||
configuration.database,
|
configuration.database,
|
||||||
@ -515,6 +513,24 @@ abstract class AbstractNode<S>(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 class ServiceInstantiationException(cause: Throwable?) : CordaException("Service Instantiation Error", cause)
|
||||||
|
|
||||||
private fun installCordaServices(myNotaryIdentity: PartyAndCertificate?) {
|
private fun installCordaServices(myNotaryIdentity: PartyAndCertificate?) {
|
||||||
@ -810,17 +826,32 @@ abstract class AbstractNode<S>(val configuration: NodeConfiguration,
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun makeNotaryService(myNotaryIdentity: PartyAndCertificate?): NotaryService? {
|
private fun makeNotaryService(myNotaryIdentity: PartyAndCertificate?): NotaryService? {
|
||||||
return configuration.notary?.let {
|
return configuration.notary?.let { notaryConfig ->
|
||||||
makeCoreNotaryService(it, myNotaryIdentity).also {
|
val serviceClass = getNotaryServiceClass(notaryConfig.className)
|
||||||
it.tokenize()
|
log.info("Starting notary service: $serviceClass")
|
||||||
runOnStop += it::stop
|
|
||||||
installCoreFlow(NotaryFlow.Client::class, it::createServiceFlow)
|
val notaryKey = myNotaryIdentity?.owningKey
|
||||||
log.info("Running core notary: ${it.javaClass.name}")
|
?: throw IllegalArgumentException("Unable to start notary service $serviceClass: notary identity not found")
|
||||||
it.start()
|
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<out NotaryService> {
|
||||||
|
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 {
|
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
|
// 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
|
// the KMS is meant for derived temporary keys used in transactions, and we're not supposed to sign things with
|
||||||
@ -828,35 +859,6 @@ abstract class AbstractNode<S>(val configuration: NodeConfiguration,
|
|||||||
return PersistentKeyManagementService(cacheFactory, identityService, database)
|
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() {
|
open fun stop() {
|
||||||
// TODO: We need a good way of handling "nice to have" shutdown events, especially those that deal with the
|
// 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()
|
// network, including unsubscribing from updates from remote services. Possibly some sort of parameter to stop()
|
||||||
|
@ -28,10 +28,8 @@ import net.corda.core.utilities.contextLogger
|
|||||||
import net.corda.node.CordaClock
|
import net.corda.node.CordaClock
|
||||||
import net.corda.node.SimpleClock
|
import net.corda.node.SimpleClock
|
||||||
import net.corda.node.VersionInfo
|
import net.corda.node.VersionInfo
|
||||||
import net.corda.node.cordapp.CordappLoader
|
|
||||||
import net.corda.node.internal.artemis.ArtemisBroker
|
import net.corda.node.internal.artemis.ArtemisBroker
|
||||||
import net.corda.node.internal.artemis.BrokerAddresses
|
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.RPCSecurityManager
|
||||||
import net.corda.node.internal.security.RPCSecurityManagerImpl
|
import net.corda.node.internal.security.RPCSecurityManagerImpl
|
||||||
import net.corda.node.internal.security.RPCSecurityManagerWithAdditionalUser
|
import net.corda.node.internal.security.RPCSecurityManagerWithAdditionalUser
|
||||||
@ -88,14 +86,12 @@ class NodeWithInfo(val node: Node, val info: NodeInfo) {
|
|||||||
open class Node(configuration: NodeConfiguration,
|
open class Node(configuration: NodeConfiguration,
|
||||||
versionInfo: VersionInfo,
|
versionInfo: VersionInfo,
|
||||||
private val initialiseSerialization: Boolean = true,
|
private val initialiseSerialization: Boolean = true,
|
||||||
cordappLoader: CordappLoader = makeCordappLoader(configuration, versionInfo),
|
|
||||||
cacheFactoryPrototype: NamedCacheFactory = DefaultNamedCacheFactory()
|
cacheFactoryPrototype: NamedCacheFactory = DefaultNamedCacheFactory()
|
||||||
) : AbstractNode<NodeInfo>(
|
) : AbstractNode<NodeInfo>(
|
||||||
configuration,
|
configuration,
|
||||||
createClock(configuration),
|
createClock(configuration),
|
||||||
cacheFactoryPrototype,
|
cacheFactoryPrototype,
|
||||||
versionInfo,
|
versionInfo,
|
||||||
cordappLoader,
|
|
||||||
// Under normal (non-test execution) it will always be "1"
|
// Under normal (non-test execution) it will always be "1"
|
||||||
AffinityExecutor.ServiceAffinityExecutor("Node thread-${sameVmNodeCounter.incrementAndGet()}", 1)
|
AffinityExecutor.ServiceAffinityExecutor("Node thread-${sameVmNodeCounter.incrementAndGet()}", 1)
|
||||||
) {
|
) {
|
||||||
@ -133,10 +129,6 @@ open class Node(configuration: NodeConfiguration,
|
|||||||
|
|
||||||
private val sameVmNodeCounter = AtomicInteger()
|
private val sameVmNodeCounter = AtomicInteger()
|
||||||
|
|
||||||
private fun makeCordappLoader(configuration: NodeConfiguration, versionInfo: VersionInfo): CordappLoader {
|
|
||||||
return JarScanningCordappLoader.fromDirectories(configuration.cordappDirectories, versionInfo)
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: make this configurable.
|
// TODO: make this configurable.
|
||||||
const val MAX_RPC_MESSAGE_SIZE = 10485760
|
const val MAX_RPC_MESSAGE_SIZE = 10485760
|
||||||
|
|
||||||
|
@ -22,7 +22,6 @@ import net.corda.node.services.config.NodeConfiguration
|
|||||||
import net.corda.node.services.config.NodeConfigurationImpl
|
import net.corda.node.services.config.NodeConfigurationImpl
|
||||||
import net.corda.node.services.config.shouldStartLocalShell
|
import net.corda.node.services.config.shouldStartLocalShell
|
||||||
import net.corda.node.services.config.shouldStartSSHDaemon
|
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.createKeyPairAndSelfSignedTLSCertificate
|
||||||
import net.corda.node.utilities.registration.HTTPNetworkRegistrationService
|
import net.corda.node.utilities.registration.HTTPNetworkRegistrationService
|
||||||
import net.corda.node.utilities.registration.NodeRegistrationException
|
import net.corda.node.utilities.registration.NodeRegistrationException
|
||||||
@ -259,7 +258,8 @@ open class NodeStartup : CordaCliWrapper("corda", "Runs a Corda Node") {
|
|||||||
}
|
}
|
||||||
|
|
||||||
val nodeInfo = node.start()
|
val nodeInfo = node.start()
|
||||||
logLoadedCorDapps(node.services.cordappProvider.cordapps)
|
val loadedCodapps = node.services.cordappProvider.cordapps.filter { it.isLoaded }
|
||||||
|
logLoadedCorDapps(loadedCodapps)
|
||||||
|
|
||||||
node.nodeReadyFuture.thenMatch({
|
node.nodeReadyFuture.thenMatch({
|
||||||
// Elapsed time in seconds. We used 10 / 100.0 and not directly / 1000.0 to only keep two decimal digits.
|
// 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)
|
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 {
|
protected open fun getVersionInfo(): VersionInfo {
|
||||||
return VersionInfo(
|
return VersionInfo(
|
||||||
PLATFORM_VERSION,
|
PLATFORM_VERSION,
|
||||||
|
@ -9,6 +9,9 @@ import net.corda.core.flows.*
|
|||||||
import net.corda.core.internal.*
|
import net.corda.core.internal.*
|
||||||
import net.corda.core.internal.cordapp.CordappImpl
|
import net.corda.core.internal.cordapp.CordappImpl
|
||||||
import net.corda.core.internal.cordapp.CordappInfoResolver
|
import net.corda.core.internal.cordapp.CordappInfoResolver
|
||||||
|
import net.corda.core.internal.notary.AsyncCFTNotaryService
|
||||||
|
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.node.services.CordaService
|
||||||
import net.corda.core.schemas.MappedSchema
|
import net.corda.core.schemas.MappedSchema
|
||||||
import net.corda.core.serialization.SerializationCustomSerializer
|
import net.corda.core.serialization.SerializationCustomSerializer
|
||||||
@ -36,9 +39,12 @@ import kotlin.streams.toList
|
|||||||
* @property cordappJarPaths The classpath of cordapp JARs
|
* @property cordappJarPaths The classpath of cordapp JARs
|
||||||
*/
|
*/
|
||||||
class JarScanningCordappLoader private constructor(private val cordappJarPaths: List<RestrictedURL>,
|
class JarScanningCordappLoader private constructor(private val cordappJarPaths: List<RestrictedURL>,
|
||||||
private val versionInfo: VersionInfo = VersionInfo.UNKNOWN) : CordappLoaderTemplate() {
|
private val versionInfo: VersionInfo = VersionInfo.UNKNOWN,
|
||||||
|
extraCordapps: List<CordappImpl>) : CordappLoaderTemplate() {
|
||||||
|
|
||||||
override val cordapps: List<CordappImpl> by lazy { loadCordapps() + coreCordapp }
|
override val cordapps: List<CordappImpl> by lazy {
|
||||||
|
loadCordapps() + extraCordapps
|
||||||
|
}
|
||||||
|
|
||||||
override val appClassLoader: ClassLoader = URLClassLoader(cordappJarPaths.stream().map { it.url }.toTypedArray(), javaClass.classLoader)
|
override val appClassLoader: ClassLoader = URLClassLoader(cordappJarPaths.stream().map { it.url }.toTypedArray(), javaClass.classLoader)
|
||||||
|
|
||||||
@ -58,9 +64,10 @@ class JarScanningCordappLoader private constructor(private val cordappJarPaths:
|
|||||||
*
|
*
|
||||||
* @param corDappDirectories Directories used to scan for CorDapp JARs.
|
* @param corDappDirectories Directories used to scan for CorDapp JARs.
|
||||||
*/
|
*/
|
||||||
fun fromDirectories(corDappDirectories: Iterable<Path>, versionInfo: VersionInfo = VersionInfo.UNKNOWN): JarScanningCordappLoader {
|
fun fromDirectories(corDappDirectories: Iterable<Path>, versionInfo: VersionInfo = VersionInfo.UNKNOWN, extraCordapps: List<CordappImpl> = emptyList()): JarScanningCordappLoader {
|
||||||
logger.info("Looking for CorDapps in ${corDappDirectories.distinct().joinToString(", ", "[", "]")}")
|
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 +75,12 @@ class JarScanningCordappLoader private constructor(private val cordappJarPaths:
|
|||||||
*
|
*
|
||||||
* @param scanJars Uses the JAR URLs provided for classpath scanning and Cordapp detection.
|
* @param scanJars Uses the JAR URLs provided for classpath scanning and Cordapp detection.
|
||||||
*/
|
*/
|
||||||
fun fromJarUrls(scanJars: List<URL>, versionInfo: VersionInfo = VersionInfo.UNKNOWN): JarScanningCordappLoader {
|
fun fromJarUrls(scanJars: List<URL>, versionInfo: VersionInfo = VersionInfo.UNKNOWN, extraCordapps: List<CordappImpl> = emptyList()): JarScanningCordappLoader {
|
||||||
return JarScanningCordappLoader(scanJars.map { it.restricted() }, versionInfo)
|
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<URL> {
|
private fun jarUrlsInDirectory(directory: Path): List<URL> {
|
||||||
|
|
||||||
@ -85,32 +93,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<CordappImpl> {
|
private fun loadCordapps(): List<CordappImpl> {
|
||||||
val cordapps = cordappJarPaths
|
val cordapps = cordappJarPaths
|
||||||
.map { scanCordapp(it).toCordapp(it) }
|
.map { scanCordapp(it).toCordapp(it) }
|
||||||
@ -126,7 +109,6 @@ class JarScanningCordappLoader private constructor(private val cordappJarPaths:
|
|||||||
cordapps.forEach { CordappInfoResolver.register(it.cordappClasses, it.info) }
|
cordapps.forEach { CordappInfoResolver.register(it.cordappClasses, it.info) }
|
||||||
return cordapps
|
return cordapps
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun RestrictedScanResult.toCordapp(url: RestrictedURL): CordappImpl {
|
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 }
|
val info = url.url.openStream().let(::JarInputStream).use { it.manifest?.toCordappInfo(CordappImpl.jarName(url.url)) ?: CordappImpl.Info.UNKNOWN }
|
||||||
return CordappImpl(
|
return CordappImpl(
|
||||||
@ -142,10 +124,22 @@ class JarScanningCordappLoader private constructor(private val cordappJarPaths:
|
|||||||
findAllFlows(this),
|
findAllFlows(this),
|
||||||
url.url,
|
url.url,
|
||||||
info,
|
info,
|
||||||
getJarHash(url.url)
|
getJarHash(url.url),
|
||||||
|
findNotaryService(this)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun findNotaryService(scanResult: RestrictedScanResult): Class<out NotaryService>? {
|
||||||
|
// 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) +
|
||||||
|
scanResult.getClassesWithSuperclass(AsyncCFTNotaryService::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 getJarHash(url: URL): SecureHash.SHA256 = url.openStream().readFully().sha256()
|
||||||
|
|
||||||
private fun findServices(scanResult: RestrictedScanResult): List<Class<out SerializeAsToken>> {
|
private fun findServices(scanResult: RestrictedScanResult): List<Class<out SerializeAsToken>> {
|
||||||
@ -202,7 +196,7 @@ class JarScanningCordappLoader private constructor(private val cordappJarPaths:
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun findCustomSchemas(scanResult: RestrictedScanResult): Set<MappedSchema> {
|
private fun findCustomSchemas(scanResult: RestrictedScanResult): Set<MappedSchema> {
|
||||||
return scanResult.getClassesWithSuperclass(MappedSchema::class).toSet()
|
return scanResult.getClassesWithSuperclass(MappedSchema::class).instances().toSet()
|
||||||
}
|
}
|
||||||
|
|
||||||
private val cachedScanResult = LRUMap<RestrictedURL, RestrictedScanResult>(1000)
|
private val cachedScanResult = LRUMap<RestrictedURL, RestrictedScanResult>(1000)
|
||||||
@ -243,18 +237,21 @@ class JarScanningCordappLoader private constructor(private val cordappJarPaths:
|
|||||||
val qualifiedNamePrefix: String get() = rootPackageName?.let { "$it." } ?: ""
|
val qualifiedNamePrefix: String get() = rootPackageName?.let { "$it." } ?: ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun <T : Any> List<Class<out T>>.instances(): List<T> {
|
||||||
|
return map { it.kotlin.objectOrNewInstance() }
|
||||||
|
}
|
||||||
|
|
||||||
private inner class RestrictedScanResult(private val scanResult: ScanResult, private val qualifiedNamePrefix: String) {
|
private inner class RestrictedScanResult(private val scanResult: ScanResult, private val qualifiedNamePrefix: String) {
|
||||||
fun getNamesOfClassesImplementing(type: KClass<*>): List<String> {
|
fun getNamesOfClassesImplementing(type: KClass<*>): List<String> {
|
||||||
return scanResult.getNamesOfClassesImplementing(type.java)
|
return scanResult.getNamesOfClassesImplementing(type.java)
|
||||||
.filter { it.startsWith(qualifiedNamePrefix) }
|
.filter { it.startsWith(qualifiedNamePrefix) }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun <T : Any> getClassesWithSuperclass(type: KClass<T>): List<T> {
|
fun <T : Any> getClassesWithSuperclass(type: KClass<T>): List<Class<out T>> {
|
||||||
return scanResult.getNamesOfSubclassesOf(type.java)
|
return scanResult.getNamesOfSubclassesOf(type.java)
|
||||||
.filter { it.startsWith(qualifiedNamePrefix) }
|
.filter { it.startsWith(qualifiedNamePrefix) }
|
||||||
.mapNotNull { loadClass(it, type) }
|
.mapNotNull { loadClass(it, type) }
|
||||||
.filterNot { Modifier.isAbstract(it.modifiers) }
|
.filterNot { Modifier.isAbstract(it.modifiers) }
|
||||||
.map { it.kotlin.objectOrNewInstance() }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun <T : Any> getClassesImplementing(type: KClass<T>): List<T> {
|
fun <T : Any> getClassesImplementing(type: KClass<T>): List<T> {
|
||||||
|
@ -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
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
@ -137,7 +137,8 @@ data class NotaryConfig(val validating: Boolean,
|
|||||||
val bftSMaRt: BFTSMaRtConfiguration? = null,
|
val bftSMaRt: BFTSMaRtConfiguration? = null,
|
||||||
val custom: Boolean = false,
|
val custom: Boolean = false,
|
||||||
val mysql: MySQLConfiguration? = null,
|
val mysql: MySQLConfiguration? = null,
|
||||||
val serviceLegalName: CordaX500Name? = null
|
val serviceLegalName: CordaX500Name? = null,
|
||||||
|
val className: String = "net.corda.node.services.transactions.SimpleNotaryService"
|
||||||
) {
|
) {
|
||||||
init {
|
init {
|
||||||
require(raft == null || bftSMaRt == null || !custom || mysql == null) {
|
require(raft == null || bftSMaRt == null || !custom || mysql == null) {
|
||||||
|
@ -14,12 +14,10 @@ import net.corda.node.services.identity.PersistentIdentityService
|
|||||||
import net.corda.node.services.keys.PersistentKeyManagementService
|
import net.corda.node.services.keys.PersistentKeyManagementService
|
||||||
import net.corda.node.services.messaging.P2PMessageDeduplicator
|
import net.corda.node.services.messaging.P2PMessageDeduplicator
|
||||||
import net.corda.node.services.persistence.DBCheckpointStorage
|
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.DBTransactionStorage
|
||||||
import net.corda.node.services.persistence.NodeAttachmentService
|
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.PersistentUniquenessProvider
|
||||||
import net.corda.node.services.transactions.RaftUniquenessProvider
|
|
||||||
import net.corda.node.services.upgrade.ContractUpgradeServiceImpl
|
import net.corda.node.services.upgrade.ContractUpgradeServiceImpl
|
||||||
import net.corda.node.services.vault.VaultSchemaV1
|
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: support plugins for schema version upgrading or custom mapping not supported by original [QueryableState].
|
||||||
* TODO: create whitelisted tables when a CorDapp is first installed
|
* TODO: create whitelisted tables when a CorDapp is first installed
|
||||||
*/
|
*/
|
||||||
class NodeSchemaService(private val extraSchemas: Set<MappedSchema> = emptySet(), includeNotarySchemas: Boolean = false) : SchemaService, SingletonSerializeAsToken() {
|
class NodeSchemaService(private val extraSchemas: Set<MappedSchema> = emptySet()) : SchemaService, SingletonSerializeAsToken() {
|
||||||
// Core Entities used by a Node
|
// Core Entities used by a Node
|
||||||
object NodeCore
|
object NodeCore
|
||||||
|
|
||||||
@ -49,26 +47,12 @@ class NodeSchemaService(private val extraSchemas: Set<MappedSchema> = emptySet()
|
|||||||
override val migrationResource = "node-core.changelog-master"
|
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
|
// Required schemas are those used by internal Corda services
|
||||||
private val requiredSchemas: Map<MappedSchema, SchemaService.SchemaOptions> =
|
private val requiredSchemas: Map<MappedSchema, SchemaService.SchemaOptions> =
|
||||||
mapOf(Pair(CommonSchemaV1, SchemaOptions()),
|
mapOf(Pair(CommonSchemaV1, SchemaOptions()),
|
||||||
Pair(VaultSchemaV1, SchemaOptions()),
|
Pair(VaultSchemaV1, SchemaOptions()),
|
||||||
Pair(NodeInfoSchemaV1, SchemaOptions()),
|
Pair(NodeInfoSchemaV1, SchemaOptions()),
|
||||||
Pair(NodeCoreV1, SchemaOptions())) +
|
Pair(NodeCoreV1, SchemaOptions()))
|
||||||
if (includeNotarySchemas) mapOf(Pair(NodeNotaryV1, SchemaOptions())) else emptyMap()
|
|
||||||
|
|
||||||
fun internalSchemas() = requiredSchemas.keys + extraSchemas.filter { schema -> // when mapped schemas from the finance module are present, they are considered as internal ones
|
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" }
|
schema::class.qualifiedName == "net.corda.finance.schemas.CashSchemaV1" || schema::class.qualifiedName == "net.corda.finance.schemas.CommercialPaperSchemaV1" }
|
||||||
|
@ -1,45 +0,0 @@
|
|||||||
package net.corda.node.services.transactions
|
|
||||||
|
|
||||||
import net.corda.core.flows.FlowLogic
|
|
||||||
import net.corda.core.flows.FlowSession
|
|
||||||
import net.corda.core.internal.notary.AsyncCFTNotaryService
|
|
||||||
import net.corda.node.services.api.ServiceHubInternal
|
|
||||||
import net.corda.node.services.config.MySQLConfiguration
|
|
||||||
import java.security.PublicKey
|
|
||||||
|
|
||||||
/** Notary service backed by a replicated MySQL database. */
|
|
||||||
abstract class MySQLNotaryService(
|
|
||||||
final override val services: ServiceHubInternal,
|
|
||||||
override val notaryIdentityKey: PublicKey,
|
|
||||||
configuration: MySQLConfiguration,
|
|
||||||
/** Database table will be automatically created in dev mode */
|
|
||||||
val devMode: Boolean) : AsyncCFTNotaryService() {
|
|
||||||
|
|
||||||
override val asyncUniquenessProvider = MySQLUniquenessProvider(
|
|
||||||
services.monitoringService.metrics,
|
|
||||||
services.clock,
|
|
||||||
configuration
|
|
||||||
)
|
|
||||||
|
|
||||||
override fun start() {
|
|
||||||
if (devMode) asyncUniquenessProvider.createTable()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun stop() {
|
|
||||||
asyncUniquenessProvider.stop()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class MySQLNonValidatingNotaryService(services: ServiceHubInternal,
|
|
||||||
notaryIdentityKey: PublicKey,
|
|
||||||
configuration: MySQLConfiguration,
|
|
||||||
devMode: Boolean = false) : MySQLNotaryService(services, notaryIdentityKey, configuration, devMode) {
|
|
||||||
override fun createServiceFlow(otherPartySession: FlowSession): FlowLogic<Void?> = NonValidatingNotaryFlow(otherPartySession, this)
|
|
||||||
}
|
|
||||||
|
|
||||||
class MySQLValidatingNotaryService(services: ServiceHubInternal,
|
|
||||||
notaryIdentityKey: PublicKey,
|
|
||||||
configuration: MySQLConfiguration,
|
|
||||||
devMode: Boolean = false) : MySQLNotaryService(services, notaryIdentityKey, configuration, devMode) {
|
|
||||||
override fun createServiceFlow(otherPartySession: FlowSession): FlowLogic<Void?> = ValidatingNotaryFlow(otherPartySession, this)
|
|
||||||
}
|
|
@ -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()
|
|
||||||
}
|
|
||||||
}
|
|
@ -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()
|
|
||||||
}
|
|
||||||
}
|
|
@ -3,15 +3,38 @@ package net.corda.node.services.transactions
|
|||||||
import net.corda.core.flows.FlowSession
|
import net.corda.core.flows.FlowSession
|
||||||
import net.corda.core.internal.notary.NotaryServiceFlow
|
import net.corda.core.internal.notary.NotaryServiceFlow
|
||||||
import net.corda.core.internal.notary.TrustedAuthorityNotaryService
|
import net.corda.core.internal.notary.TrustedAuthorityNotaryService
|
||||||
|
import net.corda.core.schemas.MappedSchema
|
||||||
import net.corda.node.services.api.ServiceHubInternal
|
import net.corda.node.services.api.ServiceHubInternal
|
||||||
import java.security.PublicKey
|
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() {
|
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 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 start() {}
|
||||||
override fun stop() {}
|
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"
|
||||||
|
}
|
@ -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() {}
|
|
||||||
}
|
|
@ -27,18 +27,6 @@
|
|||||||
<column name="consuming_transaction_id" type="NVARCHAR(64)"/>
|
<column name="consuming_transaction_id" type="NVARCHAR(64)"/>
|
||||||
</createTable>
|
</createTable>
|
||||||
</changeSet>
|
</changeSet>
|
||||||
<changeSet author="R3.Corda" id="1511451595465-18">
|
|
||||||
<createTable tableName="node_raft_committed_states">
|
|
||||||
<column name="transaction_id" type="NVARCHAR(64)">
|
|
||||||
<constraints nullable="false"/>
|
|
||||||
</column>
|
|
||||||
<column name="output_index" type="INT">
|
|
||||||
<constraints nullable="false"/>
|
|
||||||
</column>
|
|
||||||
<column name="raft_log_index" type="BIGINT"/>
|
|
||||||
<column name="consuming_transaction_id" type="NVARCHAR(64)"/>
|
|
||||||
</createTable>
|
|
||||||
</changeSet>
|
|
||||||
<changeSet author="R3.Corda" id="1521131680317-17">
|
<changeSet author="R3.Corda" id="1521131680317-17">
|
||||||
<createTable tableName="node_notary_request_log">
|
<createTable tableName="node_notary_request_log">
|
||||||
<column name="id" type="INT">
|
<column name="id" type="INT">
|
||||||
@ -58,18 +46,11 @@
|
|||||||
</column>
|
</column>
|
||||||
</createTable>
|
</createTable>
|
||||||
</changeSet>
|
</changeSet>
|
||||||
<changeSet author="R3.Corda" id="1511451595465-31">
|
|
||||||
<addPrimaryKey columnNames="output_index, transaction_id" constraintName="node_bft_states_pkey"
|
|
||||||
tableName="node_bft_committed_states"/>
|
|
||||||
</changeSet>
|
|
||||||
<changeSet author="R3.Corda" id="1511451595465-41">
|
<changeSet author="R3.Corda" id="1511451595465-41">
|
||||||
<addPrimaryKey columnNames="output_index, transaction_id" constraintName="node_notary_states_pkey"
|
<addPrimaryKey columnNames="output_index, transaction_id" constraintName="node_notary_states_pkey"
|
||||||
tableName="node_notary_committed_states"/>
|
tableName="node_notary_committed_states"/>
|
||||||
</changeSet>
|
</changeSet>
|
||||||
<changeSet author="R3.Corda" id="1511451595465-43">
|
|
||||||
<addPrimaryKey columnNames="output_index, transaction_id" constraintName="node_raft_state_pkey"
|
|
||||||
tableName="node_raft_committed_states"/>
|
|
||||||
</changeSet>
|
|
||||||
<changeSet author="R3.Corda" id="1521131680317-48">
|
<changeSet author="R3.Corda" id="1521131680317-48">
|
||||||
<addPrimaryKey columnNames="id" constraintName="node_notary_request_log_pkey"
|
<addPrimaryKey columnNames="id" constraintName="node_notary_request_log_pkey"
|
||||||
tableName="node_notary_request_log"/>
|
tableName="node_notary_request_log"/>
|
||||||
|
@ -13,9 +13,4 @@
|
|||||||
<addPrimaryKey tableName="node_bft_committed_states" columnNames="output_index, transaction_id"
|
<addPrimaryKey tableName="node_bft_committed_states" columnNames="output_index, transaction_id"
|
||||||
constraintName="node_bft_states_pkey" clustered="false"/>
|
constraintName="node_bft_states_pkey" clustered="false"/>
|
||||||
</changeSet>
|
</changeSet>
|
||||||
<changeSet id="non-clustered_pk-raft_state" author="R3.Corda" onValidationFail="MARK_RAN">
|
|
||||||
<dropPrimaryKey tableName="node_raft_committed_states" constraintName="node_raft_state_pkey"/>
|
|
||||||
<addPrimaryKey tableName="node_raft_committed_states" columnNames="output_index, transaction_id"
|
|
||||||
constraintName="node_raft_state_pkey" clustered="false"/>
|
|
||||||
</changeSet>
|
|
||||||
</databaseChangeLog>
|
</databaseChangeLog>
|
@ -7,10 +7,6 @@
|
|||||||
|
|
||||||
<changeSet author="R3.Corda" id="nullability">
|
<changeSet author="R3.Corda" id="nullability">
|
||||||
<addNotNullConstraint tableName="node_bft_committed_states" columnName="consuming_transaction_id" columnDataType="NVARCHAR(64)"/>
|
<addNotNullConstraint tableName="node_bft_committed_states" columnName="consuming_transaction_id" columnDataType="NVARCHAR(64)"/>
|
||||||
|
|
||||||
<addNotNullConstraint tableName="node_notary_committed_states" columnName="consuming_transaction_id" columnDataType="NVARCHAR(64)"/>
|
<addNotNullConstraint tableName="node_notary_committed_states" columnName="consuming_transaction_id" columnDataType="NVARCHAR(64)"/>
|
||||||
|
|
||||||
<addNotNullConstraint tableName="node_raft_committed_states" columnName="raft_log_index" columnDataType="BIGINT"/>
|
|
||||||
<addNotNullConstraint tableName="node_raft_committed_states" columnName="consuming_transaction_id" columnDataType="NVARCHAR(64)"/>
|
|
||||||
</changeSet>
|
</changeSet>
|
||||||
</databaseChangeLog>
|
</databaseChangeLog>
|
@ -47,7 +47,7 @@ class JarScanningCordappLoaderTest {
|
|||||||
fun `classes that aren't in cordapps aren't loaded`() {
|
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
|
// 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(".")))
|
val loader = JarScanningCordappLoader.fromDirectories(listOf(Paths.get(".")))
|
||||||
assertThat(loader.cordapps).containsOnly(loader.coreCordapp)
|
assertThat(loader.cordapps).isEmpty()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -55,9 +55,9 @@ class JarScanningCordappLoaderTest {
|
|||||||
val isolatedJAR = JarScanningCordappLoaderTest::class.java.getResource("isolated.jar")!!
|
val isolatedJAR = JarScanningCordappLoaderTest::class.java.getResource("isolated.jar")!!
|
||||||
val loader = JarScanningCordappLoader.fromJarUrls(listOf(isolatedJAR))
|
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.contractClassNames).isEqualTo(listOf(isolatedContractId))
|
||||||
assertThat(actualCordapp.initiatedFlows.single().name).isEqualTo("net.corda.finance.contracts.isolated.IsolatedDummyFlow\$Acceptor")
|
assertThat(actualCordapp.initiatedFlows.single().name).isEqualTo("net.corda.finance.contracts.isolated.IsolatedDummyFlow\$Acceptor")
|
||||||
assertThat(actualCordapp.rpcFlows).isEmpty()
|
assertThat(actualCordapp.rpcFlows).isEmpty()
|
||||||
@ -73,8 +73,8 @@ class JarScanningCordappLoaderTest {
|
|||||||
val loader = cordappLoaderForPackages(listOf(testScanPackage))
|
val loader = cordappLoaderForPackages(listOf(testScanPackage))
|
||||||
|
|
||||||
val actual = loader.cordapps.toTypedArray()
|
val actual = loader.cordapps.toTypedArray()
|
||||||
// One core cordapp, one cordapp from this source tree. In gradle it will also pick up the node jar.
|
// One cordapp from this source tree. In gradle it will also pick up the node jar.
|
||||||
assertThat(actual.size == 2 || actual.size == 3).isTrue()
|
assertThat(actual.size == 0 || actual.size == 1).isTrue()
|
||||||
|
|
||||||
val actualCordapp = actual.single { !it.initiatedFlows.isEmpty() }
|
val actualCordapp = actual.single { !it.initiatedFlows.isEmpty() }
|
||||||
assertThat(actualCordapp.initiatedFlows).first().hasSameClassAs(DummyFlow::class.java)
|
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`() {
|
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 jar = JarScanningCordappLoaderTest::class.java.getResource("versions/no-min-or-target-version.jar")!!
|
||||||
val loader = JarScanningCordappLoader.fromJarUrls(listOf(jar), VersionInfo.UNKNOWN)
|
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.targetPlatformVersion).isEqualTo(1)
|
||||||
assertThat(it.info.minimumPlatformVersion).isEqualTo(1)
|
assertThat(it.info.minimumPlatformVersion).isEqualTo(1)
|
||||||
}
|
}
|
||||||
@ -123,8 +123,7 @@ class JarScanningCordappLoaderTest {
|
|||||||
// make sure classloader extracts correct values
|
// make sure classloader extracts correct values
|
||||||
val jar = JarScanningCordappLoaderTest::class.java.getResource("versions/min-2-target-3.jar")!!
|
val jar = JarScanningCordappLoaderTest::class.java.getResource("versions/min-2-target-3.jar")!!
|
||||||
val loader = JarScanningCordappLoader.fromJarUrls(listOf(jar), VersionInfo.UNKNOWN)
|
val loader = JarScanningCordappLoader.fromJarUrls(listOf(jar), VersionInfo.UNKNOWN)
|
||||||
// exclude the core cordapp
|
val cordapp = loader.cordapps.first()
|
||||||
val cordapp = loader.cordapps.single { it.cordappClasses.contains("net.corda.core.internal.cordapp.CordappImpl") }
|
|
||||||
assertThat(cordapp.info.targetPlatformVersion).isEqualTo(3)
|
assertThat(cordapp.info.targetPlatformVersion).isEqualTo(3)
|
||||||
assertThat(cordapp.info.minimumPlatformVersion).isEqualTo(2)
|
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`() {
|
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 jar = JarScanningCordappLoaderTest::class.java.getResource("versions/min-2-no-target.jar")!!
|
||||||
val loader = JarScanningCordappLoader.fromJarUrls(listOf(jar), VersionInfo.UNKNOWN.copy(platformVersion = 1))
|
val loader = JarScanningCordappLoader.fromJarUrls(listOf(jar), VersionInfo.UNKNOWN.copy(platformVersion = 1))
|
||||||
// exclude the core cordapp
|
assertThat(loader.cordapps).hasSize(0)
|
||||||
assertThat(loader.cordapps).hasSize(1)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `cordapp classloader does load apps when their min platform version is less than the platform version`() {
|
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 jar = JarScanningCordappLoaderTest::class.java.getResource("versions/min-2-target-3.jar")!!
|
||||||
val loader = JarScanningCordappLoader.fromJarUrls(listOf(jar), VersionInfo.UNKNOWN.copy(platformVersion = 1000))
|
val loader = JarScanningCordappLoader.fromJarUrls(listOf(jar), VersionInfo.UNKNOWN.copy(platformVersion = 1000))
|
||||||
// exclude the core cordapp
|
assertThat(loader.cordapps).hasSize(1)
|
||||||
assertThat(loader.cordapps).hasSize(2)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `cordapp classloader does load apps when their min platform version is equal to the platform version`() {
|
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 jar = JarScanningCordappLoaderTest::class.java.getResource("versions/min-2-target-3.jar")!!
|
||||||
val loader = JarScanningCordappLoader.fromJarUrls(listOf(jar), VersionInfo.UNKNOWN.copy(platformVersion = 2))
|
val loader = JarScanningCordappLoader.fromJarUrls(listOf(jar), VersionInfo.UNKNOWN.copy(platformVersion = 2))
|
||||||
// exclude the core cordapp
|
assertThat(loader.cordapps).hasSize(1)
|
||||||
assertThat(loader.cordapps).hasSize(2)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun cordappLoaderForPackages(packages: Iterable<String>): CordappLoader {
|
private fun cordappLoaderForPackages(packages: Iterable<String>): CordappLoader {
|
||||||
|
@ -313,20 +313,11 @@ class TwoPartyTradeFlowTests(private val anonymous: Boolean) {
|
|||||||
// of gets and puts.
|
// of gets and puts.
|
||||||
private fun makeNodeWithTracking(name: CordaX500Name): TestStartedNode {
|
private fun makeNodeWithTracking(name: CordaX500Name): TestStartedNode {
|
||||||
// Create a node in the mock network ...
|
// Create a node in the mock network ...
|
||||||
return mockNet.createNode(InternalMockNodeParameters(legalName = name), nodeFactory = { args, cordappLoader ->
|
return mockNet.createNode(InternalMockNodeParameters(legalName = name), nodeFactory = { args ->
|
||||||
if (cordappLoader != null) {
|
object : InternalMockNetwork.MockNode(args) {
|
||||||
object : InternalMockNetwork.MockNode(args, cordappLoader) {
|
// That constructs a recording tx storage
|
||||||
// That constructs a recording tx storage
|
override fun makeTransactionStorage(transactionCacheSizeBytes: Long): WritableTransactionStorage {
|
||||||
override fun makeTransactionStorage(transactionCacheSizeBytes: Long): WritableTransactionStorage {
|
return RecordingTransactionStorage(database, super.makeTransactionStorage(transactionCacheSizeBytes))
|
||||||
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))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -611,7 +602,7 @@ class TwoPartyTradeFlowTests(private val anonymous: Boolean) {
|
|||||||
fillUpForBuyer(bobError, issuer, bob, notary).second
|
fillUpForBuyer(bobError, issuer, bob, notary).second
|
||||||
}
|
}
|
||||||
val alicesFakePaper = aliceNode.database.transaction {
|
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)
|
insertFakeTransactions(bobsBadCash, bobNode, bob, notaryNode, bankNode)
|
||||||
|
@ -22,6 +22,7 @@ import net.corda.core.transactions.SignedTransaction
|
|||||||
import net.corda.core.transactions.TransactionBuilder
|
import net.corda.core.transactions.TransactionBuilder
|
||||||
import net.corda.core.utilities.ProgressTracker
|
import net.corda.core.utilities.ProgressTracker
|
||||||
import net.corda.core.utilities.seconds
|
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.FlowTimeoutConfiguration
|
||||||
import net.corda.node.services.config.NodeConfiguration
|
import net.corda.node.services.config.NodeConfiguration
|
||||||
import net.corda.node.services.config.NotaryConfig
|
import net.corda.node.services.config.NotaryConfig
|
||||||
@ -60,12 +61,13 @@ class TimedFlowTestRule(val clusterSize: Int) : ExternalResource() {
|
|||||||
replicaIds.map { mockNet.baseDirectory(mockNet.nextNodeId + it) },
|
replicaIds.map { mockNet.baseDirectory(mockNet.nextNodeId + it) },
|
||||||
CordaX500Name("Custom Notary", "Zurich", "CH"))
|
CordaX500Name("Custom Notary", "Zurich", "CH"))
|
||||||
|
|
||||||
val networkParameters = NetworkParametersCopier(testNetworkParameters(listOf(NotaryInfo(notaryIdentity, true))))
|
val networkParameters = NetworkParametersCopier(testNetworkParameters(listOf(NotaryInfo(notaryIdentity, true))))
|
||||||
val notaryConfig = mock<NotaryConfig> {
|
val notaryConfig = mock<NotaryConfig> {
|
||||||
whenever(it.custom).thenReturn(true)
|
whenever(it.custom).thenReturn(true)
|
||||||
whenever(it.isClusterConfig).thenReturn(true)
|
whenever(it.isClusterConfig).thenReturn(true)
|
||||||
whenever(it.validating).thenReturn(true)
|
whenever(it.validating).thenReturn(true)
|
||||||
}
|
whenever(it.className).thenReturn(TimedFlowTests.TestNotaryService::class.java.name)
|
||||||
|
}
|
||||||
|
|
||||||
val notaryNodes = (0 until clusterSize).map {
|
val notaryNodes = (0 until clusterSize).map {
|
||||||
mockNet.createUnstartedNode(InternalMockNodeParameters(configOverrides = {
|
mockNet.createUnstartedNode(InternalMockNodeParameters(configOverrides = {
|
||||||
@ -191,8 +193,7 @@ class TimedFlowTests {
|
|||||||
}.bufferUntilSubscribed().toBlocking().toFuture()
|
}.bufferUntilSubscribed().toBlocking().toFuture()
|
||||||
}
|
}
|
||||||
|
|
||||||
@CordaService
|
class TestNotaryService(override val services: ServiceHubInternal, override val notaryIdentityKey: PublicKey) : TrustedAuthorityNotaryService() {
|
||||||
private class TestNotaryService(override val services: AppServiceHub, override val notaryIdentityKey: PublicKey) : TrustedAuthorityNotaryService() {
|
|
||||||
override val uniquenessProvider = mock<UniquenessProvider>()
|
override val uniquenessProvider = mock<UniquenessProvider>()
|
||||||
override fun createServiceFlow(otherPartySession: FlowSession): FlowLogic<Void?> = TestNotaryFlow(otherPartySession, this)
|
override fun createServiceFlow(otherPartySession: FlowSession): FlowLogic<Void?> = TestNotaryFlow(otherPartySession, this)
|
||||||
override fun start() {}
|
override fun start() {}
|
||||||
|
@ -8,7 +8,7 @@ import net.corda.core.identity.Party
|
|||||||
import net.corda.core.identity.PartyAndCertificate
|
import net.corda.core.identity.PartyAndCertificate
|
||||||
import net.corda.core.node.services.UnknownAnonymousPartyException
|
import net.corda.core.node.services.UnknownAnonymousPartyException
|
||||||
import net.corda.node.internal.configureDatabase
|
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.CertificateType
|
||||||
import net.corda.nodeapi.internal.crypto.X509Utilities
|
import net.corda.nodeapi.internal.crypto.X509Utilities
|
||||||
import net.corda.nodeapi.internal.crypto.x509Certificates
|
import net.corda.nodeapi.internal.crypto.x509Certificates
|
||||||
|
@ -5,7 +5,7 @@ import net.corda.core.utilities.loggerFor
|
|||||||
import net.corda.node.internal.configureDatabase
|
import net.corda.node.internal.configureDatabase
|
||||||
import net.corda.node.services.schema.NodeSchemaService
|
import net.corda.node.services.schema.NodeSchemaService
|
||||||
import net.corda.node.utilities.AppendOnlyPersistentMap
|
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.nodeapi.internal.persistence.DatabaseConfig
|
||||||
import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties
|
import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties
|
||||||
import org.junit.After
|
import org.junit.After
|
||||||
|
@ -9,7 +9,7 @@ import net.corda.core.toFuture
|
|||||||
import net.corda.core.transactions.SignedTransaction
|
import net.corda.core.transactions.SignedTransaction
|
||||||
import net.corda.node.internal.configureDatabase
|
import net.corda.node.internal.configureDatabase
|
||||||
import net.corda.node.services.transactions.PersistentUniquenessProvider
|
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.CordaPersistence
|
||||||
import net.corda.nodeapi.internal.persistence.DatabaseConfig
|
import net.corda.nodeapi.internal.persistence.DatabaseConfig
|
||||||
import net.corda.testing.core.*
|
import net.corda.testing.core.*
|
||||||
@ -154,7 +154,8 @@ class DBTransactionStorageTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun newTransactionStorage(cacheSizeBytesOverride: Long? = null) {
|
private fun newTransactionStorage(cacheSizeBytesOverride: Long? = null) {
|
||||||
transactionStorage = DBTransactionStorage(database, TestingNamedCacheFactory(cacheSizeBytesOverride ?: 1024))
|
transactionStorage = DBTransactionStorage(database, TestingNamedCacheFactory(cacheSizeBytesOverride
|
||||||
|
?: 1024))
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun assertTransactionIsRetrievable(transaction: SignedTransaction) {
|
private fun assertTransactionIsRetrievable(transaction: SignedTransaction) {
|
||||||
|
@ -9,7 +9,7 @@ import net.corda.finance.contracts.asset.Cash
|
|||||||
import net.corda.finance.flows.CashIssueFlow
|
import net.corda.finance.flows.CashIssueFlow
|
||||||
import net.corda.node.services.identity.PersistentIdentityService
|
import net.corda.node.services.identity.PersistentIdentityService
|
||||||
import net.corda.node.services.keys.E2ETestKeyManagementService
|
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.core.BOC_NAME
|
||||||
import net.corda.testing.node.InMemoryMessagingNetwork
|
import net.corda.testing.node.InMemoryMessagingNetwork
|
||||||
import net.corda.testing.node.MockNetwork
|
import net.corda.testing.node.MockNetwork
|
||||||
|
@ -37,11 +37,11 @@ import net.corda.node.services.schema.NodeSchemaService
|
|||||||
import net.corda.node.services.schema.PersistentStateService
|
import net.corda.node.services.schema.PersistentStateService
|
||||||
import net.corda.node.services.vault.NodeVaultService
|
import net.corda.node.services.vault.NodeVaultService
|
||||||
import net.corda.node.services.vault.VaultSchemaV1
|
import net.corda.node.services.vault.VaultSchemaV1
|
||||||
import net.corda.node.utilities.TestingNamedCacheFactory
|
|
||||||
import net.corda.nodeapi.internal.persistence.CordaPersistence
|
import net.corda.nodeapi.internal.persistence.CordaPersistence
|
||||||
import net.corda.nodeapi.internal.persistence.DatabaseConfig
|
import net.corda.nodeapi.internal.persistence.DatabaseConfig
|
||||||
import net.corda.nodeapi.internal.persistence.HibernateConfiguration
|
import net.corda.nodeapi.internal.persistence.HibernateConfiguration
|
||||||
import net.corda.testing.core.*
|
import net.corda.testing.core.*
|
||||||
|
import net.corda.testing.internal.TestingNamedCacheFactory
|
||||||
import net.corda.testing.internal.rigorousMock
|
import net.corda.testing.internal.rigorousMock
|
||||||
import net.corda.testing.internal.vault.DummyDealStateSchemaV1
|
import net.corda.testing.internal.vault.DummyDealStateSchemaV1
|
||||||
import net.corda.testing.internal.vault.DummyLinearStateSchemaV1
|
import net.corda.testing.internal.vault.DummyLinearStateSchemaV1
|
||||||
|
@ -15,7 +15,7 @@ import net.corda.core.node.services.vault.Sort
|
|||||||
import net.corda.core.utilities.getOrThrow
|
import net.corda.core.utilities.getOrThrow
|
||||||
import net.corda.node.internal.configureDatabase
|
import net.corda.node.internal.configureDatabase
|
||||||
import net.corda.node.services.transactions.PersistentUniquenessProvider
|
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.CordaPersistence
|
||||||
import net.corda.nodeapi.internal.persistence.DatabaseConfig
|
import net.corda.nodeapi.internal.persistence.DatabaseConfig
|
||||||
import net.corda.testing.internal.LogHelper
|
import net.corda.testing.internal.LogHelper
|
||||||
|
@ -10,20 +10,18 @@ import net.corda.core.schemas.PersistentState
|
|||||||
import net.corda.core.utilities.getOrThrow
|
import net.corda.core.utilities.getOrThrow
|
||||||
import net.corda.node.services.api.ServiceHubInternal
|
import net.corda.node.services.api.ServiceHubInternal
|
||||||
import net.corda.node.services.schema.NodeSchemaService.NodeCoreV1
|
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.DriverParameters
|
||||||
import net.corda.testing.driver.driver
|
import net.corda.testing.driver.driver
|
||||||
import net.corda.testing.driver.internal.InProcessImpl
|
import net.corda.testing.driver.internal.InProcessImpl
|
||||||
import net.corda.testing.internal.vault.DummyLinearStateSchemaV1
|
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.InternalMockNetwork
|
||||||
|
import net.corda.testing.node.internal.cordappsForPackages
|
||||||
import org.hibernate.annotations.Cascade
|
import org.hibernate.annotations.Cascade
|
||||||
import org.hibernate.annotations.CascadeType
|
import org.hibernate.annotations.CascadeType
|
||||||
import org.junit.Ignore
|
import org.junit.Ignore
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import javax.persistence.*
|
import javax.persistence.*
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
import kotlin.test.assertFalse
|
|
||||||
import kotlin.test.assertTrue
|
import kotlin.test.assertTrue
|
||||||
|
|
||||||
class NodeSchemaServiceTest {
|
class NodeSchemaServiceTest {
|
||||||
@ -47,7 +45,6 @@ class NodeSchemaServiceTest {
|
|||||||
|
|
||||||
// check against NodeCore schemas
|
// check against NodeCore schemas
|
||||||
assertTrue(schemaService.schemaOptions.containsKey(NodeCoreV1))
|
assertTrue(schemaService.schemaOptions.containsKey(NodeCoreV1))
|
||||||
assertFalse(schemaService.schemaOptions.containsKey(NodeNotaryV1))
|
|
||||||
mockNet.stopNodes()
|
mockNet.stopNodes()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -57,9 +54,8 @@ class NodeSchemaServiceTest {
|
|||||||
val mockNotaryNode = mockNet.notaryNodes.first()
|
val mockNotaryNode = mockNet.notaryNodes.first()
|
||||||
val schemaService = mockNotaryNode.services.schemaService
|
val schemaService = mockNotaryNode.services.schemaService
|
||||||
|
|
||||||
// check against NodeCore + NodeNotary Schemas
|
// check against NodeCore Schema
|
||||||
assertTrue(schemaService.schemaOptions.containsKey(NodeCoreV1))
|
assertTrue(schemaService.schemaOptions.containsKey(NodeCoreV1))
|
||||||
assertTrue(schemaService.schemaOptions.containsKey(NodeNotaryV1))
|
|
||||||
mockNet.stopNodes()
|
mockNet.stopNodes()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -97,7 +93,6 @@ class NodeSchemaServiceTest {
|
|||||||
val mappedSchemas = result.returnValue.getOrThrow()
|
val mappedSchemas = result.returnValue.getOrThrow()
|
||||||
// check against NodeCore schemas
|
// check against NodeCore schemas
|
||||||
assertTrue(mappedSchemas.contains(NodeCoreV1.name))
|
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)) {
|
driver(DriverParameters(startNodesInProcess = true)) {
|
||||||
val notary = defaultNotaryNode.getOrThrow()
|
val notary = defaultNotaryNode.getOrThrow()
|
||||||
val mappedSchemas = notary.rpc.startFlow(::MappedSchemasFlow).returnValue.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(NodeCoreV1.name))
|
||||||
assertTrue(mappedSchemas.contains(NodeNotaryV1.name))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -37,7 +37,7 @@ class MaxTransactionSizeTests {
|
|||||||
|
|
||||||
@Before
|
@Before
|
||||||
fun setup() {
|
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))
|
aliceNode = mockNet.createNode(MockNodeParameters(legalName = ALICE_NAME))
|
||||||
bobNode = mockNet.createNode(MockNodeParameters(legalName = BOB_NAME))
|
bobNode = mockNet.createNode(MockNodeParameters(legalName = BOB_NAME))
|
||||||
notaryNode = mockNet.defaultNotaryNode
|
notaryNode = mockNet.defaultNotaryNode
|
||||||
|
@ -10,7 +10,7 @@ import net.corda.core.identity.CordaX500Name
|
|||||||
import net.corda.core.internal.notary.NotaryInternalException
|
import net.corda.core.internal.notary.NotaryInternalException
|
||||||
import net.corda.node.internal.configureDatabase
|
import net.corda.node.internal.configureDatabase
|
||||||
import net.corda.node.services.schema.NodeSchemaService
|
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.CordaPersistence
|
||||||
import net.corda.nodeapi.internal.persistence.DatabaseConfig
|
import net.corda.nodeapi.internal.persistence.DatabaseConfig
|
||||||
import net.corda.testing.core.SerializationEnvironmentRule
|
import net.corda.testing.core.SerializationEnvironmentRule
|
||||||
@ -39,7 +39,7 @@ class PersistentUniquenessProviderTests {
|
|||||||
@Before
|
@Before
|
||||||
fun setUp() {
|
fun setUp() {
|
||||||
LogHelper.setLevel(PersistentUniquenessProvider::class)
|
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
|
@After
|
||||||
|
@ -82,7 +82,7 @@ class VaultSoftLockManagerTest {
|
|||||||
private val mockVault = rigorousMock<VaultServiceInternal>().also {
|
private val mockVault = rigorousMock<VaultServiceInternal>().also {
|
||||||
doNothing().whenever(it).softLockRelease(any(), anyOrNull())
|
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) {
|
object : InternalMockNetwork.MockNode(args) {
|
||||||
override fun makeVaultService(keyManagementService: KeyManagementService, services: ServicesForResolution, database: CordaPersistence): VaultServiceInternal {
|
override fun makeVaultService(keyManagementService: KeyManagementService, services: ServicesForResolution, database: CordaPersistence): VaultServiceInternal {
|
||||||
val node = this
|
val node = this
|
||||||
|
29
notary/mysql/build.gradle
Normal file
29
notary/mysql/build.gradle
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
apply plugin: 'kotlin'
|
||||||
|
apply plugin: 'idea'
|
||||||
|
apply plugin: 'net.corda.plugins.cordapp'
|
||||||
|
apply plugin: 'net.corda.plugins.publish-utils'
|
||||||
|
apply plugin: 'net.corda.plugins.quasar-utils'
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
cordaCompile "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
|
||||||
|
|
||||||
|
// Corda integration dependencies
|
||||||
|
cordaCompile project(':node')
|
||||||
|
|
||||||
|
// For the MySQLUniquenessProvider
|
||||||
|
compile group: 'mysql', name: 'mysql-connector-java', version: '6.0.6'
|
||||||
|
|
||||||
|
testCompile "junit:junit:$junit_version"
|
||||||
|
testCompile project(':node-driver')
|
||||||
|
}
|
||||||
|
|
||||||
|
idea {
|
||||||
|
module {
|
||||||
|
downloadJavadoc = true // defaults to false
|
||||||
|
downloadSources = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
publish {
|
||||||
|
name 'corda-notary-mysql'
|
||||||
|
}
|
@ -0,0 +1,46 @@
|
|||||||
|
package net.corda.notary.mysql
|
||||||
|
|
||||||
|
import net.corda.core.flows.FlowSession
|
||||||
|
import net.corda.core.internal.notary.AsyncCFTNotaryService
|
||||||
|
import net.corda.core.internal.notary.NotaryServiceFlow
|
||||||
|
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
|
||||||
|
|
||||||
|
/** Notary service backed by a replicated MySQL database. */
|
||||||
|
class MySQLNotaryService(
|
||||||
|
override val services: ServiceHubInternal,
|
||||||
|
override val notaryIdentityKey: PublicKey) : AsyncCFTNotaryService() {
|
||||||
|
|
||||||
|
/** Database table will be automatically created in dev mode */
|
||||||
|
private val devMode = services.configuration.devMode
|
||||||
|
|
||||||
|
private val notaryConfig = services.configuration.notary
|
||||||
|
?: throw IllegalArgumentException("Failed to register ${this::class.java}: notary configuration not present")
|
||||||
|
|
||||||
|
override val asyncUniquenessProvider = with(services) {
|
||||||
|
val mysqlConfig = notaryConfig.mysql
|
||||||
|
?: throw IllegalArgumentException("Failed to register ${this::class.java}: raft configuration not present")
|
||||||
|
MySQLUniquenessProvider(
|
||||||
|
services.monitoringService.metrics,
|
||||||
|
services.clock,
|
||||||
|
mysqlConfig
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun createServiceFlow(otherPartySession: FlowSession): NotaryServiceFlow {
|
||||||
|
return if (notaryConfig.validating) {
|
||||||
|
ValidatingNotaryFlow(otherPartySession, this)
|
||||||
|
} else NonValidatingNotaryFlow(otherPartySession, this)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
override fun start() {
|
||||||
|
if (devMode) asyncUniquenessProvider.createTable()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun stop() {
|
||||||
|
asyncUniquenessProvider.stop()
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package net.corda.node.services.transactions
|
package net.corda.notary.mysql
|
||||||
|
|
||||||
import com.codahale.metrics.Gauge
|
import com.codahale.metrics.Gauge
|
||||||
import com.codahale.metrics.MetricRegistry
|
import com.codahale.metrics.MetricRegistry
|
@ -1,4 +1,4 @@
|
|||||||
package net.corda.node.services
|
package net.corda.notary.mysql
|
||||||
|
|
||||||
import co.paralleluniverse.fibers.Suspendable
|
import co.paralleluniverse.fibers.Suspendable
|
||||||
import com.nhaarman.mockito_kotlin.doReturn
|
import com.nhaarman.mockito_kotlin.doReturn
|
||||||
@ -24,7 +24,6 @@ import net.corda.core.utilities.getOrThrow
|
|||||||
import net.corda.core.utilities.seconds
|
import net.corda.core.utilities.seconds
|
||||||
import net.corda.node.services.config.MySQLConfiguration
|
import net.corda.node.services.config.MySQLConfiguration
|
||||||
import net.corda.node.services.config.NotaryConfig
|
import net.corda.node.services.config.NotaryConfig
|
||||||
import net.corda.node.services.transactions.MySQLNotaryService
|
|
||||||
import net.corda.nodeapi.internal.DevIdentityGenerator
|
import net.corda.nodeapi.internal.DevIdentityGenerator
|
||||||
import net.corda.nodeapi.internal.network.NetworkParametersCopier
|
import net.corda.nodeapi.internal.network.NetworkParametersCopier
|
||||||
import net.corda.testing.common.internal.testNetworkParameters
|
import net.corda.testing.common.internal.testNetworkParameters
|
||||||
@ -64,9 +63,9 @@ class MySQLNotaryServiceTests : IntegrationTest() {
|
|||||||
|
|
||||||
@Before
|
@Before
|
||||||
fun before() {
|
fun before() {
|
||||||
mockNet = InternalMockNetwork(cordappsForAllNodes = cordappsForPackages("net.corda.testing.contracts"), threadPerNode = true)
|
mockNet = InternalMockNetwork(cordappsForAllNodes = cordappsForPackages("net.corda.testing.contracts", "net.corda.notary.mysql"), threadPerNode = true)
|
||||||
notaryParty = DevIdentityGenerator.generateDistributedNotarySingularIdentity(listOf(mockNet.baseDirectory(mockNet.nextNodeId)), notaryName)
|
notaryParty = DevIdentityGenerator.generateDistributedNotarySingularIdentity(listOf(mockNet.baseDirectory(mockNet.nextNodeId)), notaryName)
|
||||||
val networkParameters = NetworkParametersCopier(testNetworkParameters(listOf(NotaryInfo(notaryParty, false))))
|
val networkParameters = NetworkParametersCopier(testNetworkParameters(listOf(NotaryInfo(notaryParty, true))))
|
||||||
val notaryNodeUnstarted = createNotaryNode()
|
val notaryNodeUnstarted = createNotaryNode()
|
||||||
val nodeUnstarted = mockNet.createUnstartedNode()
|
val nodeUnstarted = mockNet.createUnstartedNode()
|
||||||
val startedNodes = listOf(notaryNodeUnstarted, nodeUnstarted).map { n ->
|
val startedNodes = listOf(notaryNodeUnstarted, nodeUnstarted).map { n ->
|
||||||
@ -256,8 +255,9 @@ class MySQLNotaryServiceTests : IntegrationTest() {
|
|||||||
entropyRoot = BigInteger.valueOf(60L),
|
entropyRoot = BigInteger.valueOf(60L),
|
||||||
configOverrides = {
|
configOverrides = {
|
||||||
val notaryConfig = NotaryConfig(
|
val notaryConfig = NotaryConfig(
|
||||||
validating = false,
|
validating = true,
|
||||||
mysql = MySQLConfiguration(dataStoreProperties, maxBatchSize = 10, maxBatchInputStates = 100)
|
mysql = MySQLConfiguration(dataStoreProperties, maxBatchSize = 10, maxBatchInputStates = 100),
|
||||||
|
className = MySQLNotaryService::class.java.name
|
||||||
)
|
)
|
||||||
doReturn(notaryConfig).whenever(it).notary
|
doReturn(notaryConfig).whenever(it).notary
|
||||||
}
|
}
|
@ -327,7 +327,7 @@ class TwoPartyTradeFlowTests(private val anonymous: Boolean) {
|
|||||||
private fun makeNodeWithTracking(
|
private fun makeNodeWithTracking(
|
||||||
name: CordaX500Name): TestStartedNode {
|
name: CordaX500Name): TestStartedNode {
|
||||||
// Create a node in the mock network ...
|
// Create a node in the mock network ...
|
||||||
return mockNet.createNode(InternalMockNodeParameters(legalName = name), nodeFactory = { args, _ ->
|
return mockNet.createNode(InternalMockNodeParameters(legalName = name), nodeFactory = { args ->
|
||||||
object : InternalMockNetwork.MockNode(args) {
|
object : InternalMockNetwork.MockNode(args) {
|
||||||
// That constructs a recording tx storage
|
// That constructs a recording tx storage
|
||||||
override fun makeTransactionStorage(transactionCacheSizeBytes: Long): WritableTransactionStorage {
|
override fun makeTransactionStorage(transactionCacheSizeBytes: Long): WritableTransactionStorage {
|
||||||
|
@ -4,10 +4,8 @@ apply plugin: 'java'
|
|||||||
apply plugin: 'kotlin'
|
apply plugin: 'kotlin'
|
||||||
apply plugin: 'idea'
|
apply plugin: 'idea'
|
||||||
apply plugin: 'net.corda.plugins.quasar-utils'
|
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.cordapp'
|
||||||
apply plugin: 'net.corda.plugins.cordformation'
|
apply plugin: 'net.corda.plugins.cordformation'
|
||||||
apply plugin: 'maven-publish'
|
|
||||||
|
|
||||||
configurations {
|
configurations {
|
||||||
integrationTestCompile.extendsFrom testCompile
|
integrationTestCompile.extendsFrom testCompile
|
||||||
@ -16,7 +14,6 @@ configurations {
|
|||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
|
compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
|
||||||
testCompile "junit:junit:$junit_version"
|
|
||||||
|
|
||||||
// Corda integration dependencies
|
// Corda integration dependencies
|
||||||
cordaCompile project(path: ":node:capsule", configuration: 'runtimeArtifacts')
|
cordaCompile project(path: ":node:capsule", configuration: 'runtimeArtifacts')
|
||||||
@ -24,26 +21,11 @@ dependencies {
|
|||||||
cordaCompile project(':core')
|
cordaCompile project(':core')
|
||||||
cordaCompile project(':client:jfx')
|
cordaCompile project(':client:jfx')
|
||||||
cordaCompile project(':client:rpc')
|
cordaCompile project(':client:rpc')
|
||||||
cordaCompile project(':node-driver')
|
cordaCompile project(':test-utils')
|
||||||
}
|
|
||||||
|
|
||||||
idea {
|
// Notary implementations
|
||||||
module {
|
cordapp project(':experimental:notary-raft')
|
||||||
downloadJavadoc = true // defaults to false
|
cordapp project(':experimental:notary-bft-smart')
|
||||||
downloadSources = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
publishing {
|
|
||||||
publications {
|
|
||||||
jarAndSources(MavenPublication) {
|
|
||||||
from components.java
|
|
||||||
artifactId 'notarydemo'
|
|
||||||
|
|
||||||
artifact sourceJar
|
|
||||||
artifact javadocJar
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
task deployNodes(dependsOn: ['deployNodesSingle', 'deployNodesRaft', 'deployNodesBFT', 'deployNodesCustom'])
|
task deployNodes(dependsOn: ['deployNodesSingle', 'deployNodesRaft', 'deployNodesBFT', 'deployNodesCustom'])
|
||||||
@ -99,9 +81,11 @@ task deployNodesCustom(type: Cordform, dependsOn: 'jar') {
|
|||||||
}
|
}
|
||||||
|
|
||||||
task deployNodesRaft(type: Cordform, dependsOn: 'jar') {
|
task deployNodesRaft(type: Cordform, dependsOn: 'jar') {
|
||||||
|
def className = "net.corda.notary.raft.RaftNotaryService"
|
||||||
directory file("$buildDir/nodes/nodesRaft")
|
directory file("$buildDir/nodes/nodesRaft")
|
||||||
nodeDefaults {
|
nodeDefaults {
|
||||||
extraConfig = [h2Settings: [address: "localhost:0"]]
|
extraConfig = [h2Settings: [address: "localhost:0"]]
|
||||||
|
cordapp project(':experimental:notary-raft')
|
||||||
}
|
}
|
||||||
node {
|
node {
|
||||||
name "O=Alice Corp,L=Madrid,C=ES"
|
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",
|
serviceLegalName: "O=Raft,L=Zurich,C=CH",
|
||||||
raft: [
|
raft: [
|
||||||
nodeAddress: "localhost:10008"
|
nodeAddress: "localhost:10008"
|
||||||
]
|
],
|
||||||
|
className: className
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
node {
|
node {
|
||||||
@ -140,7 +125,8 @@ task deployNodesRaft(type: Cordform, dependsOn: 'jar') {
|
|||||||
raft: [
|
raft: [
|
||||||
nodeAddress: "localhost:10012",
|
nodeAddress: "localhost:10012",
|
||||||
clusterAddresses: ["localhost:10008"]
|
clusterAddresses: ["localhost:10008"]
|
||||||
]
|
],
|
||||||
|
className: className
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
node {
|
node {
|
||||||
@ -156,16 +142,19 @@ task deployNodesRaft(type: Cordform, dependsOn: 'jar') {
|
|||||||
raft: [
|
raft: [
|
||||||
nodeAddress: "localhost:10016",
|
nodeAddress: "localhost:10016",
|
||||||
clusterAddresses: ["localhost:10008"]
|
clusterAddresses: ["localhost:10008"]
|
||||||
]
|
],
|
||||||
|
className: className
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
task deployNodesBFT(type: Cordform, dependsOn: 'jar') {
|
task deployNodesBFT(type: Cordform, dependsOn: 'jar') {
|
||||||
def clusterAddresses = ["localhost:11000", "localhost:11010", "localhost:11020", "localhost:11030"]
|
def clusterAddresses = ["localhost:11000", "localhost:11010", "localhost:11020", "localhost:11030"]
|
||||||
|
def className = "net.corda.notary.bftsmart.BftSmartNotaryService"
|
||||||
directory file("$buildDir/nodes/nodesBFT")
|
directory file("$buildDir/nodes/nodesBFT")
|
||||||
nodeDefaults {
|
nodeDefaults {
|
||||||
extraConfig = [h2Settings: [address: "localhost:0"]]
|
extraConfig = [h2Settings: [address: "localhost:0"]]
|
||||||
|
cordapp project(':experimental:notary-bft-smart')
|
||||||
}
|
}
|
||||||
node {
|
node {
|
||||||
name "O=Alice Corp,L=Madrid,C=ES"
|
name "O=Alice Corp,L=Madrid,C=ES"
|
||||||
@ -189,7 +178,8 @@ task deployNodesBFT(type: Cordform, dependsOn: 'jar') {
|
|||||||
bftSMaRt: [
|
bftSMaRt: [
|
||||||
replicaId: 0,
|
replicaId: 0,
|
||||||
clusterAddresses: clusterAddresses
|
clusterAddresses: clusterAddresses
|
||||||
]
|
],
|
||||||
|
className: className
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
node {
|
node {
|
||||||
@ -203,9 +193,10 @@ task deployNodesBFT(type: Cordform, dependsOn: 'jar') {
|
|||||||
validating: false,
|
validating: false,
|
||||||
serviceLegalName: "O=BFT,L=Zurich,C=CH",
|
serviceLegalName: "O=BFT,L=Zurich,C=CH",
|
||||||
bftSMaRt: [
|
bftSMaRt: [
|
||||||
replicaId: 0,
|
replicaId: 1,
|
||||||
clusterAddresses: clusterAddresses
|
clusterAddresses: clusterAddresses
|
||||||
]
|
],
|
||||||
|
className: className
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
node {
|
node {
|
||||||
@ -219,9 +210,10 @@ task deployNodesBFT(type: Cordform, dependsOn: 'jar') {
|
|||||||
validating: false,
|
validating: false,
|
||||||
serviceLegalName: "O=BFT,L=Zurich,C=CH",
|
serviceLegalName: "O=BFT,L=Zurich,C=CH",
|
||||||
bftSMaRt: [
|
bftSMaRt: [
|
||||||
replicaId: 0,
|
replicaId: 2,
|
||||||
clusterAddresses: clusterAddresses
|
clusterAddresses: clusterAddresses
|
||||||
]
|
],
|
||||||
|
className: className
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
node {
|
node {
|
||||||
@ -235,9 +227,10 @@ task deployNodesBFT(type: Cordform, dependsOn: 'jar') {
|
|||||||
validating: false,
|
validating: false,
|
||||||
serviceLegalName: "O=BFT,L=Zurich,C=CH",
|
serviceLegalName: "O=BFT,L=Zurich,C=CH",
|
||||||
bftSMaRt: [
|
bftSMaRt: [
|
||||||
replicaId: 0,
|
replicaId: 3,
|
||||||
clusterAddresses: clusterAddresses
|
clusterAddresses: clusterAddresses
|
||||||
]
|
],
|
||||||
|
className: className
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -30,6 +30,8 @@ include 'experimental:flow-worker'
|
|||||||
include 'experimental:ha-testing'
|
include 'experimental:ha-testing'
|
||||||
include 'experimental:corda-utils'
|
include 'experimental:corda-utils'
|
||||||
include 'experimental:rpc-worker'
|
include 'experimental:rpc-worker'
|
||||||
|
include 'experimental:notary-raft'
|
||||||
|
include 'experimental:notary-bft-smart'
|
||||||
include 'jdk8u-deterministic'
|
include 'jdk8u-deterministic'
|
||||||
include 'test-common'
|
include 'test-common'
|
||||||
include 'test-cli'
|
include 'test-cli'
|
||||||
@ -79,6 +81,7 @@ include 'node:dist'
|
|||||||
include 'tools:notary-healthcheck:contract'
|
include 'tools:notary-healthcheck:contract'
|
||||||
include 'tools:notary-healthcheck:cordapp'
|
include 'tools:notary-healthcheck:cordapp'
|
||||||
include 'tools:notary-healthcheck:client'
|
include 'tools:notary-healthcheck:client'
|
||||||
|
include 'notary:mysql'
|
||||||
|
|
||||||
apply from: 'buildCacheSettings.gradle'
|
apply from: 'buildCacheSettings.gradle'
|
||||||
|
|
||||||
|
@ -25,6 +25,8 @@ sourceSets {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
// Bundling in the Raft notary service for tests involving distributed notaries
|
||||||
|
compile project(':experimental:notary-raft')
|
||||||
compile project(':test-utils')
|
compile project(':test-utils')
|
||||||
|
|
||||||
// Integration test helpers
|
// Integration test helpers
|
||||||
|
@ -497,6 +497,7 @@ class DriverDSLImpl(
|
|||||||
val config = NotaryConfig(
|
val config = NotaryConfig(
|
||||||
validating = spec.validating,
|
validating = spec.validating,
|
||||||
serviceLegalName = spec.name,
|
serviceLegalName = spec.name,
|
||||||
|
className = "net.corda.notary.raft.RaftNotaryService",
|
||||||
raft = RaftConfig(nodeAddress = nodeAddress, clusterAddresses = clusterAddresses))
|
raft = RaftConfig(nodeAddress = nodeAddress, clusterAddresses = clusterAddresses))
|
||||||
return config.toConfigMap()
|
return config.toConfigMap()
|
||||||
}
|
}
|
||||||
|
@ -28,10 +28,8 @@ import net.corda.core.utilities.contextLogger
|
|||||||
import net.corda.core.utilities.hours
|
import net.corda.core.utilities.hours
|
||||||
import net.corda.core.utilities.seconds
|
import net.corda.core.utilities.seconds
|
||||||
import net.corda.node.VersionInfo
|
import net.corda.node.VersionInfo
|
||||||
import net.corda.node.cordapp.CordappLoader
|
|
||||||
import net.corda.node.internal.AbstractNode
|
import net.corda.node.internal.AbstractNode
|
||||||
import net.corda.node.internal.InitiatedFlowFactory
|
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.FlowStarter
|
||||||
import net.corda.node.services.api.ServiceHubInternal
|
import net.corda.node.services.api.ServiceHubInternal
|
||||||
import net.corda.node.services.api.StartedNodeServices
|
import net.corda.node.services.api.StartedNodeServices
|
||||||
@ -43,9 +41,6 @@ import net.corda.node.services.messaging.Message
|
|||||||
import net.corda.node.services.messaging.MessagingService
|
import net.corda.node.services.messaging.MessagingService
|
||||||
import net.corda.node.services.persistence.NodeAttachmentService
|
import net.corda.node.services.persistence.NodeAttachmentService
|
||||||
import net.corda.node.services.statemachine.StateMachineManager
|
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.AffinityExecutor.ServiceAffinityExecutor
|
||||||
import net.corda.node.utilities.DefaultNamedCacheFactory
|
import net.corda.node.utilities.DefaultNamedCacheFactory
|
||||||
import net.corda.nodeapi.internal.DevIdentityGenerator
|
import net.corda.nodeapi.internal.DevIdentityGenerator
|
||||||
@ -69,7 +64,6 @@ import java.math.BigInteger
|
|||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
import java.nio.file.Paths
|
import java.nio.file.Paths
|
||||||
import java.security.KeyPair
|
import java.security.KeyPair
|
||||||
import java.security.PublicKey
|
|
||||||
import java.time.Clock
|
import java.time.Clock
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
import java.util.concurrent.atomic.AtomicInteger
|
import java.util.concurrent.atomic.AtomicInteger
|
||||||
@ -149,7 +143,7 @@ open class InternalMockNetwork(defaultParameters: MockNetworkParameters = MockNe
|
|||||||
val notarySpecs: List<MockNetworkNotarySpec> = defaultParameters.notarySpecs,
|
val notarySpecs: List<MockNetworkNotarySpec> = defaultParameters.notarySpecs,
|
||||||
val testDirectory: Path = Paths.get("build", getTimestampAsDirectoryName()),
|
val testDirectory: Path = Paths.get("build", getTimestampAsDirectoryName()),
|
||||||
val networkParameters: NetworkParameters = testNetworkParameters(),
|
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<TestCorDapp> = emptySet(),
|
val cordappsForAllNodes: Set<TestCorDapp> = emptySet(),
|
||||||
val autoVisibleNodes: Boolean = true) : AutoCloseable {
|
val autoVisibleNodes: Boolean = true) : AutoCloseable {
|
||||||
init {
|
init {
|
||||||
@ -276,12 +270,11 @@ open class InternalMockNetwork(defaultParameters: MockNetworkParameters = MockNe
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
open class MockNode(args: MockNodeArgs, cordappLoader: CordappLoader = JarScanningCordappLoader.fromDirectories(args.config.cordappDirectories, args.version)) : AbstractNode<TestStartedNode>(
|
open class MockNode(args: MockNodeArgs) : AbstractNode<TestStartedNode>(
|
||||||
args.config,
|
args.config,
|
||||||
TestClock(Clock.systemUTC()),
|
TestClock(Clock.systemUTC()),
|
||||||
DefaultNamedCacheFactory(),
|
DefaultNamedCacheFactory(),
|
||||||
args.version,
|
args.version,
|
||||||
cordappLoader,
|
|
||||||
args.network.getServerThread(args.id),
|
args.network.getServerThread(args.id),
|
||||||
args.network.busyLatch
|
args.network.busyLatch
|
||||||
) {
|
) {
|
||||||
@ -424,27 +417,13 @@ open class InternalMockNetwork(defaultParameters: MockNetworkParameters = MockNe
|
|||||||
var acceptableLiveFiberCountOnStop: Int = 0
|
var acceptableLiveFiberCountOnStop: Int = 0
|
||||||
|
|
||||||
override fun acceptableLiveFiberCountOnStop(): Int = acceptableLiveFiberCountOnStop
|
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 {
|
fun createUnstartedNode(parameters: InternalMockNodeParameters = InternalMockNodeParameters()): MockNode {
|
||||||
return createUnstartedNode(parameters, defaultFactory)
|
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)
|
return createNodeImpl(parameters, nodeFactory, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -453,11 +432,11 @@ open class InternalMockNetwork(defaultParameters: MockNetworkParameters = MockNe
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Like the other [createNode] but takes a [nodeFactory] and propagates its [MockNode] subtype. */
|
/** 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)!!
|
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 id = parameters.forcedID ?: nextNodeId++
|
||||||
val baseDirectory = baseDirectory(id)
|
val baseDirectory = baseDirectory(id)
|
||||||
val certificatesDirectory = baseDirectory / "certificates"
|
val certificatesDirectory = baseDirectory / "certificates"
|
||||||
@ -475,7 +454,7 @@ open class InternalMockNetwork(defaultParameters: MockNetworkParameters = MockNe
|
|||||||
val cordappDirectories = sharedCorDappsDirectories + TestCordappDirectories.cached(cordapps)
|
val cordappDirectories = sharedCorDappsDirectories + TestCordappDirectories.cached(cordapps)
|
||||||
doReturn(cordappDirectories).whenever(config).cordappDirectories
|
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
|
_nodes += node
|
||||||
if (start) {
|
if (start) {
|
||||||
node.start()
|
node.start()
|
||||||
@ -483,7 +462,7 @@ open class InternalMockNetwork(defaultParameters: MockNetworkParameters = MockNe
|
|||||||
return node
|
return node
|
||||||
}
|
}
|
||||||
|
|
||||||
fun restartNode(node: TestStartedNode, nodeFactory: (MockNodeArgs, CordappLoader?) -> MockNode): TestStartedNode {
|
fun restartNode(node: TestStartedNode, nodeFactory: (MockNodeArgs) -> MockNode): TestStartedNode {
|
||||||
node.internals.disableDBCloseOnStop()
|
node.internals.disableDBCloseOnStop()
|
||||||
node.dispose()
|
node.dispose()
|
||||||
return createNode(
|
return createNode(
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
package net.corda.node.utilities
|
package net.corda.testing.internal
|
||||||
|
|
||||||
import com.codahale.metrics.MetricRegistry
|
import com.codahale.metrics.MetricRegistry
|
||||||
import com.github.benmanes.caffeine.cache.Cache
|
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.core.serialization.SingletonSerializeAsToken
|
||||||
import net.corda.node.services.config.MB
|
import net.corda.node.services.config.MB
|
||||||
import net.corda.node.services.config.NodeConfiguration
|
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() {
|
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)
|
constructor(sizeOverride: Long = 1024) : this(sizeOverride, null, null)
|
@ -153,7 +153,7 @@ private class DbManagementTool : CordaCliWrapper("database-manager", "The Corda
|
|||||||
val nodeConfig = ConfigHelper.loadConfig(baseDirectory, config).parseAs<NodeConfigurationImpl>(UnknownConfigKeysPolicy.IGNORE::handle)
|
val nodeConfig = ConfigHelper.loadConfig(baseDirectory, config).parseAs<NodeConfigurationImpl>(UnknownConfigKeysPolicy.IGNORE::handle)
|
||||||
val cordappLoader = JarScanningCordappLoader.fromDirectories(setOf(baseDirectory, baseDirectory / "cordapps"))
|
val cordappLoader = JarScanningCordappLoader.fromDirectories(setOf(baseDirectory, baseDirectory / "cordapps"))
|
||||||
|
|
||||||
val schemaService = NodeSchemaService(extraSchemas = cordappLoader.cordappSchemas, includeNotarySchemas = nodeConfig.notary != null)
|
val schemaService = NodeSchemaService(extraSchemas = cordappLoader.cordappSchemas)
|
||||||
|
|
||||||
handleCommand(baseDirectory, config, cmdLineOptions.mode, cordappLoader.appClassLoader, schemaService.schemaOptions.keys)
|
handleCommand(baseDirectory, config, cmdLineOptions.mode, cordappLoader.appClassLoader, schemaService.schemaOptions.keys)
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,7 @@ dependencies {
|
|||||||
cordaCompile project(':core')
|
cordaCompile project(':core')
|
||||||
cordaCompile project(':client:rpc')
|
cordaCompile project(':client:rpc')
|
||||||
cordaCompile project(':node-driver')
|
cordaCompile project(':node-driver')
|
||||||
|
compile project(':notary:mysql')
|
||||||
compile project(':client:mock')
|
compile project(':client:mock')
|
||||||
compile group: 'mysql', name: 'mysql-connector-java', version: '6.0.6'
|
compile group: 'mysql', name: 'mysql-connector-java', version: '6.0.6'
|
||||||
compile group: 'io.dropwizard.metrics', name: 'metrics-graphite', version: '3.2.5'
|
compile group: 'io.dropwizard.metrics', name: 'metrics-graphite', version: '3.2.5'
|
||||||
|
@ -13,9 +13,9 @@ import net.corda.core.node.services.CordaService
|
|||||||
import net.corda.core.utilities.NetworkHostAndPort
|
import net.corda.core.utilities.NetworkHostAndPort
|
||||||
import net.corda.node.services.config.ConfigHelper
|
import net.corda.node.services.config.ConfigHelper
|
||||||
import net.corda.node.services.config.MySQLConfiguration
|
import net.corda.node.services.config.MySQLConfiguration
|
||||||
import net.corda.node.services.transactions.MySQLUniquenessProvider
|
|
||||||
import net.corda.node.services.transactions.NonValidatingNotaryFlow
|
import net.corda.node.services.transactions.NonValidatingNotaryFlow
|
||||||
import net.corda.nodeapi.internal.config.parseAs
|
import net.corda.nodeapi.internal.config.parseAs
|
||||||
|
import net.corda.notary.mysql.MySQLUniquenessProvider
|
||||||
import net.corda.notarytest.flows.AsyncLoadTestFlow
|
import net.corda.notarytest.flows.AsyncLoadTestFlow
|
||||||
import java.net.InetAddress
|
import java.net.InetAddress
|
||||||
import java.net.InetSocketAddress
|
import java.net.InetSocketAddress
|
||||||
|
Loading…
x
Reference in New Issue
Block a user