mirror of
https://github.com/corda/corda.git
synced 2025-03-15 00:36:49 +00:00
Merge remote-tracking branch 'open/master' into andrius/merge-10-10
This commit is contained in:
commit
b7d59785e7
6
.idea/compiler.xml
generated
6
.idea/compiler.xml
generated
@ -218,6 +218,8 @@
|
||||
<module name="node_main" target="1.8" />
|
||||
<module name="node_smokeTest" 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_test" target="1.8" />
|
||||
<module name="notary-healthcheck-client_main" target="1.8" />
|
||||
@ -232,6 +234,8 @@
|
||||
<module name="perftestcordapp_integrationTest" target="1.8" />
|
||||
<module name="perftestcordapp_main" target="1.8" />
|
||||
<module name="perftestcordapp_test" target="1.8" />
|
||||
<module name="notary-raft_main" target="1.8" />
|
||||
<module name="notary-raft_test" target="1.8" />
|
||||
<module name="publish-utils_main" target="1.8" />
|
||||
<module name="publish-utils_test" target="1.8" />
|
||||
<module name="qa-behave_main" target="1.8" />
|
||||
@ -328,4 +332,4 @@
|
||||
<component name="JavacSettings">
|
||||
<option name="ADDITIONAL_OPTIONS_STRING" value="-parameters" />
|
||||
</component>
|
||||
</project>
|
||||
</project>
|
@ -403,7 +403,9 @@ bintrayConfig {
|
||||
'corda-notary-healthcheck-contract',
|
||||
'corda-notary-healthcheck-cordapp',
|
||||
'corda-notary-healthcheck-client',
|
||||
'corda-tools-cliutils'
|
||||
'corda-tools-cliutils',
|
||||
'corda-notary-raft',
|
||||
'corda-notary-bft-smart'
|
||||
]
|
||||
license {
|
||||
name = 'Apache-2.0'
|
||||
|
@ -4,6 +4,7 @@ import net.corda.core.DeleteForDJVM
|
||||
import net.corda.core.DoNotImplement
|
||||
import net.corda.core.crypto.SecureHash
|
||||
import net.corda.core.flows.FlowLogic
|
||||
import net.corda.core.internal.notary.NotaryService
|
||||
import net.corda.core.schemas.MappedSchema
|
||||
import net.corda.core.serialization.SerializationCustomSerializer
|
||||
import net.corda.core.serialization.SerializationWhitelist
|
||||
@ -48,4 +49,5 @@ interface Cordapp {
|
||||
val jarPath: URL
|
||||
val cordappClasses: List<String>
|
||||
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.crypto.SecureHash
|
||||
import net.corda.core.flows.FlowLogic
|
||||
import net.corda.core.internal.notary.NotaryService
|
||||
import net.corda.core.internal.toPath
|
||||
import net.corda.core.schemas.MappedSchema
|
||||
import net.corda.core.serialization.SerializationCustomSerializer
|
||||
@ -25,7 +26,10 @@ data class CordappImpl(
|
||||
override val allFlows: List<Class<out FlowLogic<*>>>,
|
||||
override val jarPath: URL,
|
||||
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)
|
||||
|
||||
companion object {
|
||||
@ -37,7 +41,10 @@ data class CordappImpl(
|
||||
*
|
||||
* 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?
|
||||
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 }
|
||||
}
|
||||
}
|
||||
}
|
@ -128,7 +128,7 @@ class AttachmentTests : WithMockNet {
|
||||
// Makes a node that doesn't do sanity checking at load time.
|
||||
private fun makeBadNode(name: CordaX500Name) = mockNet.createNode(
|
||||
InternalMockNodeParameters(legalName = makeUnique(name)),
|
||||
nodeFactory = { args, _ ->
|
||||
nodeFactory = { args ->
|
||||
object : InternalMockNetwork.MockNode(args) {
|
||||
override fun start() = super.start().apply { attachments.checkAttachmentsOnLoad = false }
|
||||
}
|
||||
|
@ -161,7 +161,7 @@ class AttachmentSerializationTest {
|
||||
}
|
||||
|
||||
private fun rebootClientAndGetAttachmentContent(checkAttachmentsOnLoad: Boolean = true): String {
|
||||
client = mockNet.restartNode(client) { args, _ ->
|
||||
client = mockNet.restartNode(client) { args ->
|
||||
object : InternalMockNetwork.MockNode(args) {
|
||||
override fun start() = super.start().apply { attachments.checkAttachmentsOnLoad = checkAttachmentsOnLoad }
|
||||
}
|
||||
|
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.client.netty.NettyClientServerCommunicationSystemClientSide
|
||||
@ -34,10 +34,11 @@ import net.corda.core.serialization.serialize
|
||||
import net.corda.core.utilities.contextLogger
|
||||
import net.corda.core.utilities.debug
|
||||
import net.corda.node.services.api.ServiceHubInternal
|
||||
import net.corda.node.services.transactions.BFTSMaRt.Client
|
||||
import net.corda.node.services.transactions.BFTSMaRt.Replica
|
||||
import net.corda.node.services.transactions.PersistentUniquenessProvider
|
||||
import net.corda.node.utilities.AppendOnlyPersistentMap
|
||||
import net.corda.nodeapi.internal.persistence.currentDBSession
|
||||
import net.corda.notary.bftsmart.BFTSMaRt.Client
|
||||
import net.corda.notary.bftsmart.BFTSMaRt.Replica
|
||||
import java.nio.file.Path
|
||||
import java.security.PublicKey
|
||||
import java.util.*
|
||||
@ -78,7 +79,7 @@ object BFTSMaRt {
|
||||
fun waitUntilAllReplicasHaveInitialized()
|
||||
}
|
||||
|
||||
class Client(config: BFTSMaRtConfig, private val clientId: Int, private val cluster: Cluster, private val notaryService: BFTNonValidatingNotaryService) : SingletonSerializeAsToken() {
|
||||
class Client(config: BFTSMaRtConfig, private val clientId: Int, private val cluster: Cluster, private val notaryService: BftSmartNotaryService) : SingletonSerializeAsToken() {
|
||||
companion object {
|
||||
private val log = contextLogger()
|
||||
}
|
||||
@ -178,7 +179,7 @@ object BFTSMaRt {
|
||||
abstract class Replica(config: BFTSMaRtConfig,
|
||||
replicaId: Int,
|
||||
createMap: () -> AppendOnlyPersistentMap<StateRef, SecureHash,
|
||||
BFTNonValidatingNotaryService.CommittedState, PersistentStateRef>,
|
||||
BftSmartNotaryService.CommittedState, PersistentStateRef>,
|
||||
protected val services: ServiceHubInternal,
|
||||
protected val notaryIdentityKey: PublicKey) : DefaultRecoverable() {
|
||||
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.writer
|
||||
import net.corda.core.utilities.NetworkHostAndPort
|
||||
import net.corda.core.utilities.contextLogger
|
||||
import net.corda.core.utilities.debug
|
||||
import net.corda.node.services.transactions.PathManager
|
||||
import java.io.PrintWriter
|
||||
import java.net.InetAddress
|
||||
import java.net.Socket
|
@ -1,4 +1,4 @@
|
||||
package net.corda.node.services.transactions
|
||||
package net.corda.notary.bftsmart
|
||||
|
||||
import co.paralleluniverse.fibers.Suspendable
|
||||
import com.google.common.util.concurrent.SettableFuture
|
||||
@ -10,6 +10,7 @@ import net.corda.core.identity.Party
|
||||
import net.corda.core.internal.notary.NotaryInternalException
|
||||
import net.corda.core.internal.notary.NotaryService
|
||||
import net.corda.core.internal.notary.verifySignature
|
||||
import net.corda.core.schemas.MappedSchema
|
||||
import net.corda.core.schemas.PersistentStateRef
|
||||
import net.corda.core.serialization.deserialize
|
||||
import net.corda.core.serialization.serialize
|
||||
@ -21,6 +22,7 @@ import net.corda.core.utilities.getOrThrow
|
||||
import net.corda.core.utilities.unwrap
|
||||
import net.corda.node.services.api.ServiceHubInternal
|
||||
import net.corda.node.services.config.BFTSMaRtConfiguration
|
||||
import net.corda.node.services.transactions.PersistentUniquenessProvider
|
||||
import net.corda.node.utilities.AppendOnlyPersistentMap
|
||||
import net.corda.nodeapi.internal.persistence.NODE_DATABASE_PREFIX
|
||||
import java.security.PublicKey
|
||||
@ -33,16 +35,30 @@ import kotlin.concurrent.thread
|
||||
*
|
||||
* A transaction is notarised when the consensus is reached by the cluster on its uniqueness, and time-window validity.
|
||||
*/
|
||||
class BFTNonValidatingNotaryService(
|
||||
class BftSmartNotaryService(
|
||||
override val services: ServiceHubInternal,
|
||||
override val notaryIdentityKey: PublicKey,
|
||||
private val bftSMaRtConfig: BFTSMaRtConfiguration,
|
||||
cluster: BFTSMaRt.Cluster
|
||||
override val notaryIdentityKey: PublicKey
|
||||
) : NotaryService() {
|
||||
companion object {
|
||||
private val log = contextLogger()
|
||||
}
|
||||
|
||||
private val notaryConfig = services.configuration.notary
|
||||
?: throw IllegalArgumentException("Failed to register ${this::class.java}: notary configuration not present")
|
||||
|
||||
private val bftSMaRtConfig = notaryConfig.bftSMaRt
|
||||
?: throw IllegalArgumentException("Failed to register ${this::class.java}: raft configuration not present")
|
||||
|
||||
private val cluster: BFTSMaRt.Cluster = makeBFTCluster(notaryIdentityKey, bftSMaRtConfig)
|
||||
|
||||
protected open fun makeBFTCluster(notaryKey: PublicKey, bftSMaRtConfig: BFTSMaRtConfiguration): BFTSMaRt.Cluster {
|
||||
return object : BFTSMaRt.Cluster {
|
||||
override fun waitUntilAllReplicasHaveInitialized() {
|
||||
log.warn("A BFT replica may still be initializing, in which case the upcoming consensus change may cause it to spin.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val client: BFTSMaRt.Client
|
||||
private val replicaHolder = SettableFuture.create<Replica>()
|
||||
|
||||
@ -71,7 +87,7 @@ class BFTNonValidatingNotaryService(
|
||||
|
||||
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
|
||||
override fun call(): Void? {
|
||||
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.whenever
|
||||
@ -23,8 +23,6 @@ import net.corda.core.utilities.getOrThrow
|
||||
import net.corda.core.utilities.seconds
|
||||
import net.corda.node.services.config.BFTSMaRtConfiguration
|
||||
import net.corda.node.services.config.NotaryConfig
|
||||
import net.corda.node.services.transactions.minClusterSize
|
||||
import net.corda.node.services.transactions.minCorrectReplicas
|
||||
import net.corda.nodeapi.internal.DevIdentityGenerator
|
||||
import net.corda.nodeapi.internal.network.NetworkParametersCopier
|
||||
import net.corda.testing.common.internal.testNetworkParameters
|
||||
@ -94,7 +92,11 @@ class BFTNotaryServiceTests {
|
||||
|
||||
val nodes = replicaIds.map { replicaId ->
|
||||
mockNet.createUnstartedNode(InternalMockNodeParameters(configOverrides = {
|
||||
val notary = NotaryConfig(validating = false, bftSMaRt = BFTSMaRtConfiguration(replicaId, clusterAddresses, exposeRaces = exposeRaces))
|
||||
val notary = NotaryConfig(
|
||||
validating = false,
|
||||
bftSMaRt = BFTSMaRtConfiguration(replicaId, clusterAddresses, exposeRaces = exposeRaces),
|
||||
className = "net.corda.notary.bftsmart.BftSmartNotaryService"
|
||||
)
|
||||
doReturn(notary).whenever(it).notary
|
||||
}))
|
||||
} + mockNet.createUnstartedNode()
|
@ -1,7 +1,7 @@
|
||||
package net.corda.node.services.transactions
|
||||
package net.corda.notary.bftsmart
|
||||
|
||||
import net.corda.core.utilities.NetworkHostAndPort
|
||||
import net.corda.node.services.transactions.BFTSMaRtConfig.Companion.portIsClaimedFormat
|
||||
import net.corda.notary.bftsmart.BFTSMaRtConfig.Companion.portIsClaimedFormat
|
||||
import org.assertj.core.api.Assertions.assertThatThrownBy
|
||||
import org.junit.Test
|
||||
import kotlin.test.assertEquals
|
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.BufferOutput
|
||||
@ -27,6 +27,7 @@ import net.corda.core.serialization.internal.checkpointSerialize
|
||||
import net.corda.core.utilities.ByteSequence
|
||||
import net.corda.core.utilities.contextLogger
|
||||
import net.corda.core.utilities.debug
|
||||
import net.corda.node.services.transactions.PersistentUniquenessProvider
|
||||
import net.corda.node.utilities.AppendOnlyPersistentMap
|
||||
import net.corda.nodeapi.internal.persistence.CordaPersistence
|
||||
import net.corda.nodeapi.internal.persistence.currentDBSession
|
||||
@ -111,7 +112,7 @@ class RaftTransactionCommitLog<E, EK>(
|
||||
}
|
||||
}
|
||||
|
||||
private fun logRequest(commitCommand: RaftTransactionCommitLog.Commands.CommitTransaction) {
|
||||
private fun logRequest(commitCommand: Commands.CommitTransaction) {
|
||||
val request = PersistentUniquenessProvider.Request(
|
||||
consumingTxHash = commitCommand.txId.toString(),
|
||||
partyName = commitCommand.requestingParty,
|
||||
@ -192,8 +193,8 @@ class RaftTransactionCommitLog<E, EK>(
|
||||
registerAbstract(SecureHash::class.java, CordaKryoSerializer::class.java)
|
||||
registerAbstract(TimeWindow::class.java, CordaKryoSerializer::class.java)
|
||||
registerAbstract(NotaryError::class.java, CordaKryoSerializer::class.java)
|
||||
register(RaftTransactionCommitLog.Commands.CommitTransaction::class.java, CordaKryoSerializer::class.java)
|
||||
register(RaftTransactionCommitLog.Commands.Get::class.java, CordaKryoSerializer::class.java)
|
||||
register(Commands.CommitTransaction::class.java, CordaKryoSerializer::class.java)
|
||||
register(Commands.Get::class.java, CordaKryoSerializer::class.java)
|
||||
register(StateRef::class.java, CordaKryoSerializer::class.java)
|
||||
register(LinkedHashMap::class.java, CordaKryoSerializer::class.java)
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package net.corda.node.services.transactions
|
||||
package net.corda.notary.raft
|
||||
|
||||
import com.codahale.metrics.Gauge
|
||||
import com.codahale.metrics.MetricRegistry
|
||||
@ -26,12 +26,12 @@ import net.corda.core.serialization.serialize
|
||||
import net.corda.core.utilities.contextLogger
|
||||
import net.corda.core.utilities.debug
|
||||
import net.corda.node.services.config.RaftConfig
|
||||
import net.corda.node.services.transactions.RaftTransactionCommitLog.Commands.CommitTransaction
|
||||
import net.corda.node.utilities.AppendOnlyPersistentMap
|
||||
import net.corda.node.utilities.NamedCacheFactory
|
||||
import net.corda.nodeapi.internal.config.MutualSslConfiguration
|
||||
import net.corda.nodeapi.internal.persistence.CordaPersistence
|
||||
import net.corda.nodeapi.internal.persistence.NODE_DATABASE_PREFIX
|
||||
import net.corda.notary.raft.RaftTransactionCommitLog.Commands.CommitTransaction
|
||||
import java.nio.file.Path
|
||||
import java.time.Clock
|
||||
import java.util.concurrent.CompletableFuture
|
@ -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.StateRef
|
@ -1,4 +1,4 @@
|
||||
package net.corda.node.services.transactions
|
||||
package net.corda.notary.raft
|
||||
|
||||
import com.typesafe.config.ConfigFactory
|
||||
import io.atomix.catalyst.transport.Address
|
||||
@ -17,7 +17,7 @@ import net.corda.core.utilities.NetworkHostAndPort
|
||||
import net.corda.core.utilities.getOrThrow
|
||||
import net.corda.node.internal.configureDatabase
|
||||
import net.corda.node.services.schema.NodeSchemaService
|
||||
import net.corda.node.utilities.TestingNamedCacheFactory
|
||||
import net.corda.testing.internal.TestingNamedCacheFactory
|
||||
import net.corda.nodeapi.internal.persistence.CordaPersistence
|
||||
import net.corda.nodeapi.internal.persistence.DatabaseConfig
|
||||
import net.corda.testing.core.ALICE_NAME
|
||||
@ -156,7 +156,7 @@ class RaftTransactionCommitLogTests {
|
||||
val storage = Storage.builder().withStorageLevel(StorageLevel.MEMORY).build()
|
||||
val address = Address(myAddress.host, myAddress.port)
|
||||
// Enterprise - OS difference: below configureDatabase parameters differs with OS intentionally to be able run test in remote database
|
||||
val database = configureDatabase(makeInternalTestDataSourceProperties( configSupplier = { ConfigFactory.empty() }), DatabaseConfig(runMigration = true), { null }, { null }, NodeSchemaService(includeNotarySchemas = true))
|
||||
val database = configureDatabase(makeInternalTestDataSourceProperties( configSupplier = { ConfigFactory.empty() }), DatabaseConfig(runMigration = true), { null }, { null }, NodeSchemaService(extraSchemas = setOf(RaftNotarySchemaV1)))
|
||||
databases.add(database)
|
||||
val stateMachineFactory = { RaftTransactionCommitLog(database, Clock.systemUTC(), { RaftUniquenessProvider.createMap(TestingNamedCacheFactory()) }) }
|
||||
|
@ -155,18 +155,9 @@ dependencies {
|
||||
// We only need this dependency to compile our Caplet against.
|
||||
compileOnly "co.paralleluniverse:capsule:$capsule_version"
|
||||
|
||||
// Java Atomix: RAFT library
|
||||
compile 'io.atomix.copycat:copycat-client:1.2.8'
|
||||
compile 'io.atomix.copycat:copycat-server:1.2.8'
|
||||
compile 'io.atomix.catalyst:catalyst-netty:1.2.1'
|
||||
|
||||
// OkHTTP: Simple HTTP library.
|
||||
compile "com.squareup.okhttp3:okhttp:$okhttp_version"
|
||||
|
||||
// BFT-SMaRt
|
||||
compile 'commons-codec:commons-codec:1.10'
|
||||
compile 'com.github.bft-smart:library:master-v1.1-beta-g6215ec8-87'
|
||||
|
||||
// Apache Shiro: authentication, authorization and session management.
|
||||
compile "org.apache.shiro:shiro-core:${shiro_version}"
|
||||
|
||||
|
@ -52,7 +52,7 @@ class DistributedServiceTests : IntegrationTest() {
|
||||
invokeRpc(CordaRPCOps::stateMachinesFeed))
|
||||
)
|
||||
driver(DriverParameters(
|
||||
extraCordappPackagesToScan = listOf("net.corda.finance.contracts", "net.corda.finance.schemas"),
|
||||
extraCordappPackagesToScan = listOf("net.corda.finance.contracts", "net.corda.finance.schemas", "net.corda.notary.raft"),
|
||||
notarySpecs = listOf(
|
||||
NotarySpec(
|
||||
DUMMY_NOTARY_NAME,
|
||||
|
@ -16,7 +16,6 @@ import net.corda.node.services.config.configureWithDevSSLCertificate
|
||||
import net.corda.node.services.network.PersistentNetworkMapCache
|
||||
import net.corda.node.services.transactions.PersistentUniquenessProvider
|
||||
import net.corda.node.utilities.AffinityExecutor.ServiceAffinityExecutor
|
||||
import net.corda.node.utilities.TestingNamedCacheFactory
|
||||
import net.corda.nodeapi.internal.persistence.CordaPersistence
|
||||
import net.corda.nodeapi.internal.persistence.DatabaseConfig
|
||||
import net.corda.testing.core.ALICE_NAME
|
||||
@ -30,6 +29,8 @@ import net.corda.testing.node.internal.MOCK_VERSION_INFO
|
||||
import net.corda.testing.node.internal.makeInternalTestDataSourceProperties
|
||||
import org.apache.activemq.artemis.api.core.Message.HDR_VALIDATED_USER
|
||||
import org.apache.activemq.artemis.api.core.SimpleString
|
||||
import net.corda.testing.internal.TestingNamedCacheFactory
|
||||
import org.apache.activemq.artemis.api.core.ActiveMQConnectionTimedOutException
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.assertj.core.api.Assertions.assertThatThrownBy
|
||||
import org.junit.After
|
||||
|
@ -5,7 +5,6 @@ import net.corda.core.utilities.NetworkHostAndPort
|
||||
import net.corda.node.internal.configureDatabase
|
||||
import net.corda.node.internal.schemas.NodeInfoSchemaV1
|
||||
import net.corda.node.services.identity.InMemoryIdentityService
|
||||
import net.corda.node.utilities.TestingNamedCacheFactory
|
||||
import net.corda.nodeapi.internal.DEV_ROOT_CA
|
||||
import net.corda.nodeapi.internal.persistence.CordaPersistence
|
||||
import net.corda.nodeapi.internal.persistence.DatabaseConfig
|
||||
@ -14,6 +13,7 @@ import net.corda.testing.internal.IntegrationTest
|
||||
import net.corda.testing.internal.IntegrationTestSchemas
|
||||
import net.corda.testing.internal.toDatabaseSchemaName
|
||||
import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties
|
||||
import net.corda.testing.internal.TestingNamedCacheFactory
|
||||
import net.corda.testing.node.internal.makeTestDatabaseProperties
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.assertj.core.api.Assertions.assertThatIllegalArgumentException
|
||||
|
@ -48,8 +48,9 @@ class P2PMessagingTest : IntegrationTest() {
|
||||
|
||||
private fun startDriverWithDistributedService(dsl: DriverDSL.(List<InProcess>) -> Unit) {
|
||||
driver(DriverParameters(
|
||||
startNodesInProcess = true,
|
||||
notarySpecs = listOf(NotarySpec(DISTRIBUTED_SERVICE_NAME, cluster = ClusterSpec.Raft(clusterSize = 2)))
|
||||
startNodesInProcess = true,
|
||||
extraCordappPackagesToScan = listOf("net.corda.notary.raft"),
|
||||
notarySpecs = listOf(NotarySpec(DISTRIBUTED_SERVICE_NAME, cluster = ClusterSpec.Raft(clusterSize = 2)))
|
||||
)) {
|
||||
dsl(defaultNotaryHandle.nodeHandles.getOrThrow().map { (it as InProcess) })
|
||||
}
|
||||
|
@ -118,7 +118,6 @@ abstract class AbstractNode<S>(val configuration: NodeConfiguration,
|
||||
val platformClock: CordaClock,
|
||||
cacheFactoryPrototype: NamedCacheFactory,
|
||||
protected val versionInfo: VersionInfo,
|
||||
protected val cordappLoader: CordappLoader,
|
||||
protected val serverThread: AffinityExecutor.ServiceAffinityExecutor,
|
||||
protected val busyNodeLatch: ReusableLatch = ReusableLatch()) : SingletonSerializeAsToken() {
|
||||
|
||||
@ -144,7 +143,8 @@ abstract class AbstractNode<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 database: CordaPersistence = createCordaPersistence(
|
||||
configuration.database,
|
||||
@ -515,6 +515,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 fun installCordaServices(myNotaryIdentity: PartyAndCertificate?) {
|
||||
@ -810,17 +828,32 @@ abstract class AbstractNode<S>(val configuration: NodeConfiguration,
|
||||
}
|
||||
|
||||
private fun makeNotaryService(myNotaryIdentity: PartyAndCertificate?): NotaryService? {
|
||||
return configuration.notary?.let {
|
||||
makeCoreNotaryService(it, myNotaryIdentity).also {
|
||||
it.tokenize()
|
||||
runOnStop += it::stop
|
||||
installCoreFlow(NotaryFlow.Client::class, it::createServiceFlow)
|
||||
log.info("Running core notary: ${it.javaClass.name}")
|
||||
it.start()
|
||||
return configuration.notary?.let { notaryConfig ->
|
||||
val serviceClass = getNotaryServiceClass(notaryConfig.className)
|
||||
log.info("Starting notary service: $serviceClass")
|
||||
|
||||
val notaryKey = myNotaryIdentity?.owningKey
|
||||
?: throw IllegalArgumentException("Unable to start notary service $serviceClass: notary identity not found")
|
||||
val constructor = serviceClass.getDeclaredConstructor(ServiceHubInternal::class.java, PublicKey::class.java).apply { isAccessible = true }
|
||||
val service = constructor.newInstance(services, notaryKey) as NotaryService
|
||||
|
||||
service.run {
|
||||
tokenize()
|
||||
runOnStop += ::stop
|
||||
installCoreFlow(NotaryFlow.Client::class, ::createServiceFlow)
|
||||
start()
|
||||
}
|
||||
return service
|
||||
}
|
||||
}
|
||||
|
||||
private fun getNotaryServiceClass(className: String): Class<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 {
|
||||
// Place the long term identity key in the KMS. Eventually, this is likely going to be separated again because
|
||||
// the KMS is meant for derived temporary keys used in transactions, and we're not supposed to sign things with
|
||||
@ -828,35 +861,6 @@ abstract class AbstractNode<S>(val configuration: NodeConfiguration,
|
||||
return PersistentKeyManagementService(cacheFactory, identityService, database)
|
||||
}
|
||||
|
||||
private fun makeCoreNotaryService(notaryConfig: NotaryConfig, myNotaryIdentity: PartyAndCertificate?): NotaryService {
|
||||
val notaryKey = myNotaryIdentity?.owningKey
|
||||
?: throw IllegalArgumentException("No notary identity initialized when creating a notary service")
|
||||
return notaryConfig.run {
|
||||
when {
|
||||
raft != null -> {
|
||||
val uniquenessProvider = RaftUniquenessProvider(configuration.baseDirectory, configuration.p2pSslOptions, database, platformClock, monitoringService.metrics, cacheFactory, raft)
|
||||
(if (validating) ::RaftValidatingNotaryService else ::RaftNonValidatingNotaryService)(services, notaryKey, uniquenessProvider)
|
||||
}
|
||||
bftSMaRt != null -> {
|
||||
if (validating) throw IllegalArgumentException("Validating BFTSMaRt notary not supported")
|
||||
BFTNonValidatingNotaryService(services, notaryKey, bftSMaRt, makeBFTCluster(notaryKey, bftSMaRt))
|
||||
}
|
||||
mysql != null -> {
|
||||
(if (validating) ::MySQLValidatingNotaryService else ::MySQLNonValidatingNotaryService)(services, notaryKey, mysql, configuration.devMode)
|
||||
}
|
||||
else -> (if (validating) ::ValidatingNotaryService else ::SimpleNotaryService)(services, notaryKey)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected open fun makeBFTCluster(notaryKey: PublicKey, bftSMaRtConfig: BFTSMaRtConfiguration): BFTSMaRt.Cluster {
|
||||
return object : BFTSMaRt.Cluster {
|
||||
override fun waitUntilAllReplicasHaveInitialized() {
|
||||
log.warn("A BFT replica may still be initializing, in which case the upcoming consensus change may cause it to spin.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
open fun stop() {
|
||||
// TODO: We need a good way of handling "nice to have" shutdown events, especially those that deal with the
|
||||
// network, including unsubscribing from updates from remote services. Possibly some sort of parameter to stop()
|
||||
|
@ -28,10 +28,8 @@ import net.corda.core.utilities.contextLogger
|
||||
import net.corda.node.CordaClock
|
||||
import net.corda.node.SimpleClock
|
||||
import net.corda.node.VersionInfo
|
||||
import net.corda.node.cordapp.CordappLoader
|
||||
import net.corda.node.internal.artemis.ArtemisBroker
|
||||
import net.corda.node.internal.artemis.BrokerAddresses
|
||||
import net.corda.node.internal.cordapp.JarScanningCordappLoader
|
||||
import net.corda.node.internal.security.RPCSecurityManager
|
||||
import net.corda.node.internal.security.RPCSecurityManagerImpl
|
||||
import net.corda.node.internal.security.RPCSecurityManagerWithAdditionalUser
|
||||
@ -88,14 +86,12 @@ class NodeWithInfo(val node: Node, val info: NodeInfo) {
|
||||
open class Node(configuration: NodeConfiguration,
|
||||
versionInfo: VersionInfo,
|
||||
private val initialiseSerialization: Boolean = true,
|
||||
cordappLoader: CordappLoader = makeCordappLoader(configuration, versionInfo),
|
||||
cacheFactoryPrototype: NamedCacheFactory = DefaultNamedCacheFactory()
|
||||
) : AbstractNode<NodeInfo>(
|
||||
configuration,
|
||||
createClock(configuration),
|
||||
cacheFactoryPrototype,
|
||||
versionInfo,
|
||||
cordappLoader,
|
||||
// Under normal (non-test execution) it will always be "1"
|
||||
AffinityExecutor.ServiceAffinityExecutor("Node thread-${sameVmNodeCounter.incrementAndGet()}", 1)
|
||||
) {
|
||||
@ -133,10 +129,6 @@ open class Node(configuration: NodeConfiguration,
|
||||
|
||||
private val sameVmNodeCounter = AtomicInteger()
|
||||
|
||||
private fun makeCordappLoader(configuration: NodeConfiguration, versionInfo: VersionInfo): CordappLoader {
|
||||
return JarScanningCordappLoader.fromDirectories(configuration.cordappDirectories, versionInfo)
|
||||
}
|
||||
|
||||
// TODO: make this configurable.
|
||||
const val MAX_RPC_MESSAGE_SIZE = 10485760
|
||||
|
||||
|
@ -22,7 +22,6 @@ import net.corda.node.services.config.NodeConfiguration
|
||||
import net.corda.node.services.config.NodeConfigurationImpl
|
||||
import net.corda.node.services.config.shouldStartLocalShell
|
||||
import net.corda.node.services.config.shouldStartSSHDaemon
|
||||
import net.corda.node.services.transactions.bftSMaRtSerialFilter
|
||||
import net.corda.node.utilities.createKeyPairAndSelfSignedTLSCertificate
|
||||
import net.corda.node.utilities.registration.HTTPNetworkRegistrationService
|
||||
import net.corda.node.utilities.registration.NodeRegistrationException
|
||||
@ -259,7 +258,8 @@ open class NodeStartup : CordaCliWrapper("corda", "Runs a Corda Node") {
|
||||
}
|
||||
|
||||
val nodeInfo = node.start()
|
||||
logLoadedCorDapps(node.services.cordappProvider.cordapps)
|
||||
val loadedCodapps = node.services.cordappProvider.cordapps.filter { it.isLoaded }
|
||||
logLoadedCorDapps(loadedCodapps)
|
||||
|
||||
node.nodeReadyFuture.thenMatch({
|
||||
// Elapsed time in seconds. We used 10 / 100.0 and not directly / 1000.0 to only keep two decimal digits.
|
||||
@ -426,6 +426,16 @@ open class NodeStartup : CordaCliWrapper("corda", "Runs a Corda Node") {
|
||||
SerialFilter.install(filter)
|
||||
}
|
||||
|
||||
/** This filter is required for BFT-Smart to work as it only supports Java serialization. */
|
||||
// TODO: move this filter out of the node, allow Cordapps to specify filters.
|
||||
private fun bftSMaRtSerialFilter(clazz: Class<*>): Boolean = clazz.name.let {
|
||||
it.startsWith("bftsmart.")
|
||||
|| it.startsWith("java.security.")
|
||||
|| it.startsWith("java.util.")
|
||||
|| it.startsWith("java.lang.")
|
||||
|| it.startsWith("java.net.")
|
||||
}
|
||||
|
||||
protected open fun getVersionInfo(): VersionInfo {
|
||||
return VersionInfo(
|
||||
PLATFORM_VERSION,
|
||||
|
@ -9,6 +9,8 @@ import net.corda.core.flows.*
|
||||
import net.corda.core.internal.*
|
||||
import net.corda.core.internal.cordapp.CordappImpl
|
||||
import net.corda.core.internal.cordapp.CordappInfoResolver
|
||||
import net.corda.core.internal.notary.NotaryService
|
||||
import net.corda.core.internal.notary.TrustedAuthorityNotaryService
|
||||
import net.corda.core.node.services.CordaService
|
||||
import net.corda.core.schemas.MappedSchema
|
||||
import net.corda.core.serialization.SerializationCustomSerializer
|
||||
@ -36,9 +38,12 @@ import kotlin.streams.toList
|
||||
* @property cordappJarPaths The classpath of cordapp JARs
|
||||
*/
|
||||
class JarScanningCordappLoader private constructor(private val cordappJarPaths: List<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)
|
||||
|
||||
@ -58,9 +63,10 @@ class JarScanningCordappLoader private constructor(private val cordappJarPaths:
|
||||
*
|
||||
* @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(", ", "[", "]")}")
|
||||
return JarScanningCordappLoader(corDappDirectories.distinct().flatMap(this::jarUrlsInDirectory).map { it.restricted() }, versionInfo)
|
||||
val paths = corDappDirectories.distinct().flatMap(this::jarUrlsInDirectory).map { it.restricted() }
|
||||
return JarScanningCordappLoader(paths, versionInfo, extraCordapps)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -68,11 +74,12 @@ class JarScanningCordappLoader private constructor(private val cordappJarPaths:
|
||||
*
|
||||
* @param scanJars Uses the JAR URLs provided for classpath scanning and Cordapp detection.
|
||||
*/
|
||||
fun fromJarUrls(scanJars: List<URL>, versionInfo: VersionInfo = VersionInfo.UNKNOWN): JarScanningCordappLoader {
|
||||
return JarScanningCordappLoader(scanJars.map { it.restricted() }, versionInfo)
|
||||
fun fromJarUrls(scanJars: List<URL>, versionInfo: VersionInfo = VersionInfo.UNKNOWN, extraCordapps: List<CordappImpl> = emptyList()): JarScanningCordappLoader {
|
||||
val paths = scanJars.map { it.restricted() }
|
||||
return JarScanningCordappLoader(paths, versionInfo, extraCordapps)
|
||||
}
|
||||
|
||||
private fun URL.restricted(rootPackageName: String? = null) = RestrictedURL(this, rootPackageName)
|
||||
private fun URL.restricted(rootPackageName: String? = null) = RestrictedURL(this, rootPackageName)
|
||||
|
||||
private fun jarUrlsInDirectory(directory: Path): List<URL> {
|
||||
|
||||
@ -85,32 +92,7 @@ class JarScanningCordappLoader private constructor(private val cordappJarPaths:
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** A list of the core RPC flows present in Corda */
|
||||
private val coreRPCFlows = listOf(
|
||||
ContractUpgradeFlow.Initiate::class.java,
|
||||
ContractUpgradeFlow.Authorise::class.java,
|
||||
ContractUpgradeFlow.Deauthorise::class.java)
|
||||
}
|
||||
|
||||
/** A Cordapp representing the core package which is not scanned automatically. */
|
||||
@VisibleForTesting
|
||||
internal val coreCordapp = CordappImpl(
|
||||
contractClassNames = listOf(),
|
||||
initiatedFlows = listOf(),
|
||||
rpcFlows = coreRPCFlows,
|
||||
serviceFlows = listOf(),
|
||||
schedulableFlows = listOf(),
|
||||
services = listOf(),
|
||||
serializationWhitelists = listOf(),
|
||||
serializationCustomSerializers = listOf(),
|
||||
customSchemas = setOf(),
|
||||
info = CordappImpl.Info("corda-core", versionInfo.vendor, versionInfo.releaseVersion, 1, versionInfo.platformVersion),
|
||||
allFlows = listOf(),
|
||||
jarPath = ContractUpgradeFlow.javaClass.location, // Core JAR location
|
||||
jarHash = SecureHash.allOnesHash
|
||||
)
|
||||
|
||||
private fun loadCordapps(): List<CordappImpl> {
|
||||
val cordapps = cordappJarPaths
|
||||
.map { scanCordapp(it).toCordapp(it) }
|
||||
@ -126,7 +108,6 @@ class JarScanningCordappLoader private constructor(private val cordappJarPaths:
|
||||
cordapps.forEach { CordappInfoResolver.register(it.cordappClasses, it.info) }
|
||||
return cordapps
|
||||
}
|
||||
|
||||
private fun RestrictedScanResult.toCordapp(url: RestrictedURL): CordappImpl {
|
||||
val info = url.url.openStream().let(::JarInputStream).use { it.manifest?.toCordappInfo(CordappImpl.jarName(url.url)) ?: CordappImpl.Info.UNKNOWN }
|
||||
return CordappImpl(
|
||||
@ -142,10 +123,21 @@ class JarScanningCordappLoader private constructor(private val cordappJarPaths:
|
||||
findAllFlows(this),
|
||||
url.url,
|
||||
info,
|
||||
getJarHash(url.url)
|
||||
getJarHash(url.url),
|
||||
findNotaryService(this)
|
||||
)
|
||||
}
|
||||
|
||||
private fun findNotaryService(scanResult: RestrictedScanResult): Class<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)
|
||||
logger.info("Found notary service CorDapp implementations: " + result.joinToString(", "))
|
||||
return result.firstOrNull()
|
||||
}
|
||||
|
||||
private fun getJarHash(url: URL): SecureHash.SHA256 = url.openStream().readFully().sha256()
|
||||
|
||||
private fun findServices(scanResult: RestrictedScanResult): List<Class<out SerializeAsToken>> {
|
||||
@ -202,7 +194,7 @@ class JarScanningCordappLoader private constructor(private val cordappJarPaths:
|
||||
}
|
||||
|
||||
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)
|
||||
@ -243,18 +235,21 @@ class JarScanningCordappLoader private constructor(private val cordappJarPaths:
|
||||
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) {
|
||||
fun getNamesOfClassesImplementing(type: KClass<*>): List<String> {
|
||||
return scanResult.getNamesOfClassesImplementing(type.java)
|
||||
.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)
|
||||
.filter { it.startsWith(qualifiedNamePrefix) }
|
||||
.mapNotNull { loadClass(it, type) }
|
||||
.filterNot { Modifier.isAbstract(it.modifiers) }
|
||||
.map { it.kotlin.objectOrNewInstance() }
|
||||
}
|
||||
|
||||
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 custom: Boolean = false,
|
||||
val mysql: MySQLConfiguration? = null,
|
||||
val serviceLegalName: CordaX500Name? = null
|
||||
val serviceLegalName: CordaX500Name? = null,
|
||||
val className: String = "net.corda.node.services.transactions.SimpleNotaryService"
|
||||
) {
|
||||
init {
|
||||
require(raft == null || bftSMaRt == null || !custom || mysql == null) {
|
||||
|
@ -14,12 +14,10 @@ import net.corda.node.services.identity.PersistentIdentityService
|
||||
import net.corda.node.services.keys.PersistentKeyManagementService
|
||||
import net.corda.node.services.messaging.P2PMessageDeduplicator
|
||||
import net.corda.node.services.persistence.DBCheckpointStorage
|
||||
import net.corda.node.services.persistence.RunOnceService
|
||||
import net.corda.node.services.persistence.DBTransactionStorage
|
||||
import net.corda.node.services.persistence.NodeAttachmentService
|
||||
import net.corda.node.services.persistence.RunOnceService
|
||||
import net.corda.node.services.transactions.BFTNonValidatingNotaryService
|
||||
import net.corda.node.services.transactions.PersistentUniquenessProvider
|
||||
import net.corda.node.services.transactions.RaftUniquenessProvider
|
||||
import net.corda.node.services.upgrade.ContractUpgradeServiceImpl
|
||||
import net.corda.node.services.vault.VaultSchemaV1
|
||||
|
||||
@ -30,7 +28,7 @@ import net.corda.node.services.vault.VaultSchemaV1
|
||||
* TODO: support plugins for schema version upgrading or custom mapping not supported by original [QueryableState].
|
||||
* TODO: create whitelisted tables when a CorDapp is first installed
|
||||
*/
|
||||
class NodeSchemaService(private val extraSchemas: Set<MappedSchema> = emptySet(), includeNotarySchemas: Boolean = false) : SchemaService, SingletonSerializeAsToken() {
|
||||
class NodeSchemaService(private val extraSchemas: Set<MappedSchema> = emptySet()) : SchemaService, SingletonSerializeAsToken() {
|
||||
// Core Entities used by a Node
|
||||
object NodeCore
|
||||
|
||||
@ -49,26 +47,12 @@ class NodeSchemaService(private val extraSchemas: Set<MappedSchema> = emptySet()
|
||||
override val migrationResource = "node-core.changelog-master"
|
||||
}
|
||||
|
||||
// Entities used by a Notary
|
||||
object NodeNotary
|
||||
|
||||
object NodeNotaryV1 : MappedSchema(schemaFamily = NodeNotary.javaClass, version = 1,
|
||||
mappedTypes = listOf(PersistentUniquenessProvider.BaseComittedState::class.java,
|
||||
PersistentUniquenessProvider.Request::class.java,
|
||||
PersistentUniquenessProvider.CommittedState::class.java,
|
||||
RaftUniquenessProvider.CommittedState::class.java,
|
||||
BFTNonValidatingNotaryService.CommittedState::class.java
|
||||
)) {
|
||||
override val migrationResource = "node-notary.changelog-master"
|
||||
}
|
||||
|
||||
// Required schemas are those used by internal Corda services
|
||||
private val requiredSchemas: Map<MappedSchema, SchemaService.SchemaOptions> =
|
||||
mapOf(Pair(CommonSchemaV1, SchemaOptions()),
|
||||
Pair(VaultSchemaV1, SchemaOptions()),
|
||||
Pair(NodeInfoSchemaV1, SchemaOptions()),
|
||||
Pair(NodeCoreV1, SchemaOptions())) +
|
||||
if (includeNotarySchemas) mapOf(Pair(NodeNotaryV1, SchemaOptions())) else emptyMap()
|
||||
Pair(VaultSchemaV1, SchemaOptions()),
|
||||
Pair(NodeInfoSchemaV1, SchemaOptions()),
|
||||
Pair(NodeCoreV1, SchemaOptions()))
|
||||
|
||||
fun internalSchemas() = requiredSchemas.keys + extraSchemas.filter { schema -> // when mapped schemas from the finance module are present, they are considered as internal ones
|
||||
schema::class.qualifiedName == "net.corda.finance.schemas.CashSchemaV1" || schema::class.qualifiedName == "net.corda.finance.schemas.CommercialPaperSchemaV1" }
|
||||
|
@ -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.internal.notary.NotaryServiceFlow
|
||||
import net.corda.core.internal.notary.TrustedAuthorityNotaryService
|
||||
import net.corda.core.schemas.MappedSchema
|
||||
import net.corda.node.services.api.ServiceHubInternal
|
||||
import java.security.PublicKey
|
||||
|
||||
/** A simple Notary service that does not perform transaction validation */
|
||||
/** An embedded notary service that uses the node's database to store committed states. */
|
||||
class SimpleNotaryService(override val services: ServiceHubInternal, override val notaryIdentityKey: PublicKey) : TrustedAuthorityNotaryService() {
|
||||
private val notaryConfig = services.configuration.notary
|
||||
?: throw IllegalArgumentException("Failed to register ${this::class.java}: notary configuration not present")
|
||||
|
||||
override val uniquenessProvider = PersistentUniquenessProvider(services.clock, services.database, services.cacheFactory)
|
||||
|
||||
override fun createServiceFlow(otherPartySession: FlowSession): NotaryServiceFlow = NonValidatingNotaryFlow(otherPartySession, this)
|
||||
override fun createServiceFlow(otherPartySession: FlowSession): NotaryServiceFlow {
|
||||
return if (notaryConfig.validating) {
|
||||
log.info("Starting in validating mode")
|
||||
ValidatingNotaryFlow(otherPartySession, this)
|
||||
} else {
|
||||
log.info("Starting in non-validating mode")
|
||||
NonValidatingNotaryFlow(otherPartySession, this)
|
||||
}
|
||||
}
|
||||
|
||||
override fun start() {}
|
||||
override fun stop() {}
|
||||
}
|
||||
|
||||
// Entities used by a Notary
|
||||
object NodeNotarySchema
|
||||
|
||||
object NodeNotarySchemaV1 : MappedSchema(schemaFamily = NodeNotarySchema.javaClass, version = 1,
|
||||
mappedTypes = listOf(PersistentUniquenessProvider.BaseComittedState::class.java,
|
||||
PersistentUniquenessProvider.Request::class.java,
|
||||
PersistentUniquenessProvider.CommittedState::class.java
|
||||
)) {
|
||||
override val migrationResource = "node-notary.changelog-master"
|
||||
}
|
@ -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)"/>
|
||||
</createTable>
|
||||
</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">
|
||||
<createTable tableName="node_notary_request_log">
|
||||
<column name="id" type="INT">
|
||||
@ -58,18 +46,11 @@
|
||||
</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="1511451595465-41">
|
||||
<addPrimaryKey columnNames="output_index, transaction_id" constraintName="node_notary_states_pkey"
|
||||
tableName="node_notary_committed_states"/>
|
||||
</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"/>
|
||||
|
@ -13,9 +13,4 @@
|
||||
<addPrimaryKey tableName="node_bft_committed_states" columnNames="output_index, transaction_id"
|
||||
constraintName="node_bft_states_pkey" clustered="false"/>
|
||||
</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>
|
@ -7,10 +7,6 @@
|
||||
|
||||
<changeSet author="R3.Corda" id="nullability">
|
||||
<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_raft_committed_states" columnName="raft_log_index" columnDataType="BIGINT"/>
|
||||
<addNotNullConstraint tableName="node_raft_committed_states" columnName="consuming_transaction_id" columnDataType="NVARCHAR(64)"/>
|
||||
</changeSet>
|
||||
</databaseChangeLog>
|
@ -47,7 +47,7 @@ class JarScanningCordappLoaderTest {
|
||||
fun `classes that aren't in cordapps aren't loaded`() {
|
||||
// Basedir will not be a corda node directory so the dummy flow shouldn't be recognised as a part of a cordapp
|
||||
val loader = JarScanningCordappLoader.fromDirectories(listOf(Paths.get(".")))
|
||||
assertThat(loader.cordapps).containsOnly(loader.coreCordapp)
|
||||
assertThat(loader.cordapps).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -55,9 +55,9 @@ class JarScanningCordappLoaderTest {
|
||||
val isolatedJAR = JarScanningCordappLoaderTest::class.java.getResource("isolated.jar")!!
|
||||
val loader = JarScanningCordappLoader.fromJarUrls(listOf(isolatedJAR))
|
||||
|
||||
assertThat(loader.cordapps).hasSize(2)
|
||||
assertThat(loader.cordapps).hasSize(1)
|
||||
|
||||
val actualCordapp = loader.cordapps.single { it != loader.coreCordapp }
|
||||
val actualCordapp = loader.cordapps.single()
|
||||
assertThat(actualCordapp.contractClassNames).isEqualTo(listOf(isolatedContractId))
|
||||
assertThat(actualCordapp.initiatedFlows.single().name).isEqualTo("net.corda.finance.contracts.isolated.IsolatedDummyFlow\$Acceptor")
|
||||
assertThat(actualCordapp.rpcFlows).isEmpty()
|
||||
@ -73,8 +73,8 @@ class JarScanningCordappLoaderTest {
|
||||
val loader = cordappLoaderForPackages(listOf(testScanPackage))
|
||||
|
||||
val actual = loader.cordapps.toTypedArray()
|
||||
// One core cordapp, one cordapp from this source tree. In gradle it will also pick up the node jar.
|
||||
assertThat(actual.size == 2 || actual.size == 3).isTrue()
|
||||
// One cordapp from this source tree. In gradle it will also pick up the node jar.
|
||||
assertThat(actual.size == 0 || actual.size == 1).isTrue()
|
||||
|
||||
val actualCordapp = actual.single { !it.initiatedFlows.isEmpty() }
|
||||
assertThat(actualCordapp.initiatedFlows).first().hasSameClassAs(DummyFlow::class.java)
|
||||
@ -111,7 +111,7 @@ class JarScanningCordappLoaderTest {
|
||||
fun `cordapp classloader sets target and min version to 1 if not specified`() {
|
||||
val jar = JarScanningCordappLoaderTest::class.java.getResource("versions/no-min-or-target-version.jar")!!
|
||||
val loader = JarScanningCordappLoader.fromJarUrls(listOf(jar), VersionInfo.UNKNOWN)
|
||||
loader.cordapps.filter { it.info.shortName != "corda-core" }.forEach {
|
||||
loader.cordapps.forEach {
|
||||
assertThat(it.info.targetPlatformVersion).isEqualTo(1)
|
||||
assertThat(it.info.minimumPlatformVersion).isEqualTo(1)
|
||||
}
|
||||
@ -123,8 +123,7 @@ class JarScanningCordappLoaderTest {
|
||||
// make sure classloader extracts correct values
|
||||
val jar = JarScanningCordappLoaderTest::class.java.getResource("versions/min-2-target-3.jar")!!
|
||||
val loader = JarScanningCordappLoader.fromJarUrls(listOf(jar), VersionInfo.UNKNOWN)
|
||||
// exclude the core cordapp
|
||||
val cordapp = loader.cordapps.single { it.cordappClasses.contains("net.corda.core.internal.cordapp.CordappImpl") }
|
||||
val cordapp = loader.cordapps.first()
|
||||
assertThat(cordapp.info.targetPlatformVersion).isEqualTo(3)
|
||||
assertThat(cordapp.info.minimumPlatformVersion).isEqualTo(2)
|
||||
}
|
||||
@ -144,24 +143,21 @@ class JarScanningCordappLoaderTest {
|
||||
fun `cordapp classloader does not load apps when their min platform version is greater than the node platform version`() {
|
||||
val jar = JarScanningCordappLoaderTest::class.java.getResource("versions/min-2-no-target.jar")!!
|
||||
val loader = JarScanningCordappLoader.fromJarUrls(listOf(jar), VersionInfo.UNKNOWN.copy(platformVersion = 1))
|
||||
// exclude the core cordapp
|
||||
assertThat(loader.cordapps).hasSize(1)
|
||||
assertThat(loader.cordapps).hasSize(0)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `cordapp classloader does load apps when their min platform version is less than the platform version`() {
|
||||
val jar = JarScanningCordappLoaderTest::class.java.getResource("versions/min-2-target-3.jar")!!
|
||||
val loader = JarScanningCordappLoader.fromJarUrls(listOf(jar), VersionInfo.UNKNOWN.copy(platformVersion = 1000))
|
||||
// exclude the core cordapp
|
||||
assertThat(loader.cordapps).hasSize(2)
|
||||
assertThat(loader.cordapps).hasSize(1)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `cordapp classloader does load apps when their min platform version is equal to the platform version`() {
|
||||
val jar = JarScanningCordappLoaderTest::class.java.getResource("versions/min-2-target-3.jar")!!
|
||||
val loader = JarScanningCordappLoader.fromJarUrls(listOf(jar), VersionInfo.UNKNOWN.copy(platformVersion = 2))
|
||||
// exclude the core cordapp
|
||||
assertThat(loader.cordapps).hasSize(2)
|
||||
assertThat(loader.cordapps).hasSize(1)
|
||||
}
|
||||
|
||||
private fun cordappLoaderForPackages(packages: Iterable<String>): CordappLoader {
|
||||
|
@ -313,20 +313,11 @@ class TwoPartyTradeFlowTests(private val anonymous: Boolean) {
|
||||
// of gets and puts.
|
||||
private fun makeNodeWithTracking(name: CordaX500Name): TestStartedNode {
|
||||
// Create a node in the mock network ...
|
||||
return mockNet.createNode(InternalMockNodeParameters(legalName = name), nodeFactory = { args, cordappLoader ->
|
||||
if (cordappLoader != null) {
|
||||
object : InternalMockNetwork.MockNode(args, cordappLoader) {
|
||||
// That constructs a recording tx storage
|
||||
override fun makeTransactionStorage(transactionCacheSizeBytes: Long): WritableTransactionStorage {
|
||||
return RecordingTransactionStorage(database, super.makeTransactionStorage(transactionCacheSizeBytes))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
object : InternalMockNetwork.MockNode(args) {
|
||||
// That constructs a recording tx storage
|
||||
override fun makeTransactionStorage(transactionCacheSizeBytes: Long): WritableTransactionStorage {
|
||||
return RecordingTransactionStorage(database, super.makeTransactionStorage(transactionCacheSizeBytes))
|
||||
}
|
||||
return mockNet.createNode(InternalMockNodeParameters(legalName = name), nodeFactory = { args ->
|
||||
object : InternalMockNetwork.MockNode(args) {
|
||||
// That constructs a recording tx storage
|
||||
override fun makeTransactionStorage(transactionCacheSizeBytes: Long): WritableTransactionStorage {
|
||||
return RecordingTransactionStorage(database, super.makeTransactionStorage(transactionCacheSizeBytes))
|
||||
}
|
||||
}
|
||||
})
|
||||
@ -611,7 +602,7 @@ class TwoPartyTradeFlowTests(private val anonymous: Boolean) {
|
||||
fillUpForBuyer(bobError, issuer, bob, notary).second
|
||||
}
|
||||
val alicesFakePaper = aliceNode.database.transaction {
|
||||
fillUpForSeller(aliceError, issuer, alice,1200.DOLLARS `issued by` issuer, null, notary).second
|
||||
fillUpForSeller(aliceError, issuer, alice, 1200.DOLLARS `issued by` issuer, null, notary).second
|
||||
}
|
||||
|
||||
insertFakeTransactions(bobsBadCash, bobNode, bob, notaryNode, bankNode)
|
||||
|
@ -22,6 +22,7 @@ import net.corda.core.transactions.SignedTransaction
|
||||
import net.corda.core.transactions.TransactionBuilder
|
||||
import net.corda.core.utilities.ProgressTracker
|
||||
import net.corda.core.utilities.seconds
|
||||
import net.corda.node.services.api.ServiceHubInternal
|
||||
import net.corda.node.services.config.FlowTimeoutConfiguration
|
||||
import net.corda.node.services.config.NodeConfiguration
|
||||
import net.corda.node.services.config.NotaryConfig
|
||||
@ -60,12 +61,13 @@ class TimedFlowTestRule(val clusterSize: Int) : ExternalResource() {
|
||||
replicaIds.map { mockNet.baseDirectory(mockNet.nextNodeId + it) },
|
||||
CordaX500Name("Custom Notary", "Zurich", "CH"))
|
||||
|
||||
val networkParameters = NetworkParametersCopier(testNetworkParameters(listOf(NotaryInfo(notaryIdentity, true))))
|
||||
val notaryConfig = mock<NotaryConfig> {
|
||||
whenever(it.custom).thenReturn(true)
|
||||
whenever(it.isClusterConfig).thenReturn(true)
|
||||
whenever(it.validating).thenReturn(true)
|
||||
}
|
||||
val networkParameters = NetworkParametersCopier(testNetworkParameters(listOf(NotaryInfo(notaryIdentity, true))))
|
||||
val notaryConfig = mock<NotaryConfig> {
|
||||
whenever(it.custom).thenReturn(true)
|
||||
whenever(it.isClusterConfig).thenReturn(true)
|
||||
whenever(it.validating).thenReturn(true)
|
||||
whenever(it.className).thenReturn(TestNotaryService::class.java.name)
|
||||
}
|
||||
|
||||
val notaryNodes = (0 until clusterSize).map {
|
||||
mockNet.createUnstartedNode(InternalMockNodeParameters(configOverrides = {
|
||||
@ -191,8 +193,7 @@ class TimedFlowTests {
|
||||
}.bufferUntilSubscribed().toBlocking().toFuture()
|
||||
}
|
||||
|
||||
@CordaService
|
||||
private class TestNotaryService(override val services: AppServiceHub, override val notaryIdentityKey: PublicKey) : TrustedAuthorityNotaryService() {
|
||||
private class TestNotaryService(override val services: ServiceHubInternal, override val notaryIdentityKey: PublicKey) : TrustedAuthorityNotaryService() {
|
||||
override val uniquenessProvider = mock<UniquenessProvider>()
|
||||
override fun createServiceFlow(otherPartySession: FlowSession): FlowLogic<Void?> = TestNotaryFlow(otherPartySession, this)
|
||||
override fun start() {}
|
||||
|
@ -8,7 +8,7 @@ import net.corda.core.identity.Party
|
||||
import net.corda.core.identity.PartyAndCertificate
|
||||
import net.corda.core.node.services.UnknownAnonymousPartyException
|
||||
import net.corda.node.internal.configureDatabase
|
||||
import net.corda.node.utilities.TestingNamedCacheFactory
|
||||
import net.corda.testing.internal.TestingNamedCacheFactory
|
||||
import net.corda.nodeapi.internal.crypto.CertificateType
|
||||
import net.corda.nodeapi.internal.crypto.X509Utilities
|
||||
import net.corda.nodeapi.internal.crypto.x509Certificates
|
||||
|
@ -5,7 +5,7 @@ import net.corda.core.utilities.loggerFor
|
||||
import net.corda.node.internal.configureDatabase
|
||||
import net.corda.node.services.schema.NodeSchemaService
|
||||
import net.corda.node.utilities.AppendOnlyPersistentMap
|
||||
import net.corda.node.utilities.TestingNamedCacheFactory
|
||||
import net.corda.testing.internal.TestingNamedCacheFactory
|
||||
import net.corda.nodeapi.internal.persistence.DatabaseConfig
|
||||
import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties
|
||||
import org.junit.After
|
||||
|
@ -9,7 +9,7 @@ import net.corda.core.toFuture
|
||||
import net.corda.core.transactions.SignedTransaction
|
||||
import net.corda.node.internal.configureDatabase
|
||||
import net.corda.node.services.transactions.PersistentUniquenessProvider
|
||||
import net.corda.node.utilities.TestingNamedCacheFactory
|
||||
import net.corda.testing.internal.TestingNamedCacheFactory
|
||||
import net.corda.nodeapi.internal.persistence.CordaPersistence
|
||||
import net.corda.nodeapi.internal.persistence.DatabaseConfig
|
||||
import net.corda.testing.core.*
|
||||
@ -154,7 +154,8 @@ class DBTransactionStorageTests {
|
||||
}
|
||||
|
||||
private fun newTransactionStorage(cacheSizeBytesOverride: Long? = null) {
|
||||
transactionStorage = DBTransactionStorage(database, TestingNamedCacheFactory(cacheSizeBytesOverride ?: 1024))
|
||||
transactionStorage = DBTransactionStorage(database, TestingNamedCacheFactory(cacheSizeBytesOverride
|
||||
?: 1024))
|
||||
}
|
||||
|
||||
private fun assertTransactionIsRetrievable(transaction: SignedTransaction) {
|
||||
|
@ -9,7 +9,7 @@ import net.corda.finance.contracts.asset.Cash
|
||||
import net.corda.finance.flows.CashIssueFlow
|
||||
import net.corda.node.services.identity.PersistentIdentityService
|
||||
import net.corda.node.services.keys.E2ETestKeyManagementService
|
||||
import net.corda.node.utilities.TestingNamedCacheFactory
|
||||
import net.corda.testing.internal.TestingNamedCacheFactory
|
||||
import net.corda.testing.core.BOC_NAME
|
||||
import net.corda.testing.node.InMemoryMessagingNetwork
|
||||
import net.corda.testing.node.MockNetwork
|
||||
|
@ -15,7 +15,7 @@ import net.corda.core.node.services.vault.Sort
|
||||
import net.corda.core.utilities.getOrThrow
|
||||
import net.corda.node.internal.configureDatabase
|
||||
import net.corda.node.services.transactions.PersistentUniquenessProvider
|
||||
import net.corda.node.utilities.TestingNamedCacheFactory
|
||||
import net.corda.testing.internal.TestingNamedCacheFactory
|
||||
import net.corda.nodeapi.internal.persistence.CordaPersistence
|
||||
import net.corda.nodeapi.internal.persistence.DatabaseConfig
|
||||
import net.corda.testing.internal.LogHelper
|
||||
|
@ -10,20 +10,18 @@ import net.corda.core.schemas.PersistentState
|
||||
import net.corda.core.utilities.getOrThrow
|
||||
import net.corda.node.services.api.ServiceHubInternal
|
||||
import net.corda.node.services.schema.NodeSchemaService.NodeCoreV1
|
||||
import net.corda.node.services.schema.NodeSchemaService.NodeNotaryV1
|
||||
import net.corda.testing.driver.DriverParameters
|
||||
import net.corda.testing.driver.driver
|
||||
import net.corda.testing.driver.internal.InProcessImpl
|
||||
import net.corda.testing.internal.vault.DummyLinearStateSchemaV1
|
||||
import net.corda.testing.node.internal.cordappsForPackages
|
||||
import net.corda.testing.node.internal.InternalMockNetwork
|
||||
import net.corda.testing.node.internal.cordappsForPackages
|
||||
import org.hibernate.annotations.Cascade
|
||||
import org.hibernate.annotations.CascadeType
|
||||
import org.junit.Ignore
|
||||
import org.junit.Test
|
||||
import javax.persistence.*
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertFalse
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
class NodeSchemaServiceTest {
|
||||
@ -47,7 +45,6 @@ class NodeSchemaServiceTest {
|
||||
|
||||
// check against NodeCore schemas
|
||||
assertTrue(schemaService.schemaOptions.containsKey(NodeCoreV1))
|
||||
assertFalse(schemaService.schemaOptions.containsKey(NodeNotaryV1))
|
||||
mockNet.stopNodes()
|
||||
}
|
||||
|
||||
@ -57,9 +54,8 @@ class NodeSchemaServiceTest {
|
||||
val mockNotaryNode = mockNet.notaryNodes.first()
|
||||
val schemaService = mockNotaryNode.services.schemaService
|
||||
|
||||
// check against NodeCore + NodeNotary Schemas
|
||||
// check against NodeCore Schema
|
||||
assertTrue(schemaService.schemaOptions.containsKey(NodeCoreV1))
|
||||
assertTrue(schemaService.schemaOptions.containsKey(NodeNotaryV1))
|
||||
mockNet.stopNodes()
|
||||
}
|
||||
|
||||
@ -97,7 +93,6 @@ class NodeSchemaServiceTest {
|
||||
val mappedSchemas = result.returnValue.getOrThrow()
|
||||
// check against NodeCore schemas
|
||||
assertTrue(mappedSchemas.contains(NodeCoreV1.name))
|
||||
assertFalse(mappedSchemas.contains(NodeNotaryV1.name)) // still gets loaded due TODO restriction
|
||||
}
|
||||
|
||||
}
|
||||
@ -107,9 +102,8 @@ class NodeSchemaServiceTest {
|
||||
driver(DriverParameters(startNodesInProcess = true)) {
|
||||
val notary = defaultNotaryNode.getOrThrow()
|
||||
val mappedSchemas = notary.rpc.startFlow(::MappedSchemasFlow).returnValue.getOrThrow()
|
||||
// check against NodeCore + NodeNotary Schemas
|
||||
// check against NodeCore Schema
|
||||
assertTrue(mappedSchemas.contains(NodeCoreV1.name))
|
||||
assertTrue(mappedSchemas.contains(NodeNotaryV1.name))
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -37,7 +37,7 @@ class MaxTransactionSizeTests {
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
mockNet = MockNetwork(listOf("net.corda.testing.contracts", "net.corda.node.services.transactions"), networkParameters = testNetworkParameters(maxTransactionSize = 3_000_000))
|
||||
mockNet = MockNetwork(listOf("net.corda.testing.contracts"), networkParameters = testNetworkParameters(maxTransactionSize = 3_000_000))
|
||||
aliceNode = mockNet.createNode(MockNodeParameters(legalName = ALICE_NAME))
|
||||
bobNode = mockNet.createNode(MockNodeParameters(legalName = BOB_NAME))
|
||||
notaryNode = mockNet.defaultNotaryNode
|
||||
|
@ -10,7 +10,7 @@ import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.internal.notary.NotaryInternalException
|
||||
import net.corda.node.internal.configureDatabase
|
||||
import net.corda.node.services.schema.NodeSchemaService
|
||||
import net.corda.node.utilities.TestingNamedCacheFactory
|
||||
import net.corda.testing.internal.TestingNamedCacheFactory
|
||||
import net.corda.nodeapi.internal.persistence.CordaPersistence
|
||||
import net.corda.nodeapi.internal.persistence.DatabaseConfig
|
||||
import net.corda.testing.core.SerializationEnvironmentRule
|
||||
@ -39,7 +39,7 @@ class PersistentUniquenessProviderTests {
|
||||
@Before
|
||||
fun setUp() {
|
||||
LogHelper.setLevel(PersistentUniquenessProvider::class)
|
||||
database = configureDatabase(makeTestDataSourceProperties(), DatabaseConfig(runMigration = true), { null }, { null }, NodeSchemaService(includeNotarySchemas = true))
|
||||
database = configureDatabase(makeTestDataSourceProperties(), DatabaseConfig(runMigration = true), { null }, { null }, NodeSchemaService(extraSchemas = setOf(NodeNotarySchemaV1)))
|
||||
}
|
||||
|
||||
@After
|
||||
|
@ -82,7 +82,7 @@ class VaultSoftLockManagerTest {
|
||||
private val mockVault = rigorousMock<VaultServiceInternal>().also {
|
||||
doNothing().whenever(it).softLockRelease(any(), anyOrNull())
|
||||
}
|
||||
private val mockNet = InternalMockNetwork(cordappsForAllNodes = cordappsForPackages(ContractImpl::class.packageName), defaultFactory = { args, _ ->
|
||||
private val mockNet = InternalMockNetwork(cordappsForAllNodes = cordappsForPackages(ContractImpl::class.packageName), defaultFactory = { args ->
|
||||
object : InternalMockNetwork.MockNode(args) {
|
||||
override fun makeVaultService(keyManagementService: KeyManagementService, services: ServicesForResolution, database: CordaPersistence): VaultServiceInternal {
|
||||
val node = this
|
||||
|
@ -4,10 +4,8 @@ apply plugin: 'java'
|
||||
apply plugin: 'kotlin'
|
||||
apply plugin: 'idea'
|
||||
apply plugin: 'net.corda.plugins.quasar-utils'
|
||||
apply plugin: 'net.corda.plugins.publish-utils'
|
||||
apply plugin: 'net.corda.plugins.cordapp'
|
||||
apply plugin: 'net.corda.plugins.cordformation'
|
||||
apply plugin: 'maven-publish'
|
||||
|
||||
configurations {
|
||||
integrationTestCompile.extendsFrom testCompile
|
||||
@ -16,7 +14,6 @@ configurations {
|
||||
|
||||
dependencies {
|
||||
compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
|
||||
testCompile "junit:junit:$junit_version"
|
||||
|
||||
// Corda integration dependencies
|
||||
cordaCompile project(path: ":node:capsule", configuration: 'runtimeArtifacts')
|
||||
@ -24,26 +21,11 @@ dependencies {
|
||||
cordaCompile project(':core')
|
||||
cordaCompile project(':client:jfx')
|
||||
cordaCompile project(':client:rpc')
|
||||
cordaCompile project(':node-driver')
|
||||
}
|
||||
cordaCompile project(':test-utils')
|
||||
|
||||
idea {
|
||||
module {
|
||||
downloadJavadoc = true // defaults to false
|
||||
downloadSources = true
|
||||
}
|
||||
}
|
||||
|
||||
publishing {
|
||||
publications {
|
||||
jarAndSources(MavenPublication) {
|
||||
from components.java
|
||||
artifactId 'notarydemo'
|
||||
|
||||
artifact sourceJar
|
||||
artifact javadocJar
|
||||
}
|
||||
}
|
||||
// Notary implementations
|
||||
cordapp project(':experimental:notary-raft')
|
||||
cordapp project(':experimental:notary-bft-smart')
|
||||
}
|
||||
|
||||
task deployNodes(dependsOn: ['deployNodesSingle', 'deployNodesRaft', 'deployNodesBFT', 'deployNodesCustom'])
|
||||
@ -99,9 +81,11 @@ task deployNodesCustom(type: Cordform, dependsOn: 'jar') {
|
||||
}
|
||||
|
||||
task deployNodesRaft(type: Cordform, dependsOn: 'jar') {
|
||||
def className = "net.corda.notary.raft.RaftNotaryService"
|
||||
directory file("$buildDir/nodes/nodesRaft")
|
||||
nodeDefaults {
|
||||
extraConfig = [h2Settings: [address: "localhost:0"]]
|
||||
cordapp project(':experimental:notary-raft')
|
||||
}
|
||||
node {
|
||||
name "O=Alice Corp,L=Madrid,C=ES"
|
||||
@ -124,7 +108,8 @@ task deployNodesRaft(type: Cordform, dependsOn: 'jar') {
|
||||
serviceLegalName: "O=Raft,L=Zurich,C=CH",
|
||||
raft: [
|
||||
nodeAddress: "localhost:10008"
|
||||
]
|
||||
],
|
||||
className: className
|
||||
]
|
||||
}
|
||||
node {
|
||||
@ -140,7 +125,8 @@ task deployNodesRaft(type: Cordform, dependsOn: 'jar') {
|
||||
raft: [
|
||||
nodeAddress: "localhost:10012",
|
||||
clusterAddresses: ["localhost:10008"]
|
||||
]
|
||||
],
|
||||
className: className
|
||||
]
|
||||
}
|
||||
node {
|
||||
@ -156,16 +142,19 @@ task deployNodesRaft(type: Cordform, dependsOn: 'jar') {
|
||||
raft: [
|
||||
nodeAddress: "localhost:10016",
|
||||
clusterAddresses: ["localhost:10008"]
|
||||
]
|
||||
],
|
||||
className: className
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
task deployNodesBFT(type: Cordform, dependsOn: 'jar') {
|
||||
def clusterAddresses = ["localhost:11000", "localhost:11010", "localhost:11020", "localhost:11030"]
|
||||
def className = "net.corda.notary.bftsmart.BftSmartNotaryService"
|
||||
directory file("$buildDir/nodes/nodesBFT")
|
||||
nodeDefaults {
|
||||
extraConfig = [h2Settings: [address: "localhost:0"]]
|
||||
cordapp project(':experimental:notary-bft-smart')
|
||||
}
|
||||
node {
|
||||
name "O=Alice Corp,L=Madrid,C=ES"
|
||||
@ -189,7 +178,8 @@ task deployNodesBFT(type: Cordform, dependsOn: 'jar') {
|
||||
bftSMaRt: [
|
||||
replicaId: 0,
|
||||
clusterAddresses: clusterAddresses
|
||||
]
|
||||
],
|
||||
className: className
|
||||
]
|
||||
}
|
||||
node {
|
||||
@ -203,9 +193,10 @@ task deployNodesBFT(type: Cordform, dependsOn: 'jar') {
|
||||
validating: false,
|
||||
serviceLegalName: "O=BFT,L=Zurich,C=CH",
|
||||
bftSMaRt: [
|
||||
replicaId: 0,
|
||||
replicaId: 1,
|
||||
clusterAddresses: clusterAddresses
|
||||
]
|
||||
],
|
||||
className: className
|
||||
]
|
||||
}
|
||||
node {
|
||||
@ -219,9 +210,10 @@ task deployNodesBFT(type: Cordform, dependsOn: 'jar') {
|
||||
validating: false,
|
||||
serviceLegalName: "O=BFT,L=Zurich,C=CH",
|
||||
bftSMaRt: [
|
||||
replicaId: 0,
|
||||
replicaId: 2,
|
||||
clusterAddresses: clusterAddresses
|
||||
]
|
||||
],
|
||||
className: className
|
||||
]
|
||||
}
|
||||
node {
|
||||
@ -235,9 +227,10 @@ task deployNodesBFT(type: Cordform, dependsOn: 'jar') {
|
||||
validating: false,
|
||||
serviceLegalName: "O=BFT,L=Zurich,C=CH",
|
||||
bftSMaRt: [
|
||||
replicaId: 0,
|
||||
replicaId: 3,
|
||||
clusterAddresses: clusterAddresses
|
||||
]
|
||||
],
|
||||
className: className
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -30,6 +30,8 @@ include 'experimental:flow-worker'
|
||||
include 'experimental:ha-testing'
|
||||
include 'experimental:corda-utils'
|
||||
include 'experimental:rpc-worker'
|
||||
include 'experimental:notary-raft'
|
||||
include 'experimental:notary-bft-smart'
|
||||
include 'jdk8u-deterministic'
|
||||
include 'test-common'
|
||||
include 'test-cli'
|
||||
|
@ -25,6 +25,8 @@ sourceSets {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
// Bundling in the Raft notary service for tests involving distributed notaries
|
||||
compile project(':experimental:notary-raft')
|
||||
compile project(':test-utils')
|
||||
|
||||
// Integration test helpers
|
||||
|
@ -497,6 +497,7 @@ class DriverDSLImpl(
|
||||
val config = NotaryConfig(
|
||||
validating = spec.validating,
|
||||
serviceLegalName = spec.name,
|
||||
className = "net.corda.notary.raft.RaftNotaryService",
|
||||
raft = RaftConfig(nodeAddress = nodeAddress, clusterAddresses = clusterAddresses))
|
||||
return config.toConfigMap()
|
||||
}
|
||||
|
@ -28,14 +28,15 @@ import net.corda.core.utilities.contextLogger
|
||||
import net.corda.core.utilities.hours
|
||||
import net.corda.core.utilities.seconds
|
||||
import net.corda.node.VersionInfo
|
||||
import net.corda.node.cordapp.CordappLoader
|
||||
import net.corda.node.internal.AbstractNode
|
||||
import net.corda.node.internal.InitiatedFlowFactory
|
||||
import net.corda.node.internal.cordapp.JarScanningCordappLoader
|
||||
import net.corda.node.services.api.FlowStarter
|
||||
import net.corda.node.services.api.ServiceHubInternal
|
||||
import net.corda.node.services.api.StartedNodeServices
|
||||
import net.corda.node.services.config.*
|
||||
import net.corda.node.services.config.FlowTimeoutConfiguration
|
||||
import net.corda.node.services.config.NodeConfiguration
|
||||
import net.corda.node.services.config.NotaryConfig
|
||||
import net.corda.node.services.config.VerifierType
|
||||
import net.corda.node.services.identity.PersistentIdentityService
|
||||
import net.corda.node.services.keys.E2ETestKeyManagementService
|
||||
import net.corda.node.services.keys.KeyManagementServiceInternal
|
||||
@ -43,9 +44,6 @@ import net.corda.node.services.messaging.Message
|
||||
import net.corda.node.services.messaging.MessagingService
|
||||
import net.corda.node.services.persistence.NodeAttachmentService
|
||||
import net.corda.node.services.statemachine.StateMachineManager
|
||||
import net.corda.node.services.transactions.BFTNonValidatingNotaryService
|
||||
import net.corda.node.services.transactions.BFTSMaRt
|
||||
import net.corda.node.services.transactions.InMemoryTransactionVerifierService
|
||||
import net.corda.node.utilities.AffinityExecutor.ServiceAffinityExecutor
|
||||
import net.corda.node.utilities.DefaultNamedCacheFactory
|
||||
import net.corda.nodeapi.internal.DevIdentityGenerator
|
||||
@ -69,7 +67,6 @@ import java.math.BigInteger
|
||||
import java.nio.file.Path
|
||||
import java.nio.file.Paths
|
||||
import java.security.KeyPair
|
||||
import java.security.PublicKey
|
||||
import java.time.Clock
|
||||
import java.util.concurrent.TimeUnit
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
@ -149,7 +146,7 @@ open class InternalMockNetwork(defaultParameters: MockNetworkParameters = MockNe
|
||||
val notarySpecs: List<MockNetworkNotarySpec> = defaultParameters.notarySpecs,
|
||||
val testDirectory: Path = Paths.get("build", getTimestampAsDirectoryName()),
|
||||
val networkParameters: NetworkParameters = testNetworkParameters(),
|
||||
val defaultFactory: (MockNodeArgs, CordappLoader?) -> MockNode = { args, cordappLoader -> cordappLoader?.let { MockNode(args, it) } ?: MockNode(args) },
|
||||
val defaultFactory: (MockNodeArgs) -> MockNode = { args -> MockNode(args) },
|
||||
val cordappsForAllNodes: Set<TestCorDapp> = emptySet(),
|
||||
val autoVisibleNodes: Boolean = true) : AutoCloseable {
|
||||
init {
|
||||
@ -276,12 +273,11 @@ open class InternalMockNetwork(defaultParameters: MockNetworkParameters = MockNe
|
||||
}
|
||||
}
|
||||
|
||||
open class MockNode(args: MockNodeArgs, cordappLoader: CordappLoader = JarScanningCordappLoader.fromDirectories(args.config.cordappDirectories, args.version)) : AbstractNode<TestStartedNode>(
|
||||
open class MockNode(args: MockNodeArgs) : AbstractNode<TestStartedNode>(
|
||||
args.config,
|
||||
TestClock(Clock.systemUTC()),
|
||||
DefaultNamedCacheFactory(),
|
||||
args.version,
|
||||
cordappLoader,
|
||||
args.network.getServerThread(args.id),
|
||||
args.network.busyLatch
|
||||
) {
|
||||
@ -424,27 +420,13 @@ open class InternalMockNetwork(defaultParameters: MockNetworkParameters = MockNe
|
||||
var acceptableLiveFiberCountOnStop: Int = 0
|
||||
|
||||
override fun acceptableLiveFiberCountOnStop(): Int = acceptableLiveFiberCountOnStop
|
||||
|
||||
override fun makeBFTCluster(notaryKey: PublicKey, bftSMaRtConfig: BFTSMaRtConfiguration): BFTSMaRt.Cluster {
|
||||
return object : BFTSMaRt.Cluster {
|
||||
override fun waitUntilAllReplicasHaveInitialized() {
|
||||
val clusterNodes = mockNet.nodes.map { it.started!! }.filter { notaryKey in it.info.legalIdentities.map { it.owningKey } }
|
||||
if (clusterNodes.size != bftSMaRtConfig.clusterAddresses.size) {
|
||||
throw IllegalStateException("Unable to enumerate all nodes in BFT cluster.")
|
||||
}
|
||||
clusterNodes.forEach {
|
||||
(it.notaryService as BFTNonValidatingNotaryService).waitUntilReplicaHasInitialized()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun createUnstartedNode(parameters: InternalMockNodeParameters = InternalMockNodeParameters()): MockNode {
|
||||
return createUnstartedNode(parameters, defaultFactory)
|
||||
}
|
||||
|
||||
fun createUnstartedNode(parameters: InternalMockNodeParameters = InternalMockNodeParameters(), nodeFactory: (MockNodeArgs, CordappLoader?) -> MockNode): MockNode {
|
||||
fun createUnstartedNode(parameters: InternalMockNodeParameters = InternalMockNodeParameters(), nodeFactory: (MockNodeArgs) -> MockNode): MockNode {
|
||||
return createNodeImpl(parameters, nodeFactory, false)
|
||||
}
|
||||
|
||||
@ -453,11 +435,11 @@ open class InternalMockNetwork(defaultParameters: MockNetworkParameters = MockNe
|
||||
}
|
||||
|
||||
/** Like the other [createNode] but takes a [nodeFactory] and propagates its [MockNode] subtype. */
|
||||
fun createNode(parameters: InternalMockNodeParameters = InternalMockNodeParameters(), nodeFactory: (MockNodeArgs, CordappLoader?) -> MockNode): TestStartedNode {
|
||||
fun createNode(parameters: InternalMockNodeParameters = InternalMockNodeParameters(), nodeFactory: (MockNodeArgs) -> MockNode): TestStartedNode {
|
||||
return uncheckedCast(createNodeImpl(parameters, nodeFactory, true).started)!!
|
||||
}
|
||||
|
||||
private fun createNodeImpl(parameters: InternalMockNodeParameters, nodeFactory: (MockNodeArgs, CordappLoader?) -> MockNode, start: Boolean): MockNode {
|
||||
private fun createNodeImpl(parameters: InternalMockNodeParameters, nodeFactory: (MockNodeArgs) -> MockNode, start: Boolean): MockNode {
|
||||
val id = parameters.forcedID ?: nextNodeId++
|
||||
val baseDirectory = baseDirectory(id)
|
||||
val certificatesDirectory = baseDirectory / "certificates"
|
||||
@ -475,7 +457,7 @@ open class InternalMockNetwork(defaultParameters: MockNetworkParameters = MockNe
|
||||
val cordappDirectories = sharedCorDappsDirectories + TestCordappDirectories.cached(cordapps)
|
||||
doReturn(cordappDirectories).whenever(config).cordappDirectories
|
||||
|
||||
val node = nodeFactory(MockNodeArgs(config, this, id, parameters.entropyRoot, parameters.version), JarScanningCordappLoader.fromDirectories(cordappDirectories, parameters.version))
|
||||
val node = nodeFactory(MockNodeArgs(config, this, id, parameters.entropyRoot, parameters.version))
|
||||
_nodes += node
|
||||
if (start) {
|
||||
node.start()
|
||||
@ -483,7 +465,7 @@ open class InternalMockNetwork(defaultParameters: MockNetworkParameters = MockNe
|
||||
return node
|
||||
}
|
||||
|
||||
fun restartNode(node: TestStartedNode, nodeFactory: (MockNodeArgs, CordappLoader?) -> MockNode): TestStartedNode {
|
||||
fun restartNode(node: TestStartedNode, nodeFactory: (MockNodeArgs) -> MockNode): TestStartedNode {
|
||||
node.internals.disableDBCloseOnStop()
|
||||
node.dispose()
|
||||
return createNode(
|
||||
|
@ -1,4 +1,4 @@
|
||||
package net.corda.node.utilities
|
||||
package net.corda.testing.internal
|
||||
|
||||
import com.codahale.metrics.MetricRegistry
|
||||
import com.github.benmanes.caffeine.cache.Cache
|
||||
@ -9,6 +9,7 @@ import net.corda.core.internal.buildNamed
|
||||
import net.corda.core.serialization.SingletonSerializeAsToken
|
||||
import net.corda.node.services.config.MB
|
||||
import net.corda.node.services.config.NodeConfiguration
|
||||
import net.corda.node.utilities.NamedCacheFactory
|
||||
|
||||
class TestingNamedCacheFactory private constructor(private val sizeOverride: Long, private val metricRegistry: MetricRegistry?, private val nodeConfiguration: NodeConfiguration?) : NamedCacheFactory, SingletonSerializeAsToken() {
|
||||
constructor(sizeOverride: Long = 1024) : this(sizeOverride, null, null)
|
Loading…
x
Reference in New Issue
Block a user