Cleaned up notary configuration by introducing a notary config option.

extraAdvertisedServiceIds is no longer used for this.
This commit is contained in:
Shams Asari
2017-10-05 12:27:45 +01:00
parent 83f37417ae
commit 727cd0e55c
88 changed files with 600 additions and 716 deletions

View File

@ -7,14 +7,14 @@ import net.corda.core.flows.StartableByRPC
import net.corda.core.internal.concurrent.transpose
import net.corda.core.messaging.startFlow
import net.corda.core.utilities.OpaqueBytes
import net.corda.core.utilities.getOrThrow
import net.corda.core.utilities.minutes
import net.corda.finance.DOLLARS
import net.corda.finance.flows.CashIssueFlow
import net.corda.finance.flows.CashPaymentFlow
import net.corda.node.services.FlowPermissions.Companion.startFlowPermission
import net.corda.nodeapi.internal.ServiceInfo
import net.corda.node.services.transactions.SimpleNotaryService
import net.corda.nodeapi.User
import net.corda.testing.DUMMY_NOTARY
import net.corda.testing.chooseIdentity
import net.corda.testing.driver.NodeHandle
import net.corda.testing.driver.driver
@ -103,10 +103,10 @@ class NodePerformanceTests {
@Test
fun `self pay rate`() {
driver(startNodesInProcess = true) {
val a = startNode(
rpcUsers = listOf(User("A", "A", setOf(startFlowPermission<CashIssueFlow>(), startFlowPermission<CashPaymentFlow>()))),
advertisedServices = setOf(ServiceInfo(SimpleNotaryService.type))
).get()
val a = startNotaryNode(
DUMMY_NOTARY.name,
rpcUsers = listOf(User("A", "A", setOf(startFlowPermission<CashIssueFlow>(), startFlowPermission<CashPaymentFlow>())))
).getOrThrow()
a as NodeHandle.InProcess
val metricRegistry = startReporter(shutdownManager, a.node.services.monitoringService.metrics)
a.rpcClientToNode().use("A", "A") { connection ->

View File

@ -18,9 +18,7 @@ import net.corda.core.utilities.getOrThrow
import net.corda.core.utilities.loggerFor
import net.corda.node.internal.cordapp.CordappLoader
import net.corda.node.internal.cordapp.CordappProviderImpl
import net.corda.node.services.transactions.SimpleNotaryService
import net.corda.nodeapi.User
import net.corda.nodeapi.internal.ServiceInfo
import net.corda.testing.DUMMY_BANK_A
import net.corda.testing.DUMMY_NOTARY
import net.corda.testing.TestDependencyInjectionBase
@ -120,7 +118,7 @@ class AttachmentLoadingTests : TestDependencyInjectionBase() {
val nodes = listOf(
startNode(providedName = bankAName, rpcUsers = listOf(adminUser)),
startNode(providedName = bankBName, rpcUsers = listOf(adminUser)),
startNode(providedName = notaryName, rpcUsers = listOf(adminUser), advertisedServices = setOf(ServiceInfo(SimpleNotaryService.type)))
startNotaryNode(providedName = notaryName, rpcUsers = listOf(adminUser), validating = false)
).transpose().getOrThrow() // Wait for all nodes to start up.
nodes.forEach { it.rpc.waitUntilNetworkReady().getOrThrow() }
return nodes

View File

@ -10,6 +10,7 @@ import net.corda.core.flows.NotaryException
import net.corda.core.flows.NotaryFlow
import net.corda.core.identity.CordaX500Name
import net.corda.core.identity.Party
import net.corda.core.internal.deleteIfExists
import net.corda.core.internal.div
import net.corda.core.transactions.SignedTransaction
import net.corda.core.transactions.TransactionBuilder
@ -18,11 +19,11 @@ import net.corda.core.utilities.Try
import net.corda.core.utilities.getOrThrow
import net.corda.node.internal.StartedNode
import net.corda.node.services.config.BFTSMaRtConfiguration
import net.corda.node.services.config.NotaryConfig
import net.corda.node.services.transactions.BFTNonValidatingNotaryService
import net.corda.node.services.transactions.minClusterSize
import net.corda.node.services.transactions.minCorrectReplicas
import net.corda.node.utilities.ServiceIdentityGenerator
import net.corda.nodeapi.internal.ServiceInfo
import net.corda.testing.chooseIdentity
import net.corda.testing.contracts.DummyContract
import net.corda.testing.dummyCommand
@ -30,14 +31,13 @@ import net.corda.testing.getDefaultNotary
import net.corda.testing.node.MockNetwork
import org.junit.After
import org.junit.Test
import java.nio.file.Files
import java.nio.file.Paths
import kotlin.test.assertEquals
import kotlin.test.assertTrue
class BFTNotaryServiceTests {
companion object {
private val serviceType = BFTNonValidatingNotaryService.type
private val clusterName = CordaX500Name(serviceType.id, "BFT", "Zurich", "CH")
private val clusterName = CordaX500Name(BFTNonValidatingNotaryService.id, "BFT", "Zurich", "CH")
}
private val mockNet = MockNetwork()
@ -49,20 +49,17 @@ class BFTNotaryServiceTests {
}
private fun bftNotaryCluster(clusterSize: Int, exposeRaces: Boolean = false) {
Files.deleteIfExists("config" / "currentView") // XXX: Make config object warn if this exists?
(Paths.get("config") / "currentView").deleteIfExists() // XXX: Make config object warn if this exists?
val replicaIds = (0 until clusterSize)
ServiceIdentityGenerator.generateToDisk(
replicaIds.map { mockNet.baseDirectory(mockNet.nextNodeId + it) },
clusterName)
val bftNotaryService = ServiceInfo(serviceType, clusterName)
val notaryClusterAddresses = replicaIds.map { NetworkHostAndPort("localhost", 11000 + it * 10) }
val clusterAddresses = replicaIds.map { NetworkHostAndPort("localhost", 11000 + it * 10) }
replicaIds.forEach { replicaId ->
mockNet.createNode(
advertisedServices = bftNotaryService,
configOverrides = {
whenever(it.bftSMaRt).thenReturn(BFTSMaRtConfiguration(replicaId, false, exposeRaces))
whenever(it.notaryClusterAddresses).thenReturn(notaryClusterAddresses)
})
mockNet.createNode(configOverrides = {
val notary = NotaryConfig(validating = false, bftSMaRt = BFTSMaRtConfiguration(replicaId, clusterAddresses, exposeRaces = exposeRaces))
whenever(it.notary).thenReturn(notary)
})
}
mockNet.runNetwork() // Exchange initial network map registration messages.
}

View File

@ -39,10 +39,9 @@ class DistributedServiceTests : DriverBasedTest() {
)
val aliceFuture = startNode(providedName = ALICE.name, rpcUsers = listOf(testUser))
val notariesFuture = startNotaryCluster(
DUMMY_NOTARY.name.copy(commonName = RaftValidatingNotaryService.type.id),
DUMMY_NOTARY.name.copy(commonName = RaftValidatingNotaryService.id),
rpcUsers = listOf(testUser),
clusterSize = clusterSize,
type = RaftValidatingNotaryService.type
clusterSize = clusterSize
)
alice = aliceFuture.get()

View File

@ -14,8 +14,6 @@ import net.corda.core.utilities.getOrThrow
import net.corda.node.internal.StartedNode
import net.corda.node.services.transactions.RaftValidatingNotaryService
import net.corda.testing.*
import net.corda.testing.DUMMY_BANK_A
import net.corda.testing.chooseIdentity
import net.corda.testing.contracts.DummyContract
import net.corda.testing.node.NodeBasedTest
import org.junit.After
@ -26,7 +24,7 @@ import kotlin.test.assertEquals
import kotlin.test.assertFailsWith
class RaftNotaryServiceTests : NodeBasedTest() {
private val notaryName = CordaX500Name(RaftValidatingNotaryService.type.id, "RAFT Notary Service", "London", "GB")
private val notaryName = CordaX500Name(RaftValidatingNotaryService.id, "RAFT Notary Service", "London", "GB")
@Before
fun setup() {

View File

@ -17,9 +17,6 @@ import net.corda.core.utilities.seconds
import net.corda.node.internal.StartedNode
import net.corda.node.services.messaging.*
import net.corda.node.services.transactions.RaftValidatingNotaryService
import net.corda.node.services.transactions.SimpleNotaryService
import net.corda.node.utilities.ServiceIdentityGenerator
import net.corda.nodeapi.internal.ServiceInfo
import net.corda.testing.*
import net.corda.testing.node.NodeBasedTest
import org.assertj.core.api.Assertions.assertThat
@ -32,8 +29,7 @@ import java.util.concurrent.atomic.AtomicInteger
class P2PMessagingTest : NodeBasedTest() {
private companion object {
val DISTRIBUTED_SERVICE_NAME = CordaX500Name(RaftValidatingNotaryService.type.id, "DistributedService", "London", "GB")
val SERVICE_2_NAME = CordaX500Name(organisation = "Service 2", locality = "London", country = "GB")
val DISTRIBUTED_SERVICE_NAME = CordaX500Name(RaftValidatingNotaryService.id, "DistributedService", "London", "GB")
}
@Test
@ -49,46 +45,6 @@ class P2PMessagingTest : NodeBasedTest() {
startNodes().getOrThrow(timeout = startUpDuration * 3)
}
// https://github.com/corda/corda/issues/71
@Test
fun `communicating with a service running on the network map node`() {
startNetworkMapNode(advertisedServices = setOf(ServiceInfo(SimpleNotaryService.type)))
networkMapNode.respondWith("Hello")
val alice = startNode(ALICE.name).getOrThrow()
val serviceAddress = alice.services.networkMapCache.run {
val notaryParty = notaryIdentities.randomOrNull()!!
alice.network.getAddressOfParty(getPartyInfo(notaryParty)!!)
}
val received = alice.receiveFrom(serviceAddress).getOrThrow(10.seconds)
assertThat(received).isEqualTo("Hello")
}
// TODO Use a dummy distributed service
@Test
fun `communicating with a distributed service which the network map node is part of`() {
ServiceIdentityGenerator.generateToDisk(
listOf(DUMMY_MAP.name, SERVICE_2_NAME).map { baseDirectory(it) },
DISTRIBUTED_SERVICE_NAME)
val distributedService = ServiceInfo(RaftValidatingNotaryService.type, DISTRIBUTED_SERVICE_NAME)
val notaryClusterAddress = freeLocalHostAndPort()
startNetworkMapNode(
DUMMY_MAP.name,
advertisedServices = setOf(distributedService),
configOverrides = mapOf("notaryNodeAddress" to notaryClusterAddress.toString()))
val (serviceNode2, alice) = listOf(
startNode(
SERVICE_2_NAME,
advertisedServices = setOf(distributedService),
configOverrides = mapOf(
"notaryNodeAddress" to freeLocalHostAndPort().toString(),
"notaryClusterAddresses" to listOf(notaryClusterAddress.toString()))),
startNode(ALICE.name)
).transpose().getOrThrow()
assertAllNodesAreUsed(listOf(networkMapNode, serviceNode2), DISTRIBUTED_SERVICE_NAME, alice)
}
@Ignore
@Test
fun `communicating with a distributed service which we're part of`() {

View File

@ -18,8 +18,6 @@ import net.corda.core.transactions.TransactionBuilder
import net.corda.core.utilities.ProgressTracker
import net.corda.core.utilities.getOrThrow
import net.corda.node.services.FlowPermissions
import net.corda.node.services.transactions.SimpleNotaryService
import net.corda.nodeapi.internal.ServiceInfo
import net.corda.nodeapi.User
import net.corda.testing.DUMMY_NOTARY
import net.corda.testing.chooseIdentity
@ -38,8 +36,7 @@ class NodeStatePersistenceTests {
val user = User("mark", "dadada", setOf(FlowPermissions.startFlowPermission<SendMessageFlow>()))
val message = Message("Hello world!")
driver(isDebug = true, startNodesInProcess = isQuasarAgentSpecified()) {
startNode(providedName = DUMMY_NOTARY.name, advertisedServices = setOf(ServiceInfo(SimpleNotaryService.type))).getOrThrow()
startNotaryNode(DUMMY_NOTARY.name, validating = false).getOrThrow()
var nodeHandle = startNode(rpcUsers = listOf(user)).getOrThrow()
val nodeName = nodeHandle.nodeInfo.chooseIdentity().name
nodeHandle.rpcClientToNode().start(user.username, user.password).use {

View File

@ -1,7 +1,6 @@
package net.corda.node.internal
import com.codahale.metrics.MetricRegistry
import com.google.common.collect.Lists
import com.google.common.collect.MutableClassToInstanceMap
import com.google.common.util.concurrent.MoreExecutors
import net.corda.confidential.SwapIdentitiesFlow
@ -41,7 +40,9 @@ import net.corda.node.services.ContractUpgradeHandler
import net.corda.node.services.FinalityHandler
import net.corda.node.services.NotaryChangeHandler
import net.corda.node.services.api.*
import net.corda.node.services.config.BFTSMaRtConfiguration
import net.corda.node.services.config.NodeConfiguration
import net.corda.node.services.config.NotaryConfig
import net.corda.node.services.config.configureWithDevSSLCertificate
import net.corda.node.services.events.NodeSchedulerService
import net.corda.node.services.events.ScheduledActivityObserver
@ -66,7 +67,6 @@ import net.corda.node.services.vault.VaultSoftLockManager
import net.corda.node.utilities.*
import net.corda.node.utilities.AddOrRemove.ADD
import net.corda.nodeapi.internal.ServiceInfo
import net.corda.nodeapi.internal.ServiceType
import org.apache.activemq.artemis.utils.ReusableLatch
import org.slf4j.Logger
import rx.Observable
@ -456,7 +456,7 @@ abstract class AbstractNode(config: NodeConfiguration,
_services = ServiceHubInternalImpl(schemaService)
attachments = NodeAttachmentService(services.monitoringService.metrics)
cordappProvider.start(attachments)
legalIdentity = obtainIdentity()
legalIdentity = obtainIdentity(notaryConfig = null)
network = makeMessagingService(legalIdentity)
info = makeInfo(legalIdentity)
val networkMapCache = services.networkMapCache
@ -500,19 +500,10 @@ abstract class AbstractNode(config: NodeConfiguration,
}
/**
* A service entry contains the advertised [ServiceInfo] along with the service identity. The identity *name* is
* taken from the configuration or, if non specified, generated by combining the node's legal name and the service id.
* Used only for notary identities.
* Obtain the node's notary identity if it's configured to be one. If part of a distributed notary then this will be
* the distributed identity shared across all the nodes of the cluster.
*/
protected open fun getNotaryIdentity(): PartyAndCertificate? {
return advertisedServices.singleOrNull { it.type.isNotary() }?.let {
it.name?.let {
require(it.commonName != null) {"Common name in '$it' must not be null for notary service, use service type id as common name."}
require(ServiceType.parse(it.commonName!!).isNotary()) {"Common name for notary service in '$it' must be the notary service type id."}
}
obtainIdentity(it)
}
}
protected fun getNotaryIdentity(): PartyAndCertificate? = configuration.notary?.let { obtainIdentity(it) }
@VisibleForTesting
protected open fun acceptableLiveFiberCountOnStop(): Int = 0
@ -558,22 +549,14 @@ abstract class AbstractNode(config: NodeConfiguration,
}
private fun makeNetworkServices(network: MessagingService, networkMapCache: NetworkMapCacheInternal, tokenizableServices: MutableList<Any>) {
val serviceTypes = advertisedServices.map { it.type }
inNodeNetworkMapService = if (configuration.networkMapService == null) makeNetworkMapService(network, networkMapCache) else NullNetworkMapService
val notaryServiceType = serviceTypes.singleOrNull { it.isNotary() }
if (notaryServiceType != null) {
val service = makeCoreNotaryService(notaryServiceType)
if (service != null) {
service.apply {
tokenizableServices.add(this)
runOnStop += this::stop
start()
}
installCoreFlow(NotaryFlow.Client::class, service::createServiceFlow)
} else {
log.info("Notary type ${notaryServiceType.id} does not match any built-in notary types. " +
"It is expected to be loaded via a CorDapp")
}
configuration.notary?.let {
val notaryService = makeCoreNotaryService(it)
tokenizableServices.add(notaryService)
runOnStop += notaryService::stop
installCoreFlow(NotaryFlow.Client::class, notaryService::createServiceFlow)
log.info("Running core notary: ${notaryService.javaClass.name}")
notaryService.start()
}
}
@ -640,15 +623,33 @@ abstract class AbstractNode(config: NodeConfiguration,
abstract protected fun makeNetworkMapService(network: MessagingService, networkMapCache: NetworkMapCacheInternal): NetworkMapService
open protected fun makeCoreNotaryService(type: ServiceType): NotaryService? {
check(myNotaryIdentity != null) { "No notary identity initialized when creating a notary service" }
return when (type) {
SimpleNotaryService.type -> SimpleNotaryService(services, myNotaryIdentity!!.owningKey)
ValidatingNotaryService.type -> ValidatingNotaryService(services, myNotaryIdentity!!.owningKey)
RaftNonValidatingNotaryService.type -> RaftNonValidatingNotaryService(services, myNotaryIdentity!!.owningKey)
RaftValidatingNotaryService.type -> RaftValidatingNotaryService(services, myNotaryIdentity!!.owningKey)
BFTNonValidatingNotaryService.type -> BFTNonValidatingNotaryService(services, myNotaryIdentity!!.owningKey)
else -> null
private fun makeCoreNotaryService(notaryConfig: NotaryConfig): NotaryService {
val notaryKey = myNotaryIdentity?.owningKey ?: throw IllegalArgumentException("No notary identity initialized when creating a notary service")
return if (notaryConfig.validating) {
if (notaryConfig.raft != null) {
RaftValidatingNotaryService(services, notaryKey, notaryConfig.raft)
} else if (notaryConfig.bftSMaRt != null) {
throw IllegalArgumentException("Validating BFTSMaRt notary not supported")
} else {
ValidatingNotaryService(services, notaryKey)
}
} else {
if (notaryConfig.raft != null) {
RaftNonValidatingNotaryService(services, notaryKey, notaryConfig.raft)
} else if (notaryConfig.bftSMaRt != null) {
val cluster = makeBFTCluster(notaryKey, notaryConfig.bftSMaRt)
BFTNonValidatingNotaryService(services, notaryKey, notaryConfig.bftSMaRt, cluster)
} 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.")
}
}
}
@ -691,29 +692,32 @@ abstract class AbstractNode(config: NodeConfiguration,
protected abstract fun startMessagingService(rpcOps: RPCOps)
private fun obtainIdentity(serviceInfo: ServiceInfo? = null): PartyAndCertificate {
// Load the private identity key, creating it if necessary. The identity key is a long term well known key that
// is distributed to other peers and we use it (or a key signed by it) when we need to do something
// "permissioned". The identity file is what gets distributed and contains the node's legal name along with
// the public key. Obviously in a real system this would need to be a certificate chain of some kind to ensure
// the legal name is actually validated in some way.
private fun obtainIdentity(notaryConfig: NotaryConfig?): PartyAndCertificate {
val keyStore = KeyStoreWrapper(configuration.nodeKeystore, configuration.keyStorePassword)
val (id, name) = if (serviceInfo == null) {
// Create node identity if service info = null
val (id, singleName) = if (notaryConfig == null) {
// Node's main identity
Pair("identity", myLegalName)
} else {
val name = serviceInfo.name ?: myLegalName.copy(commonName = serviceInfo.type.id)
Pair(serviceInfo.type.id, name)
val notaryId = notaryConfig.run { NotaryService.constructId(validating, raft != null, bftSMaRt != null) }
if (notaryConfig.bftSMaRt == null && notaryConfig.raft == null) {
// Node's notary identity
Pair(notaryId, myLegalName.copy(commonName = notaryId))
} else {
// The node is part of a distributed notary whose identity must already be generated beforehand
Pair(notaryId, null)
}
}
// TODO: Integrate with Key management service?
val privateKeyAlias = "$id-private-key"
if (!keyStore.containsAlias(privateKeyAlias)) {
singleName ?: throw IllegalArgumentException(
"Unable to find in the key store the identity of the distributed notary ($id) the node is part of")
// TODO: Remove use of [ServiceIdentityGenerator.generateToDisk].
log.info("$privateKeyAlias not found in key store ${configuration.nodeKeystore}, generating fresh key!")
keyStore.signAndSaveNewKeyPair(name, privateKeyAlias, generateKeyPair())
keyStore.signAndSaveNewKeyPair(singleName, privateKeyAlias, generateKeyPair())
}
val (x509Cert, keys) = keyStore.certificateAndKeyPair(privateKeyAlias)
@ -726,7 +730,7 @@ abstract class AbstractNode(config: NodeConfiguration,
// We have to create the certificate chain for the composite key manually, this is because we don't have a keystore
// provider that understand compositeKey-privateKey combo. The cert chain is created using the composite key certificate +
// the tail of the private key certificates, as they are both signed by the same certificate chain.
Lists.asList(certificate, keyStore.getCertificateChain(privateKeyAlias).drop(1).toTypedArray())
listOf(certificate) + keyStore.getCertificateChain(privateKeyAlias).drop(1)
} else {
keyStore.getCertificateChain(privateKeyAlias).let {
check(it[0].toX509CertHolder() == x509Cert) { "Certificates from key store do not line up!" }
@ -736,8 +740,11 @@ abstract class AbstractNode(config: NodeConfiguration,
val nodeCert = certificates[0] as? X509Certificate ?: throw ConfigurationException("Node certificate must be an X.509 certificate")
val subject = CordaX500Name.build(nodeCert.subjectX500Principal)
if (subject != name)
throw ConfigurationException("The name '$name' for $id doesn't match what's in the key store: $subject")
// TODO Include the name of the distributed notary, which the node is part of, in the notary config so that we
// can cross-check the identity we get from the key store
if (singleName != null && subject != singleName) {
throw ConfigurationException("The name '$singleName' for $id doesn't match what's in the key store: $subject")
}
partyKeys += keys
return PartyAndCertificate(CertificateFactory.getInstance("X509").generateCertPath(certificates))

View File

@ -99,7 +99,7 @@ open class NodeStartup(val args: Array<String>) {
return
}
val startedNode = node.start()
printPluginsAndServices(startedNode.internals)
Node.printBasicNodeInfo("Loaded CorDapps", startedNode.internals.cordappProvider.cordapps.joinToString { it.name })
startedNode.internals.nodeReadyFuture.thenMatch({
val elapsed = (System.currentTimeMillis() - startTime) / 10 / 100.0
val name = startedNode.info.legalIdentitiesAndCerts.first().name.organisation
@ -165,7 +165,7 @@ open class NodeStartup(val args: Array<String>) {
}
open protected fun banJavaSerialisation(conf: FullNodeConfiguration) {
SerialFilter.install(if (conf.bftSMaRt.isValid()) ::bftSMaRtSerialFilter else ::defaultSerialFilter)
SerialFilter.install(if (conf.notary?.bftSMaRt != null) ::bftSMaRtSerialFilter else ::defaultSerialFilter)
}
open protected fun getVersionInfo(): VersionInfo {
@ -263,13 +263,6 @@ open class NodeStartup(val args: Array<String>) {
}
}
private fun printPluginsAndServices(node: Node) {
node.configuration.extraAdvertisedServiceIds.filter { it.startsWith("corda.notary.") }.let {
if (it.isNotEmpty()) Node.printBasicNodeInfo("Providing additional services", it.joinToString())
}
Node.printBasicNodeInfo("Loaded CorDapps", node.cordappProvider.cordapps.joinToString { it.name })
}
open fun drawBanner(versionInfo: VersionInfo) {
// This line makes sure ANSI escapes work on Windows, where they aren't supported out of the box.
AnsiConsole.systemInstall()

View File

@ -6,17 +6,11 @@ import net.corda.node.internal.NetworkMapInfo
import net.corda.node.services.messaging.CertificateChainCheckPolicy
import net.corda.nodeapi.User
import net.corda.nodeapi.config.NodeSSLConfiguration
import net.corda.nodeapi.config.OldConfig
import net.corda.nodeapi.internal.ServiceInfo
import java.net.URL
import java.nio.file.Path
import java.util.*
/** @param exposeRaces for testing only, so its default is not in reference.conf but here. */
data class BFTSMaRtConfiguration(val replicaId: Int, val debug: Boolean, val exposeRaces: Boolean = false) {
fun isValid() = replicaId >= 0
}
interface NodeConfiguration : NodeSSLConfiguration {
// myLegalName should be only used in the initial network registration, we should use the name from the certificate instead of this.
// TODO: Remove this so we don't accidentally use this identity in the code?
@ -37,17 +31,31 @@ interface NodeConfiguration : NodeSSLConfiguration {
val certificateChainCheckPolicies: List<CertChainPolicyConfig>
val verifierType: VerifierType
val messageRedeliveryDelaySeconds: Int
val bftSMaRt: BFTSMaRtConfiguration
val notaryNodeAddress: NetworkHostAndPort?
val notaryClusterAddresses: List<NetworkHostAndPort>
val notary: NotaryConfig?
val activeMQServer: ActiveMqServerConfiguration
}
data class BridgeConfiguration(
val retryIntervalMs: Long,
val maxRetryIntervalMin: Long,
val retryIntervalMultiplier: Double
)
data class NotaryConfig(val validating: Boolean, val raft: RaftConfig? = null, val bftSMaRt: BFTSMaRtConfiguration? = null) {
init {
require(raft == null || bftSMaRt == null) { "raft and bftSMaRt configs cannot be specified together" }
}
}
data class RaftConfig(val nodeAddress: NetworkHostAndPort, val clusterAddresses: List<NetworkHostAndPort>)
/** @param exposeRaces for testing only, so its default is not in reference.conf but here. */
data class BFTSMaRtConfiguration constructor(val replicaId: Int,
val clusterAddresses: List<NetworkHostAndPort>,
val debug: Boolean = false,
val exposeRaces: Boolean = false) {
init {
require(replicaId >= 0) { "replicaId cannot be negative" }
}
}
data class BridgeConfiguration(val retryIntervalMs: Long,
val maxRetryIntervalMin: Long,
val retryIntervalMultiplier: Double)
data class ActiveMqServerConfiguration(val bridge: BridgeConfiguration)
@ -67,16 +75,13 @@ data class FullNodeConfiguration(
override val verifierType: VerifierType,
override val messageRedeliveryDelaySeconds: Int = 30,
val useHTTPS: Boolean,
@OldConfig("artemisAddress")
val p2pAddress: NetworkHostAndPort,
val rpcAddress: NetworkHostAndPort?,
// TODO This field is slightly redundant as p2pAddress is sufficient to hold the address of the node's MQ broker.
// Instead this should be a Boolean indicating whether that broker is an internal one started by the node or an external one
val messagingServerAddress: NetworkHostAndPort?,
val extraAdvertisedServiceIds: List<String>,
override val bftSMaRt: BFTSMaRtConfiguration,
override val notaryNodeAddress: NetworkHostAndPort?,
override val notaryClusterAddresses: List<NetworkHostAndPort>,
override val notary: NotaryConfig?,
override val certificateChainCheckPolicies: List<CertChainPolicyConfig>,
override val devMode: Boolean = false,
val useTestClock: Boolean = false,

View File

@ -13,6 +13,7 @@ import net.corda.core.messaging.DataFeed
import net.corda.core.messaging.SingleMessageRecipient
import net.corda.core.node.NodeInfo
import net.corda.core.node.services.NetworkMapCache.MapChange
import net.corda.core.node.services.NotaryService
import net.corda.core.node.services.PartyInfo
import net.corda.core.schemas.NodeInfoSchemaV1
import net.corda.core.serialization.SingletonSerializeAsToken
@ -81,10 +82,8 @@ open class PersistentNetworkMapCache(private val serviceHub: ServiceHubInternal)
// Notary certificates have to be signed by the doorman directly
it.legalIdentities
}
.filter {
it.name.toString().contains("corda.notary", true)
}
.distinct() // Distinct, because of distributed service nodes
.filter { it.name.commonName?.startsWith(NotaryService.ID_PREFIX) ?: false }
.toSet() // Distinct, because of distributed service nodes
.sortedBy { it.name.toString() }
}

View File

@ -20,10 +20,12 @@ import net.corda.core.serialization.serialize
import net.corda.core.transactions.FilteredTransaction
import net.corda.core.utilities.*
import net.corda.node.services.api.ServiceHubInternal
import net.corda.node.services.config.BFTSMaRtConfiguration
import net.corda.node.utilities.AppendOnlyPersistentMap
import net.corda.node.utilities.NODE_DATABASE_PREFIX
import java.security.PublicKey
import javax.persistence.Entity
import javax.persistence.Table
import kotlin.concurrent.thread
/**
@ -33,24 +35,19 @@ import kotlin.concurrent.thread
*/
class BFTNonValidatingNotaryService(override val services: ServiceHubInternal,
override val notaryIdentityKey: PublicKey,
cluster: BFTSMaRt.Cluster = distributedCluster) : NotaryService() {
private val bftSMaRtConfig: BFTSMaRtConfiguration,
cluster: BFTSMaRt.Cluster) : NotaryService() {
companion object {
val type = SimpleNotaryService.type.getSubType("bft")
val id = constructId(validating = false, bft = true)
private val log = loggerFor<BFTNonValidatingNotaryService>()
private val distributedCluster = object : BFTSMaRt.Cluster {
override fun waitUntilAllReplicasHaveInitialized() {
log.warn("A 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>()
init {
require(services.configuration.bftSMaRt.isValid()) { "bftSMaRt replicaId must be specified in the configuration" }
client = BFTSMaRtConfig(services.configuration.notaryClusterAddresses, services.configuration.bftSMaRt.debug, services.configuration.bftSMaRt.exposeRaces).use {
val replicaId = services.configuration.bftSMaRt.replicaId
client = BFTSMaRtConfig(bftSMaRtConfig.clusterAddresses, bftSMaRtConfig.debug, bftSMaRtConfig.exposeRaces).use {
val replicaId = bftSMaRtConfig.replicaId
val configHandle = it.handle()
// Replica startup must be in parallel with other replicas, otherwise the constructor may not return:
thread(name = "BFT SMaRt replica $replicaId init", isDaemon = true) {
@ -66,7 +63,7 @@ class BFTNonValidatingNotaryService(override val services: ServiceHubInternal,
}
fun waitUntilReplicaHasInitialized() {
log.debug { "Waiting for replica ${services.configuration.bftSMaRt.replicaId} to initialize." }
log.debug { "Waiting for replica ${bftSMaRtConfig.replicaId} to initialize." }
replicaHolder.getOrThrow() // It's enough to wait for the ServiceReplica constructor to return.
}
@ -96,36 +93,37 @@ class BFTNonValidatingNotaryService(override val services: ServiceHubInternal,
}
@Entity
@javax.persistence.Table(name = "${NODE_DATABASE_PREFIX}bft_smart_notary_committed_states")
@Table(name = "${NODE_DATABASE_PREFIX}bft_smart_notary_committed_states")
class PersistedCommittedState(id: PersistentStateRef, consumingTxHash: String, consumingIndex: Int, party: PersistentUniquenessProvider.PersistentParty)
: PersistentUniquenessProvider.PersistentUniqueness(id, consumingTxHash, consumingIndex, party)
fun createMap(): AppendOnlyPersistentMap<StateRef, UniquenessProvider.ConsumingTx, PersistedCommittedState, PersistentStateRef> =
AppendOnlyPersistentMap(
toPersistentEntityKey = { PersistentStateRef(it.txhash.toString(), it.index) },
fromPersistentEntity = {
//TODO null check will become obsolete after making DB/JPA columns not nullable
val txId = it.id.txId ?: throw IllegalStateException("DB returned null SecureHash transactionId")
val index = it.id.index ?: throw IllegalStateException("DB returned null SecureHash index")
Pair(StateRef(txhash = SecureHash.parse(txId), index = index),
private fun createMap(): AppendOnlyPersistentMap<StateRef, UniquenessProvider.ConsumingTx, PersistedCommittedState, PersistentStateRef> {
return AppendOnlyPersistentMap(
toPersistentEntityKey = { PersistentStateRef(it.txhash.toString(), it.index) },
fromPersistentEntity = {
//TODO null check will become obsolete after making DB/JPA columns not nullable
val txId = it.id.txId ?: throw IllegalStateException("DB returned null SecureHash transactionId")
val index = it.id.index ?: throw IllegalStateException("DB returned null SecureHash index")
Pair(StateRef(txhash = SecureHash.parse(txId), index = index),
UniquenessProvider.ConsumingTx(
id = SecureHash.parse(it.consumingTxHash),
inputIndex = it.consumingIndex,
requestingParty = Party(
name = CordaX500Name.parse(it.party.name),
owningKey = parsePublicKeyBase58(it.party.owningKey))))
},
toPersistentEntity = { (txHash, index) : StateRef, (id, inputIndex, requestingParty): UniquenessProvider.ConsumingTx ->
PersistedCommittedState(
id = PersistentStateRef(txHash.toString(), index),
consumingTxHash = id.toString(),
consumingIndex = inputIndex,
party = PersistentUniquenessProvider.PersistentParty(requestingParty.name.toString(),
requestingParty.owningKey.toBase58String())
)
},
persistentEntityClass = PersistedCommittedState::class.java
)
},
toPersistentEntity = { (txHash, index) : StateRef, (id, inputIndex, requestingParty): UniquenessProvider.ConsumingTx ->
PersistedCommittedState(
id = PersistentStateRef(txHash.toString(), index),
consumingTxHash = id.toString(),
consumingIndex = inputIndex,
party = PersistentUniquenessProvider.PersistentParty(requestingParty.name.toString(),
requestingParty.owningKey.toBase58String())
)
},
persistentEntityClass = PersistedCommittedState::class.java
)
}
private class Replica(config: BFTSMaRtConfig,
replicaId: Int,

View File

@ -5,18 +5,23 @@ import net.corda.core.flows.NotaryFlow
import net.corda.core.node.services.TimeWindowChecker
import net.corda.core.node.services.TrustedAuthorityNotaryService
import net.corda.node.services.api.ServiceHubInternal
import net.corda.node.services.config.RaftConfig
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: ServiceHubInternal, override val notaryIdentityKey: PublicKey) : TrustedAuthorityNotaryService() {
class RaftNonValidatingNotaryService(override val services: ServiceHubInternal,
override val notaryIdentityKey: PublicKey,
raftConfig: RaftConfig) : TrustedAuthorityNotaryService() {
companion object {
val type = SimpleNotaryService.type.getSubType("raft")
val id = constructId(validating = false, raft = true)
}
override val timeWindowChecker: TimeWindowChecker = TimeWindowChecker(services.clock)
override val uniquenessProvider: RaftUniquenessProvider = RaftUniquenessProvider(services)
override val uniquenessProvider: RaftUniquenessProvider = RaftUniquenessProvider(services, raftConfig)
override fun createServiceFlow(otherPartySession: FlowSession): NotaryFlow.Service = NonValidatingNotaryFlow(otherPartySession, this)
override fun createServiceFlow(otherPartySession: FlowSession): NotaryFlow.Service {
return NonValidatingNotaryFlow(otherPartySession, this)
}
override fun start() {
uniquenessProvider.start()

View File

@ -26,16 +26,14 @@ import net.corda.core.serialization.deserialize
import net.corda.core.serialization.serialize
import net.corda.core.utilities.loggerFor
import net.corda.node.services.api.ServiceHubInternal
import net.corda.node.services.config.RaftConfig
import net.corda.node.utilities.AppendOnlyPersistentMap
import net.corda.node.utilities.CordaPersistence
import net.corda.nodeapi.config.SSLConfiguration
import java.nio.file.Path
import java.util.concurrent.CompletableFuture
import javax.annotation.concurrent.ThreadSafe
import javax.persistence.Column
import javax.persistence.Entity
import javax.persistence.Id
import javax.persistence.Lob
import javax.persistence.*
/**
* A uniqueness provider that records committed input states in a distributed collection replicated and
@ -46,7 +44,7 @@ import javax.persistence.Lob
* to the cluster leader to be actioned.
*/
@ThreadSafe
class RaftUniquenessProvider(private val services: ServiceHubInternal) : UniquenessProvider, SingletonSerializeAsToken() {
class RaftUniquenessProvider(private val services: ServiceHubInternal, private val raftConfig: RaftConfig) : UniquenessProvider, SingletonSerializeAsToken() {
companion object {
private val log = loggerFor<RaftUniquenessProvider>()
@ -67,7 +65,7 @@ class RaftUniquenessProvider(private val services: ServiceHubInternal) : Uniquen
}
@Entity
@javax.persistence.Table(name = "notary_committed_states")
@Table(name = "notary_committed_states")
class RaftState(
@Id
@Column
@ -81,13 +79,6 @@ class RaftUniquenessProvider(private val services: ServiceHubInternal) : Uniquen
/** Directory storing the Raft log and state machine snapshots */
private val storagePath: Path = services.configuration.baseDirectory
/** Address of the Copycat node run by this Corda node */
private val myAddress = services.configuration.notaryNodeAddress
?: throw IllegalArgumentException("notaryNodeAddress must be specified in configuration")
/**
* List of node addresses in the existing Copycat cluster. At least one active node must be
* provided to join the cluster. If empty, a new cluster will be bootstrapped.
*/
private val clusterAddresses = services.configuration.notaryClusterAddresses
/** The database to store the state machine state in */
private val db: CordaPersistence = services.database
/** SSL configuration */
@ -96,7 +87,6 @@ class RaftUniquenessProvider(private val services: ServiceHubInternal) : Uniquen
private lateinit var _clientFuture: CompletableFuture<CopycatClient>
private lateinit var server: CopycatServer
/**
* Copycat clients are responsible for connecting to the cluster and submitting commands and queries that operate
* on the cluster's replicated state machine.
@ -108,7 +98,7 @@ class RaftUniquenessProvider(private val services: ServiceHubInternal) : Uniquen
log.info("Creating Copycat server, log stored in: ${storagePath.toFile()}")
val stateMachineFactory = {
DistributedImmutableMap(db, RaftUniquenessProvider.Companion::createMap) }
val address = Address(myAddress.host, myAddress.port)
val address = raftConfig.nodeAddress.let { Address(it.host, it.port) }
val storage = buildStorage(storagePath)
val transport = buildTransport(transportConfiguration)
val serializer = Serializer().apply {
@ -142,9 +132,9 @@ class RaftUniquenessProvider(private val services: ServiceHubInternal) : Uniquen
.withSerializer(serializer)
.build()
val serverFuture = if (clusterAddresses.isNotEmpty()) {
log.info("Joining an existing Copycat cluster at $clusterAddresses")
val cluster = clusterAddresses.map { Address(it.host, it.port) }
val serverFuture = if (raftConfig.clusterAddresses.isNotEmpty()) {
log.info("Joining an existing Copycat cluster at ${raftConfig.clusterAddresses}")
val cluster = raftConfig.clusterAddresses.map { Address(it.host, it.port) }
server.join(cluster)
} else {
log.info("Bootstrapping a Copycat cluster at $address")

View File

@ -5,18 +5,23 @@ import net.corda.core.flows.NotaryFlow
import net.corda.core.node.services.TimeWindowChecker
import net.corda.core.node.services.TrustedAuthorityNotaryService
import net.corda.node.services.api.ServiceHubInternal
import net.corda.node.services.config.RaftConfig
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: ServiceHubInternal, override val notaryIdentityKey: PublicKey) : TrustedAuthorityNotaryService() {
class RaftValidatingNotaryService(override val services: ServiceHubInternal,
override val notaryIdentityKey: PublicKey,
raftConfig: RaftConfig) : TrustedAuthorityNotaryService() {
companion object {
val type = ValidatingNotaryService.type.getSubType("raft")
val id = constructId(validating = true, raft = true)
}
override val timeWindowChecker: TimeWindowChecker = TimeWindowChecker(services.clock)
override val uniquenessProvider: RaftUniquenessProvider = RaftUniquenessProvider(services)
override val uniquenessProvider: RaftUniquenessProvider = RaftUniquenessProvider(services, raftConfig)
override fun createServiceFlow(otherPartySession: FlowSession): NotaryFlow.Service = ValidatingNotaryFlow(otherPartySession, this)
override fun createServiceFlow(otherPartySession: FlowSession): NotaryFlow.Service {
return ValidatingNotaryFlow(otherPartySession, this)
}
override fun start() {
uniquenessProvider.start()

View File

@ -4,16 +4,11 @@ import net.corda.core.flows.FlowSession
import net.corda.core.flows.NotaryFlow
import net.corda.core.node.services.TimeWindowChecker
import net.corda.core.node.services.TrustedAuthorityNotaryService
import net.corda.nodeapi.internal.ServiceType
import net.corda.node.services.api.ServiceHubInternal
import java.security.PublicKey
/** A simple Notary service that does not perform transaction validation */
class SimpleNotaryService(override val services: ServiceHubInternal, override val notaryIdentityKey: PublicKey) : TrustedAuthorityNotaryService() {
companion object {
val type = ServiceType.notary.getSubType("simple")
}
override val timeWindowChecker = TimeWindowChecker(services.clock)
override val uniquenessProvider = PersistentUniquenessProvider()

View File

@ -4,16 +4,14 @@ import net.corda.core.flows.FlowSession
import net.corda.core.flows.NotaryFlow
import net.corda.core.node.services.TimeWindowChecker
import net.corda.core.node.services.TrustedAuthorityNotaryService
import net.corda.nodeapi.internal.ServiceType
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() {
companion object {
val type = ServiceType.notary.getSubType("validating")
val id = constructId(validating = true)
}
override val timeWindowChecker = TimeWindowChecker(services.clock)
override val uniquenessProvider = PersistentUniquenessProvider()

View File

@ -19,10 +19,6 @@ useHTTPS = false
h2port = 0
useTestClock = false
verifierType = InMemory
bftSMaRt = {
replicaId = -1
debug = false
}
activeMQServer = {
bridge = {
retryIntervalMs = 5000

View File

@ -33,7 +33,7 @@ class CordappSmokeTest {
p2pPort = port.andIncrement,
rpcPort = port.andIncrement,
webPort = port.andIncrement,
extraServices = emptyList(),
isNotary = false,
users = listOf(user)
)

View File

@ -28,9 +28,7 @@ import net.corda.node.internal.StartedNode
import net.corda.node.services.FlowPermissions.Companion.startFlowPermission
import net.corda.node.services.messaging.CURRENT_RPC_CONTEXT
import net.corda.node.services.messaging.RpcContext
import net.corda.node.services.transactions.SimpleNotaryService
import net.corda.nodeapi.User
import net.corda.nodeapi.internal.ServiceInfo
import net.corda.testing.*
import net.corda.testing.node.MockNetwork
import net.corda.testing.node.MockNetwork.MockNode
@ -68,7 +66,7 @@ class CordaRPCOpsImplTest {
mockNet = MockNetwork()
aliceNode = mockNet.createNode()
notaryNode = mockNet.createNode(advertisedServices = ServiceInfo(SimpleNotaryService.type))
notaryNode = mockNet.createNotaryNode(validating = false)
rpc = CordaRPCOpsImpl(aliceNode.services, aliceNode.smm, aliceNode.database)
CURRENT_RPC_CONTEXT.set(RpcContext(User("user", "pwd", permissions = setOf(
startFlowPermission<CashIssueFlow>(),

View File

@ -13,8 +13,6 @@ import net.corda.core.utilities.ProgressTracker
import net.corda.finance.DOLLARS
import net.corda.finance.flows.CashIssueFlow
import net.corda.node.internal.cordapp.DummyRPCFlow
import net.corda.node.services.transactions.ValidatingNotaryService
import net.corda.nodeapi.internal.ServiceInfo
import net.corda.testing.DUMMY_NOTARY
import net.corda.testing.node.MockNetwork
import net.corda.testing.setCordappPackages
@ -23,7 +21,10 @@ import org.junit.After
import org.junit.Before
import org.junit.Test
import java.util.concurrent.atomic.AtomicInteger
import kotlin.test.*
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith
import kotlin.test.assertNotEquals
import kotlin.test.assertTrue
@StartableByService
class DummyServiceFlow : FlowLogic<FlowInitiator>() {
@ -86,9 +87,7 @@ class CordaServiceTest {
fun start() {
setCordappPackages("net.corda.node.internal","net.corda.finance")
mockNet = MockNetwork(threadPerNode = true)
notaryNode = mockNet.createNode(
legalName = DUMMY_NOTARY.name,
advertisedServices = *arrayOf(ServiceInfo(ValidatingNotaryService.type)))
notaryNode = mockNet.createNotaryNode(legalName = DUMMY_NOTARY.name, validating = true)
nodeA = mockNet.createNode()
mockNet.startNodes()
}

View File

@ -12,8 +12,6 @@ import net.corda.core.transactions.WireTransaction
import net.corda.core.utilities.getOrThrow
import net.corda.core.utilities.seconds
import net.corda.node.internal.StartedNode
import net.corda.node.services.transactions.ValidatingNotaryService
import net.corda.nodeapi.internal.ServiceInfo
import net.corda.testing.*
import net.corda.testing.contracts.DummyContract
import net.corda.testing.node.MockNetwork
@ -39,12 +37,10 @@ class NotaryChangeTests {
fun setUp() {
setCordappPackages("net.corda.testing.contracts")
mockNet = MockNetwork()
oldNotaryNode = mockNet.createNode(
legalName = DUMMY_NOTARY.name,
advertisedServices = *arrayOf(ServiceInfo(ValidatingNotaryService.type)))
oldNotaryNode = mockNet.createNotaryNode(legalName = DUMMY_NOTARY.name)
clientNodeA = mockNet.createNode()
clientNodeB = mockNet.createNode()
newNotaryNode = mockNet.createNode(advertisedServices = ServiceInfo(ValidatingNotaryService.type))
newNotaryNode = mockNet.createNotaryNode()
mockNet.runNetwork() // Clear network map registration messages
oldNotaryNode.internals.ensureRegistered()
newNotaryParty = newNotaryNode.info.legalIdentities[1]

View File

@ -30,9 +30,7 @@ class FullNodeConfigurationTest {
rpcAddress = NetworkHostAndPort("localhost", 1),
messagingServerAddress = null,
extraAdvertisedServiceIds = emptyList(),
bftSMaRt = BFTSMaRtConfiguration(-1, false),
notaryNodeAddress = null,
notaryClusterAddresses = emptyList(),
notary = null,
certificateChainCheckPolicies = emptyList(),
devMode = true,
activeMQServer = ActiveMqServerConfiguration(BridgeConfiguration(0, 0, 0.0)))

View File

@ -16,8 +16,6 @@ import net.corda.core.transactions.TransactionBuilder
import net.corda.core.utilities.getOrThrow
import net.corda.node.internal.StartedNode
import net.corda.node.services.statemachine.StateMachineManager
import net.corda.node.services.transactions.ValidatingNotaryService
import net.corda.nodeapi.internal.ServiceInfo
import net.corda.testing.*
import net.corda.testing.contracts.DummyContract
import net.corda.testing.node.MockNetwork
@ -95,9 +93,7 @@ class ScheduledFlowTests {
fun setup() {
setCordappPackages("net.corda.testing.contracts")
mockNet = MockNetwork(threadPerNode = true)
notaryNode = mockNet.createNode(
legalName = DUMMY_NOTARY.name,
advertisedServices = *arrayOf(ServiceInfo(ValidatingNotaryService.type)))
notaryNode = mockNet.createNotaryNode(legalName = DUMMY_NOTARY.name)
val a = mockNet.createUnstartedNode()
val b = mockNet.createUnstartedNode()

View File

@ -6,10 +6,8 @@ import co.paralleluniverse.strands.concurrent.Semaphore
import net.corda.core.concurrent.CordaFuture
import net.corda.core.contracts.ContractState
import net.corda.core.contracts.StateAndRef
import net.corda.core.crypto.generateKeyPair
import net.corda.core.crypto.random63BitValue
import net.corda.core.flows.*
import net.corda.core.identity.CordaX500Name
import net.corda.core.identity.Party
import net.corda.core.internal.concurrent.flatMap
import net.corda.core.internal.concurrent.map
@ -21,23 +19,16 @@ import net.corda.core.serialization.serialize
import net.corda.core.toFuture
import net.corda.core.transactions.SignedTransaction
import net.corda.core.transactions.TransactionBuilder
import net.corda.core.utilities.OpaqueBytes
import net.corda.core.utilities.ProgressTracker
import net.corda.core.utilities.ProgressTracker.Change
import net.corda.core.utilities.getOrThrow
import net.corda.core.utilities.unwrap
import net.corda.finance.DOLLARS
import net.corda.finance.flows.CashIssueFlow
import net.corda.finance.flows.CashPaymentFlow
import net.corda.node.internal.InitiatedFlowFactory
import net.corda.node.internal.StartedNode
import net.corda.node.services.persistence.checkpoints
import net.corda.node.services.transactions.ValidatingNotaryService
import net.corda.nodeapi.internal.ServiceInfo
import net.corda.testing.*
import net.corda.testing.contracts.DummyContract
import net.corda.testing.contracts.DummyState
import net.corda.testing.node.InMemoryMessagingNetwork
import net.corda.testing.node.InMemoryMessagingNetwork.MessageTransfer
import net.corda.testing.node.InMemoryMessagingNetwork.ServicePeerAllocationStrategy.RoundRobin
import net.corda.testing.node.MockNetwork
@ -69,10 +60,8 @@ class FlowFrameworkTests {
private val receivedSessionMessages = ArrayList<SessionTransfer>()
private lateinit var node1: StartedNode<MockNode>
private lateinit var node2: StartedNode<MockNode>
private lateinit var notary1: StartedNode<MockNode>
private lateinit var notary2: StartedNode<MockNode>
private lateinit var notary1Identity: Party
private lateinit var notary2Identity: Party
private lateinit var notary: StartedNode<MockNode>
private lateinit var notaryIdentity: Party
@Before
fun start() {
@ -85,19 +74,13 @@ class FlowFrameworkTests {
node1.internals.ensureRegistered()
// We intentionally create our own notary and ignore the one provided by the network
val notaryKeyPair = generateKeyPair()
val notaryService = ServiceInfo(ValidatingNotaryService.type, CordaX500Name(commonName = ValidatingNotaryService.type.id, organisation = "Notary service 2000", locality = "London", country = "GB"))
val notaryIdentityOverride = Pair(notaryService, notaryKeyPair)
// Note that these notaries don't operate correctly as they don't share their state. They are only used for testing
// service addressing.
notary1 = mockNet.createNotaryNode(notaryIdentity = notaryIdentityOverride, serviceName = notaryService.name)
notary2 = mockNet.createNotaryNode(notaryIdentity = notaryIdentityOverride, serviceName = notaryService.name)
notary = mockNet.createNotaryNode()
receivedSessionMessagesObservable().forEach { receivedSessionMessages += it }
mockNet.runNetwork()
notary1Identity = notary1.services.myInfo.legalIdentities[1]
notary2Identity = notary2.services.myInfo.legalIdentities[1]
notaryIdentity = notary.services.myInfo.legalIdentities[1]
}
@After
@ -335,62 +318,6 @@ class FlowFrameworkTests {
)
}
@Test
fun `different notaries are picked when addressing shared notary identity`() {
assertEquals(notary1Identity, notary2Identity)
assertThat(node1.services.networkMapCache.notaryIdentities.size == 1)
node1.services.startFlow(CashIssueFlow(
2000.DOLLARS,
OpaqueBytes.of(0x01),
notary1Identity)).resultFuture.getOrThrow()
// We pay a couple of times, the notary picking should go round robin
for (i in 1..3) {
val flow = node1.services.startFlow(CashPaymentFlow(500.DOLLARS, node2.info.chooseIdentity()))
mockNet.runNetwork()
flow.resultFuture.getOrThrow()
}
val endpoint = mockNet.messagingNetwork.endpoint(notary1.network.myAddress as InMemoryMessagingNetwork.PeerHandle)!!
val party1Info = notary1.services.networkMapCache.getPartyInfo(notary1Identity)!!
assertTrue(party1Info is PartyInfo.DistributedNode)
val notary1Address: MessageRecipients = endpoint.getAddressOfParty(notary1.services.networkMapCache.getPartyInfo(notary1Identity)!!)
assertThat(notary1Address).isInstanceOf(InMemoryMessagingNetwork.ServiceHandle::class.java)
assertEquals(notary1Address, endpoint.getAddressOfParty(notary2.services.networkMapCache.getPartyInfo(notary2Identity)!!))
receivedSessionMessages.expectEvents(isStrict = false) {
sequence(
// First Pay
expect(match = { it.message is SessionInit && it.message.initiatingFlowClass == NotaryFlow.Client::class.java.name }) {
it.message as SessionInit
assertEquals(node1.internals.id, it.from)
assertEquals(notary1Address, it.to)
},
expect(match = { it.message is SessionConfirm }) {
it.message as SessionConfirm
assertEquals(notary1.internals.id, it.from)
},
// Second pay
expect(match = { it.message is SessionInit && it.message.initiatingFlowClass == NotaryFlow.Client::class.java.name }) {
it.message as SessionInit
assertEquals(node1.internals.id, it.from)
assertEquals(notary1Address, it.to)
},
expect(match = { it.message is SessionConfirm }) {
it.message as SessionConfirm
assertEquals(notary2.internals.id, it.from)
},
// Third pay
expect(match = { it.message is SessionInit && it.message.initiatingFlowClass == NotaryFlow.Client::class.java.name }) {
it.message as SessionInit
assertEquals(node1.internals.id, it.from)
assertEquals(notary1Address, it.to)
},
expect(match = { it.message is SessionConfirm }) {
it.message as SessionConfirm
assertEquals(it.from, notary1.internals.id)
}
)
}
}
@Test
fun `other side ends before doing expected send`() {
node2.registerFlowFactory(ReceiveFlow::class) { NoOpFlow() }
@ -604,7 +531,7 @@ class FlowFrameworkTests {
@Test
fun `wait for transaction`() {
val ptx = TransactionBuilder(notary = notary1Identity)
val ptx = TransactionBuilder(notary = notaryIdentity)
.addOutputState(DummyState(), DummyContract.PROGRAM_ID)
.addCommand(dummyCommand(node1.info.chooseIdentity().owningKey))
val stx = node1.services.signInitialTransaction(ptx)
@ -619,7 +546,7 @@ class FlowFrameworkTests {
@Test
fun `committer throws exception before calling the finality flow`() {
val ptx = TransactionBuilder(notary = notary1Identity)
val ptx = TransactionBuilder(notary = notaryIdentity)
.addOutputState(DummyState(), DummyContract.PROGRAM_ID)
.addCommand(dummyCommand())
val stx = node1.services.signInitialTransaction(ptx)
@ -636,7 +563,7 @@ class FlowFrameworkTests {
@Test
fun `verify vault query service is tokenizable by force checkpointing within a flow`() {
val ptx = TransactionBuilder(notary = notary1Identity)
val ptx = TransactionBuilder(notary = notaryIdentity)
.addOutputState(DummyState(), DummyContract.PROGRAM_ID)
.addCommand(dummyCommand(node1.info.chooseIdentity().owningKey))
val stx = node1.services.signInitialTransaction(ptx)

View File

@ -13,7 +13,6 @@ import net.corda.core.transactions.TransactionBuilder
import net.corda.core.utilities.getOrThrow
import net.corda.core.utilities.seconds
import net.corda.node.internal.StartedNode
import net.corda.nodeapi.internal.ServiceInfo
import net.corda.testing.*
import net.corda.testing.contracts.DummyContract
import net.corda.testing.node.MockNetwork
@ -36,9 +35,7 @@ class NotaryServiceTests {
fun setup() {
setCordappPackages("net.corda.testing.contracts")
mockNet = MockNetwork()
notaryNode = mockNet.createNode(
legalName = DUMMY_NOTARY.name,
advertisedServices = *arrayOf(ServiceInfo(SimpleNotaryService.type)))
notaryNode = mockNet.createNotaryNode(legalName = DUMMY_NOTARY.name, validating = false)
clientNode = mockNet.createNode()
mockNet.runNetwork() // Clear network map registration messages
notaryNode.internals.ensureRegistered()

View File

@ -14,7 +14,6 @@ import net.corda.core.transactions.TransactionBuilder
import net.corda.core.utilities.getOrThrow
import net.corda.node.internal.StartedNode
import net.corda.node.services.issueInvalidState
import net.corda.nodeapi.internal.ServiceInfo
import net.corda.testing.*
import net.corda.testing.contracts.DummyContract
import net.corda.testing.node.MockNetwork
@ -36,10 +35,7 @@ class ValidatingNotaryServiceTests {
fun setup() {
setCordappPackages("net.corda.testing.contracts")
mockNet = MockNetwork()
notaryNode = mockNet.createNode(
legalName = DUMMY_NOTARY.name,
advertisedServices = *arrayOf(ServiceInfo(ValidatingNotaryService.type))
)
notaryNode = mockNet.createNotaryNode(legalName = DUMMY_NOTARY.name)
clientNode = mockNet.createNode()
mockNet.runNetwork() // Clear network map registration messages
notaryNode.internals.ensureRegistered()