mirror of
https://github.com/corda/corda.git
synced 2025-06-17 06:38:21 +00:00
Introducing network parameters.
network-parameters file read in by the node at startup, of which only the list of notaries is used. For now, the driver and MockNetwork have been updated to require notaries to be started first. This is so that the same set of network parameters can be defined for all the nodes. CN in the legal name is not longer disallowed since it's no longer reserved for distributed notary names. Single-node notaries now only have one identity, their main identity. Nodes part of a cluster continue to have two. (Based off Kasia's work)
This commit is contained in:
@ -9,20 +9,15 @@ import net.corda.core.utilities.getOrThrow
|
||||
import net.corda.node.internal.NodeStartup
|
||||
import net.corda.node.services.Permissions.Companion.startFlow
|
||||
import net.corda.nodeapi.User
|
||||
import net.corda.nodeapi.internal.ServiceInfo
|
||||
import net.corda.nodeapi.internal.ServiceType
|
||||
import net.corda.testing.ALICE
|
||||
import net.corda.testing.ProjectStructure.projectRootDir
|
||||
import net.corda.testing.driver.ListenProcessDeathException
|
||||
import net.corda.testing.driver.driver
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.assertj.core.api.Assertions.assertThatThrownBy
|
||||
import org.junit.Ignore
|
||||
import org.junit.Test
|
||||
import java.io.*
|
||||
import java.nio.file.Files
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertFailsWith
|
||||
|
||||
class BootTests {
|
||||
|
||||
@ -53,16 +48,6 @@ class BootTests {
|
||||
assertEquals(1, numberOfNodesThatLogged)
|
||||
}
|
||||
}
|
||||
|
||||
@Ignore("Need rewriting to produce too big network map registration (adverticed services trick doesn't work after services removal).")
|
||||
@Test
|
||||
fun `node quits on failure to register with network map`() {
|
||||
val tooManyAdvertisedServices = (1..100).map { ServiceInfo(ServiceType.notary.getSubType("$it")) }.toSet()
|
||||
driver {
|
||||
val future = startNode(providedName = ALICE.name)
|
||||
assertFailsWith(ListenProcessDeathException::class) { future.getOrThrow() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@StartableByRPC
|
||||
|
@ -20,10 +20,13 @@ import net.corda.core.utilities.seconds
|
||||
import net.corda.node.internal.cordapp.CordappLoader
|
||||
import net.corda.node.internal.cordapp.CordappProviderImpl
|
||||
import net.corda.nodeapi.User
|
||||
import net.corda.testing.*
|
||||
import net.corda.testing.DUMMY_BANK_A
|
||||
import net.corda.testing.DUMMY_NOTARY
|
||||
import net.corda.testing.SerializationEnvironmentRule
|
||||
import net.corda.testing.driver.DriverDSLExposedInterface
|
||||
import net.corda.testing.driver.NodeHandle
|
||||
import net.corda.testing.driver.driver
|
||||
import net.corda.testing.eventually
|
||||
import net.corda.testing.node.MockServices
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Before
|
||||
@ -54,16 +57,16 @@ class AttachmentLoadingTests {
|
||||
val bankAName = CordaX500Name("BankA", "Zurich", "CH")
|
||||
val bankBName = CordaX500Name("BankB", "Zurich", "CH")
|
||||
val notaryName = CordaX500Name("Notary", "Zurich", "CH")
|
||||
val flowInitiatorClass =
|
||||
val flowInitiatorClass: Class<out FlowLogic<*>> =
|
||||
Class.forName("net.corda.finance.contracts.isolated.IsolatedDummyFlow\$Initiator", true, URLClassLoader(arrayOf(isolatedJAR)))
|
||||
.asSubclass(FlowLogic::class.java)
|
||||
|
||||
private fun DriverDSLExposedInterface.createTwoNodesAndNotary(): List<NodeHandle> {
|
||||
private fun DriverDSLExposedInterface.createNotaryAndTwoNodes(): List<NodeHandle> {
|
||||
val adminUser = User("admin", "admin", permissions = setOf("ALL"))
|
||||
val nodes = listOf(
|
||||
startNotaryNode(providedName = notaryName, rpcUsers = listOf(adminUser), validating = false),
|
||||
startNode(providedName = bankAName, rpcUsers = listOf(adminUser)),
|
||||
startNode(providedName = bankBName, rpcUsers = listOf(adminUser)),
|
||||
startNotaryNode(providedName = notaryName, rpcUsers = listOf(adminUser), validating = false)
|
||||
startNode(providedName = bankBName, rpcUsers = listOf(adminUser))
|
||||
).transpose().getOrThrow() // Wait for all nodes to start up.
|
||||
nodes.forEach { it.rpc.waitUntilNetworkReady().getOrThrow() }
|
||||
return nodes
|
||||
@ -119,7 +122,7 @@ class AttachmentLoadingTests {
|
||||
fun `test that attachments retrieved over the network are not used for code`() {
|
||||
driver(initialiseSerialization = false) {
|
||||
installIsolatedCordappTo(bankAName)
|
||||
val (bankA, bankB, _) = createTwoNodesAndNotary()
|
||||
val (_, bankA, bankB) = createNotaryAndTwoNodes()
|
||||
eventuallyPassingTest {
|
||||
assertFailsWith<UnexpectedFlowEndException>("Party C=CH,L=Zurich,O=BankB rejected session request: Don't know net.corda.finance.contracts.isolated.IsolatedDummyFlow\$Initiator") {
|
||||
bankA.rpc.startFlowDynamic(flowInitiatorClass, bankB.nodeInfo.legalIdentities.first()).returnValue.getOrThrow()
|
||||
@ -133,7 +136,7 @@ class AttachmentLoadingTests {
|
||||
driver(initialiseSerialization = false) {
|
||||
installIsolatedCordappTo(bankAName)
|
||||
installIsolatedCordappTo(bankBName)
|
||||
val (bankA, bankB, _) = createTwoNodesAndNotary()
|
||||
val (_, bankA, bankB) = createNotaryAndTwoNodes()
|
||||
eventuallyPassingTest {
|
||||
bankA.rpc.startFlowDynamic(flowInitiatorClass, bankB.nodeInfo.legalIdentities.first()).returnValue.getOrThrow()
|
||||
}
|
||||
|
@ -13,6 +13,8 @@ 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.node.NotaryInfo
|
||||
import net.corda.core.node.services.NotaryService
|
||||
import net.corda.core.transactions.SignedTransaction
|
||||
import net.corda.core.transactions.TransactionBuilder
|
||||
import net.corda.core.utilities.NetworkHostAndPort
|
||||
@ -21,15 +23,16 @@ 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.testing.chooseIdentity
|
||||
import net.corda.testing.common.internal.NetworkParametersCopier
|
||||
import net.corda.testing.common.internal.testNetworkParameters
|
||||
import net.corda.testing.contracts.DummyContract
|
||||
import net.corda.testing.dummyCommand
|
||||
import net.corda.testing.getDefaultNotary
|
||||
import net.corda.testing.node.MockNetwork
|
||||
import net.corda.testing.node.MockNetwork.MockNode
|
||||
import net.corda.testing.node.MockNodeParameters
|
||||
import org.junit.After
|
||||
import org.junit.Test
|
||||
@ -38,39 +41,47 @@ import kotlin.test.assertEquals
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
class BFTNotaryServiceTests {
|
||||
companion object {
|
||||
private val clusterName = CordaX500Name(BFTNonValidatingNotaryService.id, "BFT", "Zurich", "CH")
|
||||
}
|
||||
|
||||
private val mockNet = MockNetwork()
|
||||
private val node = mockNet.createNode()
|
||||
private lateinit var notary: Party
|
||||
private lateinit var node: StartedNode<MockNode>
|
||||
|
||||
@After
|
||||
fun stopNodes() {
|
||||
mockNet.stopNodes()
|
||||
}
|
||||
|
||||
private fun bftNotaryCluster(clusterSize: Int, exposeRaces: Boolean = false) {
|
||||
private fun startBftClusterAndNode(clusterSize: Int, exposeRaces: Boolean = false) {
|
||||
(Paths.get("config") / "currentView").deleteIfExists() // XXX: Make config object warn if this exists?
|
||||
val replicaIds = (0 until clusterSize)
|
||||
ServiceIdentityGenerator.generateToDisk(
|
||||
|
||||
notary = ServiceIdentityGenerator.generateToDisk(
|
||||
replicaIds.map { mockNet.baseDirectory(mockNet.nextNodeId + it) },
|
||||
clusterName)
|
||||
CordaX500Name("BFT", "Zurich", "CH"),
|
||||
NotaryService.constructId(validating = false, bft = true))
|
||||
|
||||
val networkParameters = NetworkParametersCopier(testNetworkParameters(listOf(NotaryInfo(notary, false))))
|
||||
|
||||
val clusterAddresses = replicaIds.map { NetworkHostAndPort("localhost", 11000 + it * 10) }
|
||||
replicaIds.forEach { replicaId ->
|
||||
mockNet.createNode(MockNodeParameters(configOverrides = {
|
||||
|
||||
val nodes = replicaIds.map { replicaId ->
|
||||
mockNet.createUnstartedNode(MockNodeParameters(configOverrides = {
|
||||
val notary = NotaryConfig(validating = false, bftSMaRt = BFTSMaRtConfiguration(replicaId, clusterAddresses, exposeRaces = exposeRaces))
|
||||
doReturn(notary).whenever(it).notary
|
||||
}))
|
||||
}
|
||||
mockNet.runNetwork() // Exchange initial network map registration messages.
|
||||
} + mockNet.createUnstartedNode()
|
||||
|
||||
// MockNetwork doesn't support BFT clusters, so we create all the nodes we need unstarted, and then install the
|
||||
// network-parameters in their directories before they're started.
|
||||
node = nodes.map { node ->
|
||||
networkParameters.install(mockNet.baseDirectory(node.id))
|
||||
node.start()
|
||||
}.last()
|
||||
}
|
||||
|
||||
/** Failure mode is the redundant replica gets stuck in startup, so we can't dispose it cleanly at the end. */
|
||||
@Test
|
||||
fun `all replicas start even if there is a new consensus during startup`() {
|
||||
bftNotaryCluster(minClusterSize(1), true) // This true adds a sleep to expose the race.
|
||||
val notary = node.services.getDefaultNotary()
|
||||
startBftClusterAndNode(minClusterSize(1), exposeRaces = true) // This true adds a sleep to expose the race.
|
||||
val f = node.run {
|
||||
val trivialTx = signInitialTransaction(notary) {
|
||||
addOutputState(DummyContract.SingleOwnerState(owner = info.chooseIdentity()), DummyContract.PROGRAM_ID, AlwaysAcceptAttachmentConstraint)
|
||||
@ -94,8 +105,7 @@ class BFTNotaryServiceTests {
|
||||
|
||||
private fun detectDoubleSpend(faultyReplicas: Int) {
|
||||
val clusterSize = minClusterSize(faultyReplicas)
|
||||
bftNotaryCluster(clusterSize)
|
||||
val notary = node.services.getDefaultNotary()
|
||||
startBftClusterAndNode(clusterSize)
|
||||
node.run {
|
||||
val issueTx = signInitialTransaction(notary) {
|
||||
addOutputState(DummyContract.SingleOwnerState(owner = info.chooseIdentity()), DummyContract.PROGRAM_ID, AlwaysAcceptAttachmentConstraint)
|
||||
@ -138,15 +148,13 @@ class BFTNotaryServiceTests {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun StartedNode<*>.signInitialTransaction(
|
||||
notary: Party,
|
||||
block: TransactionBuilder.() -> Any?
|
||||
): SignedTransaction {
|
||||
return services.signInitialTransaction(
|
||||
TransactionBuilder(notary).apply {
|
||||
addCommand(dummyCommand(services.myInfo.chooseIdentity().owningKey))
|
||||
block()
|
||||
})
|
||||
private fun StartedNode<*>.signInitialTransaction(notary: Party, block: TransactionBuilder.() -> Any?): SignedTransaction {
|
||||
return services.signInitialTransaction(
|
||||
TransactionBuilder(notary).apply {
|
||||
addCommand(dummyCommand(services.myInfo.chooseIdentity().owningKey))
|
||||
block()
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -38,12 +38,12 @@ class DistributedServiceTests {
|
||||
invokeRpc(CordaRPCOps::nodeInfo),
|
||||
invokeRpc(CordaRPCOps::stateMachinesFeed))
|
||||
)
|
||||
val aliceFuture = startNode(providedName = ALICE.name, rpcUsers = listOf(testUser))
|
||||
val notariesFuture = startNotaryCluster(
|
||||
DUMMY_NOTARY.name.copy(commonName = RaftValidatingNotaryService.id),
|
||||
rpcUsers = listOf(testUser),
|
||||
clusterSize = clusterSize
|
||||
)
|
||||
val aliceFuture = startNode(providedName = ALICE.name, rpcUsers = listOf(testUser))
|
||||
|
||||
alice = aliceFuture.get()
|
||||
val (notaryIdentity, notaryNodes) = notariesFuture.get()
|
||||
|
@ -6,15 +6,11 @@ import net.corda.cordform.CordformNode
|
||||
import net.corda.core.internal.createDirectories
|
||||
import net.corda.core.internal.div
|
||||
import net.corda.core.node.NodeInfo
|
||||
import net.corda.core.node.services.KeyManagementService
|
||||
import net.corda.node.services.identity.InMemoryIdentityService
|
||||
import net.corda.nodeapi.NodeInfoFilesCopier
|
||||
import net.corda.testing.ALICE
|
||||
import net.corda.testing.ALICE_KEY
|
||||
import net.corda.testing.DEV_TRUST_ROOT
|
||||
import net.corda.testing.getTestPartyAndCertificate
|
||||
import net.corda.testing.internal.NodeBasedTest
|
||||
import net.corda.testing.node.MockKeyManagementService
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.assertj.core.api.Assertions.contentOf
|
||||
import org.junit.Before
|
||||
@ -31,7 +27,6 @@ class NodeInfoWatcherTest : NodeBasedTest() {
|
||||
val nodeInfo = NodeInfo(listOf(), listOf(getTestPartyAndCertificate(ALICE)), 0, 0)
|
||||
}
|
||||
|
||||
private lateinit var keyManagementService: KeyManagementService
|
||||
private lateinit var nodeInfoPath: Path
|
||||
private val scheduler = TestScheduler()
|
||||
private val testSubscriber = TestSubscriber<NodeInfo>()
|
||||
@ -41,8 +36,6 @@ class NodeInfoWatcherTest : NodeBasedTest() {
|
||||
|
||||
@Before
|
||||
fun start() {
|
||||
val identityService = InMemoryIdentityService(trustRoot = DEV_TRUST_ROOT)
|
||||
keyManagementService = MockKeyManagementService(identityService, ALICE_KEY)
|
||||
nodeInfoWatcher = NodeInfoWatcher(tempFolder.root.toPath(), scheduler = scheduler)
|
||||
nodeInfoPath = tempFolder.root.toPath() / CordformNode.NODE_INFO_DIRECTORY
|
||||
}
|
||||
@ -51,7 +44,7 @@ class NodeInfoWatcherTest : NodeBasedTest() {
|
||||
fun `save a NodeInfo`() {
|
||||
assertEquals(0,
|
||||
tempFolder.root.list().filter { it.startsWith(NodeInfoFilesCopier.NODE_INFO_FILE_NAME_PREFIX) }.size)
|
||||
NodeInfoWatcher.saveToFile(tempFolder.root.toPath(), nodeInfo, keyManagementService)
|
||||
NodeInfoWatcher.saveToFile(tempFolder.root.toPath(), nodeInfo, ALICE_KEY)
|
||||
|
||||
val nodeInfoFiles = tempFolder.root.list().filter { it.startsWith(NodeInfoFilesCopier.NODE_INFO_FILE_NAME_PREFIX) }
|
||||
assertEquals(1, nodeInfoFiles.size)
|
||||
@ -66,7 +59,7 @@ class NodeInfoWatcherTest : NodeBasedTest() {
|
||||
fun `save a NodeInfo to JimFs`() {
|
||||
val jimFs = Jimfs.newFileSystem(Configuration.unix())
|
||||
val jimFolder = jimFs.getPath("/nodeInfo")
|
||||
NodeInfoWatcher.saveToFile(jimFolder, nodeInfo, keyManagementService)
|
||||
NodeInfoWatcher.saveToFile(jimFolder, nodeInfo, ALICE_KEY)
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -135,6 +128,6 @@ class NodeInfoWatcherTest : NodeBasedTest() {
|
||||
|
||||
// Write a nodeInfo under the right path.
|
||||
private fun createNodeInfoFileInPath(nodeInfo: NodeInfo) {
|
||||
NodeInfoWatcher.saveToFile(nodeInfoPath, nodeInfo, keyManagementService)
|
||||
NodeInfoWatcher.saveToFile(nodeInfoPath, nodeInfo, ALICE_KEY)
|
||||
}
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ import net.corda.node.internal.Node
|
||||
import net.corda.node.internal.StartedNode
|
||||
import net.corda.testing.ALICE
|
||||
import net.corda.testing.BOB
|
||||
import net.corda.testing.DUMMY_NOTARY
|
||||
import net.corda.testing.DUMMY_REGULATOR
|
||||
import net.corda.testing.chooseIdentity
|
||||
import net.corda.testing.internal.NodeBasedTest
|
||||
import org.junit.Before
|
||||
@ -16,9 +16,9 @@ import org.junit.Test
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
class PersistentNetworkMapCacheTest : NodeBasedTest() {
|
||||
private val partiesList = listOf(DUMMY_NOTARY, ALICE, BOB)
|
||||
private val partiesList = listOf(DUMMY_REGULATOR, ALICE, BOB)
|
||||
private val addressesMap = HashMap<CordaX500Name, NetworkHostAndPort>()
|
||||
private val infos: MutableSet<NodeInfo> = HashSet()
|
||||
private val infos = HashSet<NodeInfo>()
|
||||
|
||||
@Before
|
||||
fun start() {
|
||||
@ -37,8 +37,8 @@ class PersistentNetworkMapCacheTest : NodeBasedTest() {
|
||||
alice.database.transaction {
|
||||
val res = netCache.getNodeByLegalIdentity(alice.info.chooseIdentity())
|
||||
assertEquals(alice.info, res)
|
||||
val res2 = netCache.getNodeByLegalName(DUMMY_NOTARY.name)
|
||||
assertEquals(infos.singleOrNull { DUMMY_NOTARY.name in it.legalIdentitiesAndCerts.map { it.name } }, res2)
|
||||
val res2 = netCache.getNodeByLegalName(DUMMY_REGULATOR.name)
|
||||
assertEquals(infos.singleOrNull { DUMMY_REGULATOR.name in it.legalIdentities.map { it.name } }, res2)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -8,11 +8,11 @@ import net.corda.core.messaging.startFlow
|
||||
import net.corda.core.transactions.TransactionBuilder
|
||||
import net.corda.testing.BOB
|
||||
import net.corda.testing.DUMMY_NOTARY
|
||||
import net.corda.testing.aliceBobAndNotary
|
||||
import net.corda.testing.contracts.DummyContract
|
||||
import net.corda.testing.contracts.DummyState
|
||||
import net.corda.testing.driver.driver
|
||||
import net.corda.testing.dummyCommand
|
||||
import net.corda.testing.notaryAliceAndBob
|
||||
import org.junit.Test
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
@ -65,7 +65,7 @@ class LargeTransactionsTest {
|
||||
val bigFile3 = InputStreamAndHash.createInMemoryTestZip(1024 * 1024 * 3, 2)
|
||||
val bigFile4 = InputStreamAndHash.createInMemoryTestZip(1024 * 1024 * 3, 3)
|
||||
driver(startNodesInProcess = true, extraCordappPackagesToScan = listOf("net.corda.testing.contracts")) {
|
||||
val (alice, _, _) = aliceBobAndNotary()
|
||||
val (_, alice) = notaryAliceAndBob()
|
||||
alice.useRPC {
|
||||
val hash1 = it.uploadAttachment(bigFile1.inputStream)
|
||||
val hash2 = it.uploadAttachment(bigFile2.inputStream)
|
||||
|
@ -18,7 +18,6 @@ import org.assertj.core.api.Assertions.assertThatExceptionOfType
|
||||
import org.bouncycastle.asn1.x509.GeneralName
|
||||
import org.bouncycastle.asn1.x509.GeneralSubtree
|
||||
import org.bouncycastle.asn1.x509.NameConstraints
|
||||
import org.bouncycastle.cert.X509CertificateHolder
|
||||
import org.junit.Test
|
||||
import java.nio.file.Files
|
||||
|
||||
|
@ -23,7 +23,7 @@ import net.corda.nodeapi.User
|
||||
import net.corda.testing.DUMMY_NOTARY
|
||||
import net.corda.testing.chooseIdentity
|
||||
import net.corda.testing.driver.driver
|
||||
import org.junit.Assume
|
||||
import org.junit.Assume.assumeFalse
|
||||
import org.junit.Test
|
||||
import java.lang.management.ManagementFactory
|
||||
import javax.persistence.Column
|
||||
@ -33,25 +33,24 @@ import kotlin.test.assertEquals
|
||||
import kotlin.test.assertNotNull
|
||||
|
||||
class NodeStatePersistenceTests {
|
||||
|
||||
@Test
|
||||
fun `persistent state survives node restart`() {
|
||||
// Temporary disable this test when executed on Windows. It is known to be sporadically failing.
|
||||
// More investigation is needed to establish why.
|
||||
Assume.assumeFalse(System.getProperty("os.name").toLowerCase().startsWith("win"))
|
||||
assumeFalse(System.getProperty("os.name").toLowerCase().startsWith("win"))
|
||||
|
||||
val user = User("mark", "dadada", setOf(startFlow<SendMessageFlow>(), invokeRpc("vaultQuery")))
|
||||
val message = Message("Hello world!")
|
||||
driver(isDebug = true, startNodesInProcess = isQuasarAgentSpecified()) {
|
||||
val (nodeName, notaryNodeHandle) = {
|
||||
val notaryNodeHandle = startNotaryNode(DUMMY_NOTARY.name, validating = false).getOrThrow()
|
||||
val nodeName = {
|
||||
startNotaryNode(DUMMY_NOTARY.name, validating = false).getOrThrow()
|
||||
val nodeHandle = startNode(rpcUsers = listOf(user)).getOrThrow()
|
||||
val nodeName = nodeHandle.nodeInfo.chooseIdentity().name
|
||||
nodeHandle.rpcClientToNode().start(user.username, user.password).use {
|
||||
it.proxy.startFlow(::SendMessageFlow, message).returnValue.getOrThrow()
|
||||
}
|
||||
nodeHandle.stop()
|
||||
nodeName to notaryNodeHandle
|
||||
nodeName
|
||||
}()
|
||||
|
||||
val nodeHandle = startNode(providedName = nodeName, rpcUsers = listOf(user)).getOrThrow()
|
||||
|
@ -7,6 +7,7 @@ import net.corda.confidential.SwapIdentitiesFlow
|
||||
import net.corda.confidential.SwapIdentitiesHandler
|
||||
import net.corda.core.CordaException
|
||||
import net.corda.core.concurrent.CordaFuture
|
||||
import net.corda.core.crypto.SignedData
|
||||
import net.corda.core.flows.*
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.identity.Party
|
||||
@ -20,6 +21,7 @@ import net.corda.core.node.services.*
|
||||
import net.corda.core.serialization.SerializationWhitelist
|
||||
import net.corda.core.serialization.SerializeAsToken
|
||||
import net.corda.core.serialization.SingletonSerializeAsToken
|
||||
import net.corda.core.serialization.deserialize
|
||||
import net.corda.core.transactions.SignedTransaction
|
||||
import net.corda.core.utilities.NetworkHostAndPort
|
||||
import net.corda.core.utilities.debug
|
||||
@ -42,7 +44,9 @@ import net.corda.node.services.events.ScheduledActivityObserver
|
||||
import net.corda.node.services.identity.PersistentIdentityService
|
||||
import net.corda.node.services.keys.PersistentKeyManagementService
|
||||
import net.corda.node.services.messaging.MessagingService
|
||||
import net.corda.node.services.network.*
|
||||
import net.corda.node.services.network.NetworkMapCacheImpl
|
||||
import net.corda.node.services.network.NodeInfoWatcher
|
||||
import net.corda.node.services.network.PersistentNetworkMapCache
|
||||
import net.corda.node.services.persistence.DBCheckpointStorage
|
||||
import net.corda.node.services.persistence.DBTransactionMappingStorage
|
||||
import net.corda.node.services.persistence.DBTransactionStorage
|
||||
@ -82,18 +86,15 @@ import net.corda.core.crypto.generateKeyPair as cryptoGenerateKeyPair
|
||||
* Marked as SingletonSerializeAsToken to prevent the invisible reference to AbstractNode in the ServiceHub accidentally
|
||||
* sweeping up the Node into the Kryo checkpoint serialization via any flows holding a reference to ServiceHub.
|
||||
*/
|
||||
// TODO Log warning if this node is a notary but not one of the ones specified in the network parameters, both for core and custom
|
||||
|
||||
// In theory the NodeInfo for the node should be passed in, instead, however currently this is constructed by the
|
||||
// AbstractNode. It should be possible to generate the NodeInfo outside of AbstractNode, so it can be passed in.
|
||||
abstract class AbstractNode(config: NodeConfiguration,
|
||||
abstract class AbstractNode(val configuration: NodeConfiguration,
|
||||
val platformClock: Clock,
|
||||
protected val versionInfo: VersionInfo,
|
||||
protected val cordappLoader: CordappLoader,
|
||||
@VisibleForTesting val busyNodeLatch: ReusableLatch = ReusableLatch()) : SingletonSerializeAsToken() {
|
||||
open val configuration = config.apply {
|
||||
require(minimumPlatformVersion <= versionInfo.platformVersion) {
|
||||
"minimumPlatformVersion cannot be greater than the node's own version"
|
||||
}
|
||||
}
|
||||
private val busyNodeLatch: ReusableLatch = ReusableLatch()) : SingletonSerializeAsToken() {
|
||||
|
||||
private class StartedNodeImpl<out N : AbstractNode>(
|
||||
override val internals: N,
|
||||
@ -116,14 +117,12 @@ abstract class AbstractNode(config: NodeConfiguration,
|
||||
// low-performance prototyping period.
|
||||
protected abstract val serverThread: AffinityExecutor
|
||||
|
||||
protected lateinit var networkParameters: NetworkParameters
|
||||
private val cordappServices = MutableClassToInstanceMap.create<SerializeAsToken>()
|
||||
private val flowFactories = ConcurrentHashMap<Class<out FlowLogic<*>>, InitiatedFlowFactory<*>>()
|
||||
protected val partyKeys = mutableSetOf<KeyPair>()
|
||||
|
||||
protected val services: ServiceHubInternal get() = _services
|
||||
private lateinit var _services: ServiceHubInternalImpl
|
||||
protected lateinit var legalIdentity: PartyAndCertificate
|
||||
private lateinit var allIdentities: List<PartyAndCertificate>
|
||||
protected lateinit var info: NodeInfo
|
||||
protected var myNotaryIdentity: PartyAndCertificate? = null
|
||||
protected lateinit var checkpointStorage: CheckpointStorage
|
||||
@ -157,10 +156,6 @@ abstract class AbstractNode(config: NodeConfiguration,
|
||||
return SecureCordaRPCOps(services, smm, database, flowStarter)
|
||||
}
|
||||
|
||||
private fun saveOwnNodeInfo() {
|
||||
NodeInfoWatcher.saveToFile(configuration.baseDirectory, info, services.keyManagementService)
|
||||
}
|
||||
|
||||
private fun initCertificate() {
|
||||
if (configuration.devMode) {
|
||||
log.warn("Corda node is running in dev mode.")
|
||||
@ -169,33 +164,34 @@ abstract class AbstractNode(config: NodeConfiguration,
|
||||
validateKeystore()
|
||||
}
|
||||
|
||||
private fun makeSchemaService() = NodeSchemaService(cordappLoader)
|
||||
open fun generateNodeInfo() {
|
||||
check(started == null) { "Node has already been started" }
|
||||
initCertificate()
|
||||
log.info("Generating nodeInfo ...")
|
||||
val schemaService = makeSchemaService()
|
||||
initialiseDatabasePersistence(schemaService) {
|
||||
val transactionStorage = makeTransactionStorage()
|
||||
makeServices(schemaService, transactionStorage, StateLoaderImpl(transactionStorage))
|
||||
saveOwnNodeInfo()
|
||||
}
|
||||
initCertificate()
|
||||
initNodeInfo()
|
||||
}
|
||||
|
||||
open fun start(): StartedNode<AbstractNode> {
|
||||
check(started == null) { "Node has already been started" }
|
||||
initCertificate()
|
||||
log.info("Node starting up ...")
|
||||
val schemaService = makeSchemaService()
|
||||
initCertificate()
|
||||
val keyPairs = initNodeInfo()
|
||||
readNetworkParameters()
|
||||
val schemaService = NodeSchemaService(cordappLoader)
|
||||
// Do all of this in a database transaction so anything that might need a connection has one.
|
||||
val startedImpl = initialiseDatabasePersistence(schemaService) {
|
||||
val transactionStorage = makeTransactionStorage()
|
||||
val stateLoader = StateLoaderImpl(transactionStorage)
|
||||
val services = makeServices(schemaService, transactionStorage, stateLoader)
|
||||
saveOwnNodeInfo()
|
||||
val services = makeServices(keyPairs, schemaService, transactionStorage, stateLoader)
|
||||
smm = makeStateMachineManager()
|
||||
val flowStarter = FlowStarterImpl(serverThread, smm)
|
||||
val schedulerService = NodeSchedulerService(platformClock, this@AbstractNode.database, flowStarter, stateLoader, unfinishedSchedules = busyNodeLatch, serverThread = serverThread)
|
||||
val schedulerService = NodeSchedulerService(
|
||||
platformClock,
|
||||
this@AbstractNode.database,
|
||||
flowStarter,
|
||||
stateLoader,
|
||||
unfinishedSchedules = busyNodeLatch,
|
||||
serverThread = serverThread)
|
||||
if (serverThread is ExecutorService) {
|
||||
runOnStop += {
|
||||
// We wait here, even though any in-flight messages should have been drained away because the
|
||||
@ -232,6 +228,35 @@ abstract class AbstractNode(config: NodeConfiguration,
|
||||
}
|
||||
}
|
||||
|
||||
private fun initNodeInfo(): Set<KeyPair> {
|
||||
val (identity, identityKeyPair) = obtainIdentity(notaryConfig = null)
|
||||
val keyPairs = mutableSetOf(identityKeyPair)
|
||||
|
||||
myNotaryIdentity = configuration.notary?.let {
|
||||
if (it.isClusterConfig) {
|
||||
val (notaryIdentity, notaryIdentityKeyPair) = obtainIdentity(it)
|
||||
keyPairs += notaryIdentityKeyPair
|
||||
notaryIdentity
|
||||
} else {
|
||||
// In case of a single notary service myNotaryIdentity will be the node's single identity.
|
||||
identity
|
||||
}
|
||||
}
|
||||
|
||||
info = NodeInfo(
|
||||
myAddresses(),
|
||||
setOf(identity, myNotaryIdentity).filterNotNull(),
|
||||
versionInfo.platformVersion,
|
||||
platformClock.instant().toEpochMilli()
|
||||
)
|
||||
|
||||
NodeInfoWatcher.saveToFile(configuration.baseDirectory, info, identityKeyPair)
|
||||
|
||||
return keyPairs
|
||||
}
|
||||
|
||||
protected abstract fun myAddresses(): List<NetworkHostAndPort>
|
||||
|
||||
protected open fun makeStateMachineManager(): StateMachineManager {
|
||||
return StateMachineManagerImpl(
|
||||
services,
|
||||
@ -458,23 +483,25 @@ abstract class AbstractNode(config: NodeConfiguration,
|
||||
* Builds node internal, advertised, and plugin services.
|
||||
* Returns a list of tokenizable services to be added to the serialisation context.
|
||||
*/
|
||||
private fun makeServices(schemaService: SchemaService, transactionStorage: WritableTransactionStorage, stateLoader: StateLoader): MutableList<Any> {
|
||||
private fun makeServices(keyPairs: Set<KeyPair>, schemaService: SchemaService, transactionStorage: WritableTransactionStorage, stateLoader: StateLoader): MutableList<Any> {
|
||||
checkpointStorage = DBCheckpointStorage()
|
||||
val metrics = MetricRegistry()
|
||||
attachments = NodeAttachmentService(metrics)
|
||||
val cordappProvider = CordappProviderImpl(cordappLoader, attachments)
|
||||
_services = ServiceHubInternalImpl(schemaService, transactionStorage, stateLoader, MonitoringService(metrics), cordappProvider)
|
||||
legalIdentity = obtainIdentity(notaryConfig = null)
|
||||
// TODO We keep only notary identity as additional legalIdentity if we run it on a node . Multiple identities need more design thinking.
|
||||
myNotaryIdentity = getNotaryIdentity()
|
||||
allIdentities = listOf(legalIdentity, myNotaryIdentity).filterNotNull()
|
||||
network = makeMessagingService(legalIdentity)
|
||||
val addresses = myAddresses() // TODO There is no support for multiple IP addresses yet.
|
||||
info = NodeInfo(addresses, allIdentities, versionInfo.platformVersion, platformClock.instant().toEpochMilli())
|
||||
val networkMapCache = services.networkMapCache
|
||||
val identityService = makeIdentityService()
|
||||
val keyManagementService = makeKeyManagementService(identityService, keyPairs)
|
||||
_services = ServiceHubInternalImpl(
|
||||
identityService,
|
||||
keyManagementService,
|
||||
schemaService,
|
||||
transactionStorage,
|
||||
stateLoader,
|
||||
MonitoringService(metrics),
|
||||
cordappProvider)
|
||||
network = makeMessagingService()
|
||||
val tokenizableServices = mutableListOf(attachments, network, services.vaultService,
|
||||
services.keyManagementService, services.identityService, platformClock,
|
||||
services.auditService, services.monitoringService, networkMapCache, services.schemaService,
|
||||
services.auditService, services.monitoringService, services.networkMapCache, services.schemaService,
|
||||
services.transactionVerifierService, services.validatedTransactions, services.contractUpgradeService,
|
||||
services, cordappProvider, this)
|
||||
makeNetworkServices(tokenizableServices)
|
||||
@ -489,12 +516,6 @@ abstract class AbstractNode(config: NodeConfiguration,
|
||||
HibernateObserver.install(services.vaultService.rawUpdates, database.hibernateConfig)
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 fun getNotaryIdentity(): PartyAndCertificate? = configuration.notary?.let { obtainIdentity(it) }
|
||||
|
||||
@VisibleForTesting
|
||||
protected open fun acceptableLiveFiberCountOnStop(): Int = 0
|
||||
|
||||
@ -549,9 +570,6 @@ abstract class AbstractNode(config: NodeConfiguration,
|
||||
}
|
||||
}
|
||||
|
||||
/** Return list of node's addresses. It's overridden in MockNetwork as we don't have real addresses for MockNodes. */
|
||||
protected abstract fun myAddresses(): List<NetworkHostAndPort>
|
||||
|
||||
open protected fun checkNetworkMapIsInitialized() {
|
||||
if (!services.networkMapCache.loadDBSuccess) {
|
||||
// TODO: There should be a consistent approach to configuration error exceptions.
|
||||
@ -559,8 +577,15 @@ abstract class AbstractNode(config: NodeConfiguration,
|
||||
}
|
||||
}
|
||||
|
||||
protected open fun makeKeyManagementService(identityService: IdentityService): KeyManagementService {
|
||||
return PersistentKeyManagementService(identityService, partyKeys)
|
||||
protected open fun makeKeyManagementService(identityService: IdentityService, keyPairs: Set<KeyPair>): KeyManagementService {
|
||||
return PersistentKeyManagementService(identityService, keyPairs)
|
||||
}
|
||||
|
||||
private fun readNetworkParameters() {
|
||||
val file = configuration.baseDirectory / "network-parameters"
|
||||
networkParameters = file.readAll().deserialize<SignedData<NetworkParameters>>().verified()
|
||||
log.info(networkParameters.toString())
|
||||
check(networkParameters.minimumPlatformVersion <= versionInfo.platformVersion) { "Node is too old for the network" }
|
||||
}
|
||||
|
||||
private fun makeCoreNotaryService(notaryConfig: NotaryConfig): NotaryService {
|
||||
@ -593,13 +618,13 @@ abstract class AbstractNode(config: NodeConfiguration,
|
||||
}
|
||||
}
|
||||
|
||||
protected open fun makeIdentityService(trustRoot: X509Certificate,
|
||||
clientCa: CertificateAndKeyPair?,
|
||||
legalIdentity: PartyAndCertificate): IdentityService {
|
||||
val caCertificates: Array<X509Certificate> = listOf(legalIdentity.certificate, clientCa?.certificate?.cert)
|
||||
.filterNotNull()
|
||||
.toTypedArray()
|
||||
return PersistentIdentityService(allIdentities, trustRoot = trustRoot, caCertificates = *caCertificates)
|
||||
private fun makeIdentityService(): IdentityService {
|
||||
val trustStore = KeyStoreWrapper(configuration.trustStoreFile, configuration.trustStorePassword)
|
||||
val caKeyStore = KeyStoreWrapper(configuration.nodeKeystore, configuration.keyStorePassword)
|
||||
val trustRoot = trustStore.getX509Certificate(X509Utilities.CORDA_ROOT_CA)
|
||||
val clientCa = caKeyStore.certificateAndKeyPair(X509Utilities.CORDA_CLIENT_CA)
|
||||
val caCertificates = arrayOf(info.legalIdentitiesAndCerts[0].certificate, clientCa.certificate.cert)
|
||||
return PersistentIdentityService(info.legalIdentitiesAndCerts, trustRoot = trustRoot, caCertificates = *caCertificates)
|
||||
}
|
||||
|
||||
protected abstract fun makeTransactionVerifierService(): TransactionVerifierService
|
||||
@ -619,29 +644,23 @@ abstract class AbstractNode(config: NodeConfiguration,
|
||||
_started = null
|
||||
}
|
||||
|
||||
protected abstract fun makeMessagingService(legalIdentity: PartyAndCertificate): MessagingService
|
||||
protected abstract fun makeMessagingService(): MessagingService
|
||||
|
||||
protected abstract fun startMessagingService(rpcOps: RPCOps)
|
||||
|
||||
private fun obtainIdentity(notaryConfig: NotaryConfig?): PartyAndCertificate {
|
||||
private fun obtainIdentity(notaryConfig: NotaryConfig?): Pair<PartyAndCertificate, KeyPair> {
|
||||
val keyStore = KeyStoreWrapper(configuration.nodeKeystore, configuration.keyStorePassword)
|
||||
|
||||
val (id, singleName) = if (notaryConfig == null) {
|
||||
// Node's main identity
|
||||
val (id, singleName) = if (notaryConfig == null || !notaryConfig.isClusterConfig) {
|
||||
// Node's main identity or if it's a single node notary
|
||||
Pair("identity", myLegalName)
|
||||
} else {
|
||||
val notaryId = notaryConfig.run {
|
||||
NotaryService.constructId(validating, raft != null, bftSMaRt != null, custom)
|
||||
}
|
||||
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)
|
||||
}
|
||||
// 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"
|
||||
|
||||
@ -653,7 +672,7 @@ abstract class AbstractNode(config: NodeConfiguration,
|
||||
keyStore.signAndSaveNewKeyPair(singleName, privateKeyAlias, generateKeyPair())
|
||||
}
|
||||
|
||||
val (x509Cert, keys) = keyStore.certificateAndKeyPair(privateKeyAlias)
|
||||
val (x509Cert, keyPair) = keyStore.certificateAndKeyPair(privateKeyAlias)
|
||||
|
||||
// TODO: Use configuration to indicate composite key should be used instead of public key for the identity.
|
||||
val compositeKeyAlias = "$id-composite-key"
|
||||
@ -679,8 +698,8 @@ abstract class AbstractNode(config: NodeConfiguration,
|
||||
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))
|
||||
val certPath = CertificateFactory.getInstance("X509").generateCertPath(certificates)
|
||||
return Pair(PartyAndCertificate(certPath), keyPair)
|
||||
}
|
||||
|
||||
protected open fun generateKeyPair() = cryptoGenerateKeyPair()
|
||||
@ -689,6 +708,11 @@ abstract class AbstractNode(config: NodeConfiguration,
|
||||
}
|
||||
|
||||
private inner class ServiceHubInternalImpl(
|
||||
override val identityService: IdentityService,
|
||||
// Place the long term identity key in the KMS. Eventually, this is likely going to be separated again because
|
||||
// the KMS is meant for derived temporary keys used in transactions, and we're not supposed to sign things with
|
||||
// the identity key. But the infrastructure to make that easy isn't here yet.
|
||||
override val keyManagementService: KeyManagementService,
|
||||
override val schemaService: SchemaService,
|
||||
override val validatedTransactions: WritableTransactionStorage,
|
||||
private val stateLoader: StateLoader,
|
||||
@ -699,22 +723,16 @@ abstract class AbstractNode(config: NodeConfiguration,
|
||||
override val stateMachineRecordedTransactionMapping = DBTransactionMappingStorage()
|
||||
override val auditService = DummyAuditService()
|
||||
override val transactionVerifierService by lazy { makeTransactionVerifierService() }
|
||||
override val networkMapCache by lazy { NetworkMapCacheImpl(PersistentNetworkMapCache(this@AbstractNode.database, this@AbstractNode.configuration), identityService) }
|
||||
override val networkMapCache by lazy {
|
||||
NetworkMapCacheImpl(
|
||||
PersistentNetworkMapCache(
|
||||
this@AbstractNode.database,
|
||||
this@AbstractNode.configuration,
|
||||
networkParameters.notaries),
|
||||
identityService)
|
||||
}
|
||||
override val vaultService by lazy { makeVaultService(keyManagementService, stateLoader) }
|
||||
override val contractUpgradeService by lazy { ContractUpgradeServiceImpl() }
|
||||
|
||||
// Place the long term identity key in the KMS. Eventually, this is likely going to be separated again because
|
||||
// the KMS is meant for derived temporary keys used in transactions, and we're not supposed to sign things with
|
||||
// the identity key. But the infrastructure to make that easy isn't here yet.
|
||||
override val keyManagementService by lazy { makeKeyManagementService(identityService) }
|
||||
override val identityService by lazy {
|
||||
val trustStore = KeyStoreWrapper(configuration.trustStoreFile, configuration.trustStorePassword)
|
||||
val caKeyStore = KeyStoreWrapper(configuration.nodeKeystore, configuration.keyStorePassword)
|
||||
makeIdentityService(
|
||||
trustStore.getX509Certificate(X509Utilities.CORDA_ROOT_CA),
|
||||
caKeyStore.certificateAndKeyPair(X509Utilities.CORDA_CLIENT_CA),
|
||||
legalIdentity)
|
||||
}
|
||||
override val attachments: AttachmentStorage get() = this@AbstractNode.attachments
|
||||
override val networkService: MessagingService get() = network
|
||||
override val clock: Clock get() = platformClock
|
||||
|
@ -4,7 +4,6 @@ import com.codahale.metrics.JmxReporter
|
||||
import net.corda.core.CordaException
|
||||
import net.corda.core.concurrent.CordaFuture
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.identity.PartyAndCertificate
|
||||
import net.corda.core.internal.concurrent.openFuture
|
||||
import net.corda.core.internal.concurrent.thenMatch
|
||||
import net.corda.core.internal.uncheckedCast
|
||||
@ -45,7 +44,7 @@ import kotlin.system.exitProcess
|
||||
*
|
||||
* @param configuration This is typically loaded from a TypeSafe HOCON configuration file.
|
||||
*/
|
||||
open class Node(override val configuration: NodeConfiguration,
|
||||
open class Node(configuration: NodeConfiguration,
|
||||
versionInfo: VersionInfo,
|
||||
val initialiseSerialization: Boolean = true,
|
||||
cordappLoader: CordappLoader = makeCordappLoader(configuration)
|
||||
@ -131,11 +130,11 @@ open class Node(override val configuration: NodeConfiguration,
|
||||
|
||||
private lateinit var userService: RPCUserService
|
||||
|
||||
override fun makeMessagingService(legalIdentity: PartyAndCertificate): MessagingService {
|
||||
override fun makeMessagingService(): MessagingService {
|
||||
userService = RPCUserServiceImpl(configuration.rpcUsers)
|
||||
|
||||
val serverAddress = configuration.messagingServerAddress ?: makeLocalMessageBroker()
|
||||
val advertisedAddress = configuration.messagingServerAddress ?: getAdvertisedAddress()
|
||||
val advertisedAddress = info.addresses.single()
|
||||
|
||||
printBasicNodeInfo("Incoming connection address", advertisedAddress.toString())
|
||||
|
||||
@ -143,7 +142,7 @@ open class Node(override val configuration: NodeConfiguration,
|
||||
configuration,
|
||||
versionInfo,
|
||||
serverAddress,
|
||||
legalIdentity.owningKey,
|
||||
info.legalIdentities[0].owningKey,
|
||||
serverThread,
|
||||
database,
|
||||
services.monitoringService,
|
||||
@ -157,14 +156,18 @@ open class Node(override val configuration: NodeConfiguration,
|
||||
}
|
||||
}
|
||||
|
||||
override fun myAddresses(): List<NetworkHostAndPort> {
|
||||
return listOf(configuration.messagingServerAddress ?: getAdvertisedAddress())
|
||||
}
|
||||
|
||||
private fun getAdvertisedAddress(): NetworkHostAndPort {
|
||||
return with(configuration) {
|
||||
val useHost = if (detectPublicIp) {
|
||||
val host = if (detectPublicIp) {
|
||||
tryDetectIfNotPublicHost(p2pAddress.host) ?: p2pAddress.host
|
||||
} else {
|
||||
p2pAddress.host
|
||||
}
|
||||
NetworkHostAndPort(useHost, p2pAddress.port)
|
||||
NetworkHostAndPort(host, p2pAddress.port)
|
||||
}
|
||||
}
|
||||
|
||||
@ -196,11 +199,6 @@ open class Node(override val configuration: NodeConfiguration,
|
||||
(network as NodeMessagingClient).start(rpcOps, userService)
|
||||
}
|
||||
|
||||
override fun myAddresses(): List<NetworkHostAndPort> {
|
||||
val address = network.myAddress as ArtemisMessagingComponent.ArtemisPeerAddress
|
||||
return listOf(address.hostAndPort)
|
||||
}
|
||||
|
||||
/**
|
||||
* If the node is persisting to an embedded H2 database, then expose this via TCP with a JDBC URL of the form:
|
||||
* jdbc:h2:tcp://<host>:<port>/node
|
||||
|
@ -18,7 +18,6 @@ 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?
|
||||
val myLegalName: CordaX500Name
|
||||
val minimumPlatformVersion: Int
|
||||
val emailAddress: String
|
||||
val exportJMXto: String
|
||||
val dataSourceProperties: Properties
|
||||
@ -52,15 +51,17 @@ data class NotaryConfig(val validating: Boolean,
|
||||
"raft, bftSMaRt, and custom configs cannot be specified together"
|
||||
}
|
||||
}
|
||||
val isClusterConfig: Boolean get() = raft != null || bftSMaRt != null
|
||||
}
|
||||
|
||||
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
|
||||
data class BFTSMaRtConfiguration(
|
||||
val replicaId: Int,
|
||||
val clusterAddresses: List<NetworkHostAndPort>,
|
||||
val debug: Boolean = false,
|
||||
val exposeRaces: Boolean = false
|
||||
) {
|
||||
init {
|
||||
require(replicaId >= 0) { "replicaId cannot be negative" }
|
||||
@ -85,7 +86,6 @@ data class NodeConfigurationImpl(
|
||||
override val dataSourceProperties: Properties,
|
||||
override val database: Properties?,
|
||||
override val certificateSigningService: URL,
|
||||
override val minimumPlatformVersion: Int = 1,
|
||||
override val rpcUsers: List<User>,
|
||||
override val verifierType: VerifierType,
|
||||
// TODO typesafe config supports the notion of durations. Make use of that by mapping it to java.time.Duration.
|
||||
@ -113,8 +113,6 @@ data class NodeConfigurationImpl(
|
||||
// This is a sanity feature do not remove.
|
||||
require(!useTestClock || devMode) { "Cannot use test clock outside of dev mode" }
|
||||
require(devModeOptions == null || devMode) { "Cannot use devModeOptions outside of dev mode" }
|
||||
require(myLegalName.commonName == null) { "Common name must be null: $myLegalName" }
|
||||
require(minimumPlatformVersion >= 1) { "minimumPlatformVersion cannot be less than 1" }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2,9 +2,9 @@ package net.corda.node.services.network
|
||||
|
||||
import net.corda.cordform.CordformNode
|
||||
import net.corda.core.crypto.SignedData
|
||||
import net.corda.core.crypto.sign
|
||||
import net.corda.core.internal.*
|
||||
import net.corda.core.node.NodeInfo
|
||||
import net.corda.core.node.services.KeyManagementService
|
||||
import net.corda.core.serialization.deserialize
|
||||
import net.corda.core.serialization.serialize
|
||||
import net.corda.core.utilities.loggerFor
|
||||
@ -15,6 +15,7 @@ import rx.Scheduler
|
||||
import rx.schedulers.Schedulers
|
||||
import java.io.IOException
|
||||
import java.nio.file.Path
|
||||
import java.security.KeyPair
|
||||
import java.util.concurrent.TimeUnit
|
||||
import kotlin.streams.toList
|
||||
|
||||
@ -48,13 +49,13 @@ class NodeInfoWatcher(private val nodePath: Path,
|
||||
*
|
||||
* @param path the path where to write the file, if non-existent it will be created.
|
||||
* @param nodeInfo the NodeInfo to serialize.
|
||||
* @param keyManager a KeyManagementService used to sign the NodeInfo data.
|
||||
* @param signingKey used to sign the NodeInfo data.
|
||||
*/
|
||||
fun saveToFile(path: Path, nodeInfo: NodeInfo, keyManager: KeyManagementService) {
|
||||
fun saveToFile(path: Path, nodeInfo: NodeInfo, signingKey: KeyPair) {
|
||||
try {
|
||||
path.createDirectories()
|
||||
val serializedBytes = nodeInfo.serialize()
|
||||
val regSig = keyManager.sign(serializedBytes.bytes, nodeInfo.legalIdentities.first().owningKey)
|
||||
val regSig = signingKey.sign(serializedBytes.bytes)
|
||||
val signedData = SignedData(serializedBytes, regSig)
|
||||
signedData.serialize().open().copyTo(
|
||||
path / "${NodeInfoFilesCopier.NODE_INFO_FILE_NAME_PREFIX}${serializedBytes.hash}")
|
||||
|
@ -10,9 +10,9 @@ import net.corda.core.internal.bufferUntilSubscribed
|
||||
import net.corda.core.internal.concurrent.openFuture
|
||||
import net.corda.core.messaging.DataFeed
|
||||
import net.corda.core.node.NodeInfo
|
||||
import net.corda.core.node.NotaryInfo
|
||||
import net.corda.core.node.services.IdentityService
|
||||
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
|
||||
@ -32,7 +32,10 @@ import java.util.*
|
||||
import javax.annotation.concurrent.ThreadSafe
|
||||
import kotlin.collections.HashMap
|
||||
|
||||
class NetworkMapCacheImpl(networkMapCacheBase: NetworkMapCacheBaseInternal, private val identityService: IdentityService) : NetworkMapCacheBaseInternal by networkMapCacheBase, NetworkMapCacheInternal {
|
||||
class NetworkMapCacheImpl(
|
||||
networkMapCacheBase: NetworkMapCacheBaseInternal,
|
||||
private val identityService: IdentityService
|
||||
) : NetworkMapCacheBaseInternal by networkMapCacheBase, NetworkMapCacheInternal {
|
||||
init {
|
||||
networkMapCacheBase.allNodes.forEach { it.legalIdentitiesAndCerts.forEach { identityService.verifyAndRegisterIdentity(it) } }
|
||||
networkMapCacheBase.changed.subscribe { mapChange ->
|
||||
@ -57,13 +60,16 @@ class NetworkMapCacheImpl(networkMapCacheBase: NetworkMapCacheBaseInternal, priv
|
||||
* Extremely simple in-memory cache of the network map.
|
||||
*/
|
||||
@ThreadSafe
|
||||
open class PersistentNetworkMapCache(private val database: CordaPersistence, configuration: NodeConfiguration) : SingletonSerializeAsToken(), NetworkMapCacheBaseInternal {
|
||||
open class PersistentNetworkMapCache(
|
||||
private val database: CordaPersistence,
|
||||
val configuration: NodeConfiguration,
|
||||
notaries: List<NotaryInfo>
|
||||
) : SingletonSerializeAsToken(), NetworkMapCacheBaseInternal {
|
||||
companion object {
|
||||
val logger = loggerFor<PersistentNetworkMapCache>()
|
||||
}
|
||||
|
||||
// TODO Small explanation, partyNodes and registeredNodes is left in memory as it was before, because it will be removed in
|
||||
// next PR that gets rid of services. These maps are used only for queries by service.
|
||||
// TODO Cleanup registered and party nodes
|
||||
protected val registeredNodes: MutableMap<PublicKey, NodeInfo> = Collections.synchronizedMap(HashMap())
|
||||
protected val partyNodes: MutableList<NodeInfo> get() = registeredNodes.map { it.value }.toMutableList()
|
||||
private val _changed = PublishSubject.create<MapChange>()
|
||||
@ -77,22 +83,9 @@ open class PersistentNetworkMapCache(private val database: CordaPersistence, con
|
||||
override val nodeReady: CordaFuture<Void?> get() = _registrationFuture
|
||||
private var _loadDBSuccess: Boolean = false
|
||||
override val loadDBSuccess get() = _loadDBSuccess
|
||||
// TODO From the NetworkMapService redesign doc: Remove the concept of network services.
|
||||
// As a temporary hack, just assume for now that every network has a notary service named "Notary Service" that can be looked up in the map.
|
||||
// This should eliminate the only required usage of services.
|
||||
// It is ensured on node startup when constructing a notary that the name contains "notary".
|
||||
override val notaryIdentities: List<Party>
|
||||
get() {
|
||||
return partyNodes
|
||||
.flatMap {
|
||||
// TODO: validate notary identity certificates before loading into network map cache.
|
||||
// Notary certificates have to be signed by the doorman directly
|
||||
it.legalIdentities
|
||||
}
|
||||
.filter { it.name.commonName?.startsWith(NotaryService.ID_PREFIX) ?: false }
|
||||
.toSet() // Distinct, because of distributed service nodes
|
||||
.sortedBy { it.name.toString() }
|
||||
}
|
||||
|
||||
override val notaryIdentities: List<Party> = notaries.map { it.identity }
|
||||
private val validatingNotaries = notaries.mapNotNull { if (it.validating) it.identity else null }
|
||||
|
||||
private val nodeInfoSerializer = NodeInfoWatcher(configuration.baseDirectory,
|
||||
configuration.additionalNodeInfoPollingFrequencyMsec)
|
||||
@ -107,6 +100,8 @@ open class PersistentNetworkMapCache(private val database: CordaPersistence, con
|
||||
nodeInfoSerializer.nodeInfoUpdates().subscribe { node -> addNode(node) }
|
||||
}
|
||||
|
||||
override fun isValidatingNotary(party: Party): Boolean = party in validatingNotaries
|
||||
|
||||
override fun getPartyInfo(party: Party): PartyInfo? {
|
||||
val nodes = database.transaction { queryByIdentityKey(session, party.owningKey) }
|
||||
if (nodes.size == 1 && nodes[0].isLegalIdentity(party)) {
|
||||
@ -286,7 +281,6 @@ open class PersistentNetworkMapCache(private val database: CordaPersistence, con
|
||||
return NodeInfoSchemaV1.PersistentNodeInfo(
|
||||
id = 0,
|
||||
addresses = nodeInfo.addresses.map { NodeInfoSchemaV1.DBHostAndPort.fromHostAndPort(it) },
|
||||
// TODO Another ugly hack with special first identity...
|
||||
legalIdentitiesAndCerts = nodeInfo.legalIdentitiesAndCerts.mapIndexed { idx, elem ->
|
||||
NodeInfoSchemaV1.DBPartyAndCertificate(elem, isMain = idx == 0)
|
||||
},
|
||||
|
@ -5,10 +5,10 @@ import net.corda.core.crypto.generateKeyPair
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.core.internal.cert
|
||||
import net.corda.core.internal.createDirectories
|
||||
import net.corda.core.internal.div
|
||||
import net.corda.core.utilities.loggerFor
|
||||
import net.corda.core.utilities.trace
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Path
|
||||
|
||||
object ServiceIdentityGenerator {
|
||||
@ -20,13 +20,12 @@ object ServiceIdentityGenerator {
|
||||
* This method should be called *before* any of the nodes are started.
|
||||
*
|
||||
* @param dirs List of node directories to place the generated identity and key pairs in.
|
||||
* @param serviceId The service id of the distributed service.
|
||||
* @param serviceName The legal name of the distributed service, with service id as CN.
|
||||
* @param serviceName The legal name of the distributed service.
|
||||
* @param threshold The threshold for the generated group [CompositeKey].
|
||||
*/
|
||||
// TODO: This needs to write out to the key store, not just files on disk
|
||||
fun generateToDisk(dirs: List<Path>,
|
||||
serviceName: CordaX500Name,
|
||||
serviceId: String,
|
||||
threshold: Int = 1): Party {
|
||||
log.trace { "Generating a group identity \"serviceName\" for nodes: ${dirs.joinToString()}" }
|
||||
val keyPairs = (1..dirs.size).map { generateKeyPair() }
|
||||
@ -39,9 +38,8 @@ object ServiceIdentityGenerator {
|
||||
keyPairs.zip(dirs) { keyPair, dir ->
|
||||
val serviceKeyCert = X509Utilities.createCertificate(CertificateType.CLIENT_CA, issuer.certificate, issuer.keyPair, serviceName, keyPair.public)
|
||||
val compositeKeyCert = X509Utilities.createCertificate(CertificateType.CLIENT_CA, issuer.certificate, issuer.keyPair, serviceName, notaryKey)
|
||||
val certPath = Files.createDirectories(dir / "certificates") / "distributedService.jks"
|
||||
val certPath = (dir / "certificates").createDirectories() / "distributedService.jks"
|
||||
val keystore = loadOrCreateKeyStore(certPath, "cordacadevpass")
|
||||
val serviceId = serviceName.commonName
|
||||
keystore.setCertificateEntry("$serviceId-composite-key", compositeKeyCert.cert)
|
||||
keystore.setKeyEntry("$serviceId-private-key", keyPair.private, "cordacadevkeypass".toCharArray(), arrayOf(serviceKeyCert.cert, issuer.certificate.cert, rootCert))
|
||||
keystore.save(certPath, "cordacadevpass")
|
||||
|
@ -55,22 +55,22 @@ class CordaRPCOpsImplTest {
|
||||
val testJar = "net/corda/node/testing/test.jar"
|
||||
}
|
||||
|
||||
lateinit var mockNet: MockNetwork
|
||||
lateinit var aliceNode: StartedNode<MockNode>
|
||||
lateinit var notaryNode: StartedNode<MockNode>
|
||||
lateinit var notary: Party
|
||||
lateinit var rpc: CordaRPCOps
|
||||
lateinit var stateMachineUpdates: Observable<StateMachineUpdate>
|
||||
lateinit var transactions: Observable<SignedTransaction>
|
||||
lateinit var vaultTrackCash: Observable<Vault.Update<Cash.State>>
|
||||
private lateinit var mockNet: MockNetwork
|
||||
private lateinit var aliceNode: StartedNode<MockNode>
|
||||
private lateinit var notaryNode: StartedNode<MockNode>
|
||||
private lateinit var notary: Party
|
||||
private lateinit var rpc: CordaRPCOps
|
||||
private lateinit var stateMachineUpdates: Observable<StateMachineUpdate>
|
||||
private lateinit var transactions: Observable<SignedTransaction>
|
||||
private lateinit var vaultTrackCash: Observable<Vault.Update<Cash.State>>
|
||||
|
||||
private val user = User("user", "pwd", permissions = emptySet())
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
mockNet = MockNetwork(cordappPackages = listOf("net.corda.finance.contracts.asset"))
|
||||
aliceNode = mockNet.createNode()
|
||||
notaryNode = mockNet.createNotaryNode(validating = false)
|
||||
aliceNode = mockNet.createNode()
|
||||
rpc = SecureCordaRPCOps(aliceNode.services, aliceNode.smm, aliceNode.database, aliceNode.services)
|
||||
CURRENT_RPC_CONTEXT.set(RpcContext(user))
|
||||
|
||||
|
@ -615,18 +615,25 @@ class TwoPartyTradeFlowTests(val anonymous: Boolean) {
|
||||
notaryNode: StartedNode<*>,
|
||||
vararg extraSigningNodes: StartedNode<*>): Map<SecureHash, SignedTransaction> {
|
||||
|
||||
val notaryParty = notaryNode.info.legalIdentities[0]
|
||||
val signed = wtxToSign.map {
|
||||
val id = it.id
|
||||
val sigs = mutableListOf<TransactionSignature>()
|
||||
val nodeKey = node.info.chooseIdentity().owningKey
|
||||
sigs.add(node.services.keyManagementService.sign(SignableData(id, SignatureMetadata(1, Crypto.findSignatureScheme(nodeKey).schemeNumberID)), nodeKey))
|
||||
sigs.add(notaryNode.services.keyManagementService.sign(SignableData(id, SignatureMetadata(1,
|
||||
Crypto.findSignatureScheme(notaryNode.info.legalIdentities[1].owningKey).schemeNumberID)), notaryNode.info.legalIdentities[1].owningKey))
|
||||
sigs += node.services.keyManagementService.sign(
|
||||
SignableData(id, SignatureMetadata(1, Crypto.findSignatureScheme(nodeKey).schemeNumberID)),
|
||||
nodeKey
|
||||
)
|
||||
sigs += notaryNode.services.keyManagementService.sign(
|
||||
SignableData(id, SignatureMetadata(1, Crypto.findSignatureScheme(notaryParty.owningKey).schemeNumberID)),
|
||||
notaryParty.owningKey
|
||||
)
|
||||
extraSigningNodes.forEach { currentNode ->
|
||||
sigs.add(currentNode.services.keyManagementService.sign(
|
||||
SignableData(id, SignatureMetadata(1, Crypto.findSignatureScheme(currentNode.info.chooseIdentity().owningKey).schemeNumberID)),
|
||||
sigs += currentNode.services.keyManagementService.sign(
|
||||
SignableData(id, SignatureMetadata(
|
||||
1,
|
||||
Crypto.findSignatureScheme(currentNode.info.chooseIdentity().owningKey).schemeNumberID)),
|
||||
currentNode.info.chooseIdentity().owningKey)
|
||||
)
|
||||
}
|
||||
SignedTransaction(it, sigs)
|
||||
}
|
||||
|
@ -1,5 +1,7 @@
|
||||
package net.corda.node.services
|
||||
|
||||
import com.nhaarman.mockito_kotlin.doReturn
|
||||
import com.nhaarman.mockito_kotlin.whenever
|
||||
import net.corda.core.contracts.*
|
||||
import net.corda.core.crypto.generateKeyPair
|
||||
import net.corda.core.flows.NotaryChangeFlow
|
||||
@ -13,8 +15,12 @@ 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.testing.*
|
||||
import net.corda.node.services.config.NotaryConfig
|
||||
import net.corda.testing.DUMMY_NOTARY
|
||||
import net.corda.testing.chooseIdentity
|
||||
import net.corda.testing.contracts.DummyContract
|
||||
import net.corda.testing.dummyCommand
|
||||
import net.corda.testing.getTestPartyAndCertificate
|
||||
import net.corda.testing.node.MockNetwork
|
||||
import net.corda.testing.node.MockNodeParameters
|
||||
import org.assertj.core.api.Assertions.assertThatExceptionOfType
|
||||
@ -27,24 +33,32 @@ import kotlin.test.assertEquals
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
class NotaryChangeTests {
|
||||
lateinit var mockNet: MockNetwork
|
||||
lateinit var oldNotaryNode: StartedNode<MockNetwork.MockNode>
|
||||
lateinit var newNotaryNode: StartedNode<MockNetwork.MockNode>
|
||||
lateinit var clientNodeA: StartedNode<MockNetwork.MockNode>
|
||||
lateinit var clientNodeB: StartedNode<MockNetwork.MockNode>
|
||||
lateinit var newNotaryParty: Party
|
||||
lateinit var oldNotaryParty: Party
|
||||
private lateinit var mockNet: MockNetwork
|
||||
private lateinit var oldNotaryNode: StartedNode<MockNetwork.MockNode>
|
||||
private lateinit var clientNodeA: StartedNode<MockNetwork.MockNode>
|
||||
private lateinit var clientNodeB: StartedNode<MockNetwork.MockNode>
|
||||
private lateinit var newNotaryParty: Party
|
||||
private lateinit var oldNotaryParty: Party
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
mockNet = MockNetwork(cordappPackages = listOf("net.corda.testing.contracts"))
|
||||
oldNotaryNode = mockNet.createNotaryNode(MockNodeParameters(legalName = DUMMY_NOTARY.name))
|
||||
val (oldNotaryNode, newNotaryNode) = listOf(
|
||||
createUnstartedNotary(DUMMY_NOTARY.name),
|
||||
createUnstartedNotary(DUMMY_NOTARY.name.copy(organisation = "Dummy Notary 2"))
|
||||
).map { it.start() }
|
||||
this.oldNotaryNode = oldNotaryNode
|
||||
clientNodeA = mockNet.createNode()
|
||||
clientNodeB = mockNet.createNode()
|
||||
newNotaryNode = mockNet.createNotaryNode(MockNodeParameters(legalName = DUMMY_NOTARY.name.copy(organisation = "Dummy Notary 2")))
|
||||
mockNet.runNetwork() // Clear network map registration messages
|
||||
oldNotaryParty = newNotaryNode.services.networkMapCache.getNotary(DUMMY_NOTARY_SERVICE_NAME)!!
|
||||
newNotaryParty = newNotaryNode.services.networkMapCache.getNotary(DUMMY_NOTARY_SERVICE_NAME.copy(organisation = "Dummy Notary 2"))!!
|
||||
oldNotaryParty = newNotaryNode.services.networkMapCache.getNotary(DUMMY_NOTARY.name)!!
|
||||
newNotaryParty = newNotaryNode.services.networkMapCache.getNotary(DUMMY_NOTARY.name.copy(organisation = "Dummy Notary 2"))!!
|
||||
}
|
||||
|
||||
private fun createUnstartedNotary(name: CordaX500Name): MockNetwork.MockNode {
|
||||
return mockNet.createUnstartedNode(MockNodeParameters(
|
||||
legalName = name,
|
||||
configOverrides = { doReturn(NotaryConfig(validating = true)).whenever(it).notary }
|
||||
))
|
||||
}
|
||||
|
||||
@After
|
||||
|
@ -42,6 +42,7 @@ class ArtemisMessagingTests {
|
||||
companion object {
|
||||
const val TOPIC = "platform.self"
|
||||
}
|
||||
|
||||
@Rule
|
||||
@JvmField
|
||||
val testSerialization = SerializationEnvironmentRule()
|
||||
@ -50,21 +51,21 @@ class ArtemisMessagingTests {
|
||||
@JvmField
|
||||
val temporaryFolder = TemporaryFolder()
|
||||
|
||||
val serverPort = freePort()
|
||||
val rpcPort = freePort()
|
||||
val identity = generateKeyPair()
|
||||
private val serverPort = freePort()
|
||||
private val rpcPort = freePort()
|
||||
private val identity = generateKeyPair()
|
||||
|
||||
lateinit var config: NodeConfiguration
|
||||
lateinit var database: CordaPersistence
|
||||
lateinit var userService: RPCUserService
|
||||
lateinit var networkMapRegistrationFuture: CordaFuture<Unit>
|
||||
private lateinit var config: NodeConfiguration
|
||||
private lateinit var database: CordaPersistence
|
||||
private lateinit var userService: RPCUserService
|
||||
private lateinit var networkMapRegistrationFuture: CordaFuture<Unit>
|
||||
|
||||
var messagingClient: NodeMessagingClient? = null
|
||||
var messagingServer: ArtemisMessagingServer? = null
|
||||
private var messagingClient: NodeMessagingClient? = null
|
||||
private var messagingServer: ArtemisMessagingServer? = null
|
||||
|
||||
lateinit var networkMapCache: NetworkMapCacheImpl
|
||||
private lateinit var networkMapCache: NetworkMapCacheImpl
|
||||
|
||||
val rpcOps = object : RPCOps {
|
||||
private val rpcOps = object : RPCOps {
|
||||
override val protocolVersion: Int get() = throw UnsupportedOperationException()
|
||||
}
|
||||
|
||||
@ -78,7 +79,7 @@ class ArtemisMessagingTests {
|
||||
LogHelper.setLevel(PersistentUniquenessProvider::class)
|
||||
database = configureDatabase(makeTestDataSourceProperties(), makeTestDatabaseProperties(), ::makeTestIdentityService)
|
||||
networkMapRegistrationFuture = doneFuture(Unit)
|
||||
networkMapCache = NetworkMapCacheImpl(PersistentNetworkMapCache(database, config), rigorousMock())
|
||||
networkMapCache = NetworkMapCacheImpl(PersistentNetworkMapCache(database, config, emptyList()), rigorousMock())
|
||||
}
|
||||
|
||||
@After
|
||||
|
@ -72,18 +72,14 @@ class FlowFrameworkTests {
|
||||
|
||||
@Before
|
||||
fun start() {
|
||||
mockNet = MockNetwork(servicePeerAllocationStrategy = RoundRobin(), cordappPackages = listOf("net.corda.finance.contracts", "net.corda.testing.contracts"))
|
||||
mockNet = MockNetwork(
|
||||
servicePeerAllocationStrategy = RoundRobin(),
|
||||
cordappPackages = listOf("net.corda.finance.contracts", "net.corda.testing.contracts"))
|
||||
val notary = mockNet.createNotaryNode()
|
||||
aliceNode = mockNet.createNode(MockNodeParameters(legalName = ALICE_NAME))
|
||||
bobNode = mockNet.createNode(MockNodeParameters(legalName = BOB_NAME))
|
||||
mockNet.runNetwork()
|
||||
|
||||
// We intentionally create our own notary and ignore the one provided by the network
|
||||
// Note that these notaries don't operate correctly as they don't share their state. They are only used for testing
|
||||
// service addressing.
|
||||
val notary = mockNet.createNotaryNode()
|
||||
|
||||
receivedSessionMessagesObservable().forEach { receivedSessionMessages += it }
|
||||
mockNet.runNetwork()
|
||||
|
||||
// Extract identities
|
||||
alice = aliceNode.info.singleIdentity()
|
||||
|
Reference in New Issue
Block a user