mirror of
https://github.com/corda/corda.git
synced 2025-06-18 07:08:15 +00:00
Cleaned up notary configuration by introducing a notary config option.
extraAdvertisedServiceIds is no longer used for this.
This commit is contained in:
@ -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 ->
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
}
|
||||
|
@ -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()
|
||||
|
@ -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() {
|
||||
|
@ -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`() {
|
||||
|
@ -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 {
|
||||
|
@ -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))
|
||||
|
@ -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()
|
||||
|
@ -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,
|
||||
|
@ -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() }
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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()
|
||||
|
@ -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")
|
||||
|
@ -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()
|
||||
|
@ -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()
|
||||
|
||||
|
@ -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()
|
||||
|
||||
|
@ -19,10 +19,6 @@ useHTTPS = false
|
||||
h2port = 0
|
||||
useTestClock = false
|
||||
verifierType = InMemory
|
||||
bftSMaRt = {
|
||||
replicaId = -1
|
||||
debug = false
|
||||
}
|
||||
activeMQServer = {
|
||||
bridge = {
|
||||
retryIntervalMs = 5000
|
||||
|
@ -33,7 +33,7 @@ class CordappSmokeTest {
|
||||
p2pPort = port.andIncrement,
|
||||
rpcPort = port.andIncrement,
|
||||
webPort = port.andIncrement,
|
||||
extraServices = emptyList(),
|
||||
isNotary = false,
|
||||
users = listOf(user)
|
||||
)
|
||||
|
||||
|
@ -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>(),
|
||||
|
@ -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()
|
||||
}
|
||||
|
@ -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]
|
||||
|
@ -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)))
|
||||
|
@ -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()
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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()
|
||||
|
@ -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()
|
||||
|
Reference in New Issue
Block a user