Merge remote-tracking branch 'open/master' into andrius/merge-10-10

This commit is contained in:
Andrius Dagys 2018-10-10 10:45:21 +01:00
commit b7d59785e7
66 changed files with 648 additions and 374 deletions

6
.idea/compiler.xml generated
View File

@ -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>

View File

@ -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'

View File

@ -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>?
}

View File

@ -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 }
}
}
}

View File

@ -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 }
}

View File

@ -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 }
}

View 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'
}

View File

@ -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 {

View File

@ -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

View File

@ -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 }

View File

@ -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"
}

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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()

View File

@ -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

View 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'
}

View File

@ -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()
}
}

View File

@ -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)
}

View File

@ -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

View File

@ -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"
}

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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

View File

@ -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()) }) }

View File

@ -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}"

View File

@ -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,

View File

@ -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

View File

@ -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

View File

@ -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) })
}

View File

@ -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()

View File

@ -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

View File

@ -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,

View File

@ -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> {

View File

@ -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
)
}
}

View File

@ -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) {

View File

@ -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" }

View File

@ -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()
}
}

View File

@ -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()
}
}

View File

@ -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"
}

View File

@ -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() {}
}

View File

@ -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"/>

View File

@ -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>

View File

@ -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>

View File

@ -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 {

View File

@ -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)

View File

@ -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() {}

View File

@ -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

View File

@ -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

View File

@ -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) {

View File

@ -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

View File

@ -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

View File

@ -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))
}
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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
]
}
}

View File

@ -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'

View File

@ -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

View File

@ -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()
}

View File

@ -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(

View File

@ -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)