Removed the startNotary methods from NodeBasedTest, and moved it into an internal package

This commit is in preparation for the upcoming network parameters work.
This commit is contained in:
Shams Asari 2017-10-30 21:53:12 +00:00
parent 3aeacef3bf
commit 6ebba9dea1
13 changed files with 336 additions and 534 deletions

3
.idea/compiler.xml generated
View File

@ -42,10 +42,13 @@
<module name="explorer-capsule_test" target="1.6" /> <module name="explorer-capsule_test" target="1.6" />
<module name="explorer_main" target="1.8" /> <module name="explorer_main" target="1.8" />
<module name="explorer_test" target="1.8" /> <module name="explorer_test" target="1.8" />
<module name="finance_integrationTest" target="1.8" />
<module name="finance_main" target="1.8" /> <module name="finance_main" target="1.8" />
<module name="finance_test" target="1.8" /> <module name="finance_test" target="1.8" />
<module name="graphs_main" target="1.8" /> <module name="graphs_main" target="1.8" />
<module name="graphs_test" target="1.8" /> <module name="graphs_test" target="1.8" />
<module name="irs-demo-cordapp_main" target="1.8" />
<module name="irs-demo-cordapp_test" target="1.8" />
<module name="irs-demo_integrationTest" target="1.8" /> <module name="irs-demo_integrationTest" target="1.8" />
<module name="irs-demo_main" target="1.8" /> <module name="irs-demo_main" target="1.8" />
<module name="irs-demo_test" target="1.8" /> <module name="irs-demo_test" target="1.8" />

View File

@ -1,6 +1,5 @@
package net.corda.client.rpc; package net.corda.client.rpc;
import net.corda.core.concurrent.CordaFuture;
import net.corda.core.contracts.Amount; import net.corda.core.contracts.Amount;
import net.corda.core.messaging.CordaRPCOps; import net.corda.core.messaging.CordaRPCOps;
import net.corda.core.messaging.FlowHandle; import net.corda.core.messaging.FlowHandle;
@ -13,7 +12,7 @@ import net.corda.node.internal.Node;
import net.corda.node.internal.StartedNode; import net.corda.node.internal.StartedNode;
import net.corda.nodeapi.User; import net.corda.nodeapi.User;
import net.corda.testing.CoreTestUtils; import net.corda.testing.CoreTestUtils;
import net.corda.testing.node.NodeBasedTest; import net.corda.testing.internal.NodeBasedTest;
import org.junit.After; import org.junit.After;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
@ -51,8 +50,7 @@ public class CordaRPCJavaClientTest extends NodeBasedTest {
@Before @Before
public void setUp() throws ExecutionException, InterruptedException { public void setUp() throws ExecutionException, InterruptedException {
CordaFuture<StartedNode<Node>> nodeFuture = startNotaryNode(getALICE().getName(), singletonList(rpcUser), true); node = startNode(getALICE().getName(), 1, singletonList(rpcUser));
node = nodeFuture.get();
client = new CordaRPCClient(requireNonNull(node.getInternals().getConfiguration().getRpcAddress())); client = new CordaRPCClient(requireNonNull(node.getInternals().getConfiguration().getRpcAddress()));
} }

View File

@ -2,89 +2,38 @@ package net.corda.client.rpc
import co.paralleluniverse.fibers.Suspendable import co.paralleluniverse.fibers.Suspendable
import com.esotericsoftware.kryo.KryoException import com.esotericsoftware.kryo.KryoException
import net.corda.core.flows.* import net.corda.core.flows.FlowLogic
import net.corda.core.identity.Party import net.corda.core.flows.StartableByRPC
import net.corda.core.messaging.startFlow import net.corda.core.messaging.startFlow
import net.corda.core.serialization.CordaSerializable import net.corda.core.serialization.CordaSerializable
import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.getOrThrow
import net.corda.core.utilities.loggerFor import net.corda.testing.ALICE
import net.corda.core.utilities.unwrap import net.corda.testing.driver.driver
import net.corda.node.internal.Node import org.assertj.core.api.Assertions.assertThatExceptionOfType
import net.corda.node.internal.StartedNode
import net.corda.nodeapi.User
import net.corda.testing.*
import net.corda.testing.node.NodeBasedTest
import org.junit.After
import org.junit.Before
import org.junit.Rule
import org.junit.Test import org.junit.Test
import org.junit.rules.ExpectedException
@CordaSerializable class BlacklistKotlinClosureTest {
data class Packet(val x: () -> Long)
class BlacklistKotlinClosureTest : NodeBasedTest(listOf("net.corda.client.rpc")) {
companion object { companion object {
@Suppress("UNUSED") val logger = loggerFor<BlacklistKotlinClosureTest>()
const val EVIL: Long = 666 const val EVIL: Long = 666
} }
@StartableByRPC @StartableByRPC
@InitiatingFlow class FlowC(@Suppress("unused") private val data: Packet) : FlowLogic<Unit>() {
class FlowC(private val remoteParty: Party, private val data: Packet) : FlowLogic<Unit>() {
@Suspendable @Suspendable
override fun call() { override fun call() = Unit
val session = initiateFlow(remoteParty)
val x = session.sendAndReceive<Packet>(data).unwrap { x -> x }
logger.info("FlowC: ${x.x()}")
}
} }
@InitiatedBy(FlowC::class) @CordaSerializable
class RemoteFlowC(private val session: FlowSession) : FlowLogic<Unit>() { data class Packet(val x: () -> Long)
@Suspendable
override fun call() {
val packet = session.receive<Packet>().unwrap { x -> x }
logger.info("RemoteFlowC: ${packet.x() + 1}")
session.send(Packet({ packet.x() + 1 }))
}
}
@JvmField
@Rule
val expectedEx: ExpectedException = ExpectedException.none()
private val rpcUser = User("user1", "test", permissions = setOf("ALL"))
private lateinit var aliceNode: StartedNode<Node>
private lateinit var bobNode: StartedNode<Node>
private lateinit var aliceClient: CordaRPCClient
private var connection: CordaRPCConnection? = null
private fun login(username: String, password: String) {
connection = aliceClient.start(username, password)
}
@Before
fun setUp() {
aliceNode = startNode(ALICE.name, rpcUsers = listOf(rpcUser)).getOrThrow()
bobNode = startNode(BOB.name, rpcUsers = listOf(rpcUser)).getOrThrow()
bobNode.registerInitiatedFlow(RemoteFlowC::class.java)
aliceClient = CordaRPCClient(aliceNode.internals.configuration.rpcAddress!!)
}
@After
fun done() {
connection?.close()
bobNode.internals.stop()
aliceNode.internals.stop()
}
@Test @Test
fun `closure sent via RPC`() { fun `closure sent via RPC`() {
login(rpcUser.username, rpcUser.password) driver(startNodesInProcess = true) {
val proxy = connection!!.proxy val rpc = startNode(providedName = ALICE.name).getOrThrow().rpc
expectedEx.expect(KryoException::class.java) val packet = Packet { EVIL }
expectedEx.expectMessage("is not annotated or on the whitelist, so cannot be used in serialization") assertThatExceptionOfType(KryoException::class.java)
proxy.startFlow(::FlowC, bobNode.info.chooseIdentity(), Packet{ EVIL }).returnValue.getOrThrow() .isThrownBy { rpc.startFlow(::FlowC, packet) }
.withMessageContaining("is not annotated or on the whitelist, so cannot be used in serialization")
}
} }
} }

View File

@ -24,7 +24,7 @@ import net.corda.node.services.FlowPermissions.Companion.startFlowPermission
import net.corda.nodeapi.User import net.corda.nodeapi.User
import net.corda.testing.ALICE import net.corda.testing.ALICE
import net.corda.testing.chooseIdentity import net.corda.testing.chooseIdentity
import net.corda.testing.node.NodeBasedTest import net.corda.testing.internal.NodeBasedTest
import org.apache.activemq.artemis.api.core.ActiveMQSecurityException import org.apache.activemq.artemis.api.core.ActiveMQSecurityException
import org.assertj.core.api.Assertions.assertThatExceptionOfType import org.assertj.core.api.Assertions.assertThatExceptionOfType
import org.junit.After import org.junit.After
@ -49,7 +49,7 @@ class CordaRPCClientTest : NodeBasedTest(listOf("net.corda.finance.contracts", C
@Before @Before
fun setUp() { fun setUp() {
node = startNotaryNode(ALICE.name, rpcUsers = listOf(rpcUser)).getOrThrow() node = startNode(ALICE.name, rpcUsers = listOf(rpcUser))
client = CordaRPCClient(node.internals.configuration.rpcAddress!!) client = CordaRPCClient(node.internals.configuration.rpcAddress!!)
} }

View File

@ -8,30 +8,29 @@ import net.corda.core.flows.NotaryFlow
import net.corda.core.identity.CordaX500Name import net.corda.core.identity.CordaX500Name
import net.corda.core.identity.Party import net.corda.core.identity.Party
import net.corda.core.internal.concurrent.map import net.corda.core.internal.concurrent.map
import net.corda.core.internal.concurrent.transpose
import net.corda.core.transactions.TransactionBuilder import net.corda.core.transactions.TransactionBuilder
import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.getOrThrow
import net.corda.node.internal.StartedNode import net.corda.node.internal.StartedNode
import net.corda.node.services.transactions.RaftValidatingNotaryService 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.contracts.DummyContract
import net.corda.testing.node.NodeBasedTest import net.corda.testing.driver.NodeHandle
import net.corda.testing.driver.driver
import net.corda.testing.dummyCommand
import org.junit.Test import org.junit.Test
import java.util.* import java.util.*
import kotlin.test.assertEquals import kotlin.test.assertEquals
import kotlin.test.assertFailsWith import kotlin.test.assertFailsWith
class RaftNotaryServiceTests : NodeBasedTest(listOf("net.corda.testing.contracts")) { class RaftNotaryServiceTests {
private val notaryName = CordaX500Name(RaftValidatingNotaryService.id, "RAFT Notary Service", "London", "GB") private val notaryName = CordaX500Name(RaftValidatingNotaryService.id, "RAFT Notary Service", "London", "GB")
@Test @Test
fun `detect double spend`() { fun `detect double spend`() {
val (bankA) = listOf( driver(startNodesInProcess = true, extraCordappPackagesToScan = listOf("net.corda.testing.contracts")) {
startNode(DUMMY_BANK_A.name), val (notaryParty) = startNotaryCluster(notaryName, 3).getOrThrow()
startNotaryCluster(notaryName, 3).map { it.first() } val bankA = startNode(providedName = DUMMY_BANK_A.name).map { (it as NodeHandle.InProcess).node }.getOrThrow()
).transpose().getOrThrow()
val notaryParty = bankA.services.networkMapCache.getNotary(notaryName)!!
val inputState = issueState(bankA, notaryParty) val inputState = issueState(bankA, notaryParty)
@ -56,6 +55,7 @@ class RaftNotaryServiceTests : NodeBasedTest(listOf("net.corda.testing.contracts
val error = ex.error as NotaryError.Conflict val error = ex.error as NotaryError.Conflict
assertEquals(error.txId, secondSpendTx.id) assertEquals(error.txId, secondSpendTx.id)
} }
}
private fun issueState(node: StartedNode<*>, notary: Party): StateAndRef<*> { private fun issueState(node: StartedNode<*>, notary: Party): StateAndRef<*> {
return node.database.transaction { return node.database.transaction {

View File

@ -13,14 +13,12 @@ import net.corda.testing.ALICE
import net.corda.testing.ALICE_KEY import net.corda.testing.ALICE_KEY
import net.corda.testing.DEV_TRUST_ROOT import net.corda.testing.DEV_TRUST_ROOT
import net.corda.testing.getTestPartyAndCertificate import net.corda.testing.getTestPartyAndCertificate
import net.corda.testing.internal.NodeBasedTest
import net.corda.testing.node.MockKeyManagementService import net.corda.testing.node.MockKeyManagementService
import net.corda.testing.node.NodeBasedTest
import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThat
import org.assertj.core.api.Assertions.contentOf import org.assertj.core.api.Assertions.contentOf
import org.junit.Before import org.junit.Before
import org.junit.Rule
import org.junit.Test import org.junit.Test
import org.junit.rules.TemporaryFolder
import rx.observers.TestSubscriber import rx.observers.TestSubscriber
import rx.schedulers.TestScheduler import rx.schedulers.TestScheduler
import java.nio.file.Path import java.nio.file.Path
@ -29,42 +27,37 @@ import kotlin.test.assertEquals
import kotlin.test.assertTrue import kotlin.test.assertTrue
class NodeInfoWatcherTest : NodeBasedTest() { class NodeInfoWatcherTest : NodeBasedTest() {
@Rule
@JvmField
var folder = TemporaryFolder()
lateinit var keyManagementService: KeyManagementService
lateinit var nodeInfoPath: Path
val scheduler = TestScheduler()
val testSubscriber = TestSubscriber<NodeInfo>()
// Object under test
lateinit var nodeInfoWatcher: NodeInfoWatcher
companion object { companion object {
val nodeInfo = NodeInfo(listOf(), listOf(getTestPartyAndCertificate(ALICE)), 0, 0) 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>()
// Object under test
private lateinit var nodeInfoWatcher: NodeInfoWatcher
@Before @Before
fun start() { fun start() {
val identityService = InMemoryIdentityService(trustRoot = DEV_TRUST_ROOT) val identityService = InMemoryIdentityService(trustRoot = DEV_TRUST_ROOT)
keyManagementService = MockKeyManagementService(identityService, ALICE_KEY) keyManagementService = MockKeyManagementService(identityService, ALICE_KEY)
nodeInfoWatcher = NodeInfoWatcher(folder.root.toPath(), scheduler = scheduler) nodeInfoWatcher = NodeInfoWatcher(tempFolder.root.toPath(), scheduler = scheduler)
nodeInfoPath = folder.root.toPath() / CordformNode.NODE_INFO_DIRECTORY nodeInfoPath = tempFolder.root.toPath() / CordformNode.NODE_INFO_DIRECTORY
} }
@Test @Test
fun `save a NodeInfo`() { fun `save a NodeInfo`() {
assertEquals(0, assertEquals(0,
folder.root.list().filter { it.startsWith(NodeInfoFilesCopier.NODE_INFO_FILE_NAME_PREFIX) }.size) tempFolder.root.list().filter { it.startsWith(NodeInfoFilesCopier.NODE_INFO_FILE_NAME_PREFIX) }.size)
NodeInfoWatcher.saveToFile(folder.root.toPath(), nodeInfo, keyManagementService) NodeInfoWatcher.saveToFile(tempFolder.root.toPath(), nodeInfo, keyManagementService)
val nodeInfoFiles = folder.root.list().filter { it.startsWith(NodeInfoFilesCopier.NODE_INFO_FILE_NAME_PREFIX) } val nodeInfoFiles = tempFolder.root.list().filter { it.startsWith(NodeInfoFilesCopier.NODE_INFO_FILE_NAME_PREFIX) }
assertEquals(1, nodeInfoFiles.size) assertEquals(1, nodeInfoFiles.size)
val fileName = nodeInfoFiles.first() val fileName = nodeInfoFiles.first()
assertTrue(fileName.startsWith(NodeInfoFilesCopier.NODE_INFO_FILE_NAME_PREFIX)) assertTrue(fileName.startsWith(NodeInfoFilesCopier.NODE_INFO_FILE_NAME_PREFIX))
val file = (folder.root.path / fileName).toFile() val file = (tempFolder.root.path / fileName).toFile()
// Just check that something is written, another tests verifies that the written value can be read back. // Just check that something is written, another tests verifies that the written value can be read back.
assertThat(contentOf(file)).isNotEmpty() assertThat(contentOf(file)).isNotEmpty()
} }

View File

@ -1,32 +1,28 @@
package net.corda.node.services.network package net.corda.node.services.network
import co.paralleluniverse.fibers.Suspendable
import net.corda.core.flows.FlowLogic
import net.corda.core.flows.FlowSession
import net.corda.core.flows.InitiatedBy
import net.corda.core.flows.InitiatingFlow
import net.corda.core.identity.CordaX500Name import net.corda.core.identity.CordaX500Name
import net.corda.core.identity.Party import net.corda.core.identity.Party
import net.corda.core.node.NodeInfo import net.corda.core.node.NodeInfo
import net.corda.core.utilities.* import net.corda.core.utilities.NetworkHostAndPort
import net.corda.node.internal.Node import net.corda.node.internal.Node
import net.corda.node.internal.StartedNode import net.corda.node.internal.StartedNode
import net.corda.testing.* import net.corda.testing.ALICE
import net.corda.testing.node.NodeBasedTest import net.corda.testing.BOB
import org.assertj.core.api.Assertions.assertThat import net.corda.testing.DUMMY_NOTARY
import net.corda.testing.chooseIdentity
import net.corda.testing.internal.NodeBasedTest
import org.junit.Before import org.junit.Before
import org.junit.Test import org.junit.Test
import kotlin.test.assertEquals import kotlin.test.assertEquals
class PersistentNetworkMapCacheTest : NodeBasedTest() { class PersistentNetworkMapCacheTest : NodeBasedTest() {
private val partiesList = listOf(DUMMY_NOTARY, ALICE, BOB) private val partiesList = listOf(DUMMY_NOTARY, ALICE, BOB)
private val addressesMap: HashMap<CordaX500Name, NetworkHostAndPort> = HashMap() private val addressesMap = HashMap<CordaX500Name, NetworkHostAndPort>()
private val infos: MutableSet<NodeInfo> = HashSet() private val infos: MutableSet<NodeInfo> = HashSet()
@Before @Before
fun start() { fun start() {
val nodes = startNodesWithPort(partiesList) val nodes = startNodesWithPort(partiesList)
nodes.forEach { it.internals.nodeReadyFuture.get() } // Need to wait for network map registration, as these tests are ran without waiting.
nodes.forEach { nodes.forEach {
infos.add(it.info) infos.add(it.info)
addressesMap[it.info.chooseIdentity().name] = it.info.addresses[0] addressesMap[it.info.chooseIdentity().name] = it.info.addresses[0]
@ -35,7 +31,7 @@ class PersistentNetworkMapCacheTest : NodeBasedTest() {
} }
@Test @Test
fun `get nodes by owning key and by name, no network map service`() { fun `get nodes by owning key and by name`() {
val alice = startNodesWithPort(listOf(ALICE))[0] val alice = startNodesWithPort(listOf(ALICE))[0]
val netCache = alice.services.networkMapCache val netCache = alice.services.networkMapCache
alice.database.transaction { alice.database.transaction {
@ -47,7 +43,7 @@ class PersistentNetworkMapCacheTest : NodeBasedTest() {
} }
@Test @Test
fun `get nodes by address no network map service`() { fun `get nodes by address`() {
val alice = startNodesWithPort(listOf(ALICE))[0] val alice = startNodesWithPort(listOf(ALICE))[0]
val netCache = alice.services.networkMapCache val netCache = alice.services.networkMapCache
alice.database.transaction { alice.database.transaction {
@ -57,92 +53,20 @@ class PersistentNetworkMapCacheTest : NodeBasedTest() {
} }
@Test @Test
fun `restart node with DB map cache and no network map`() { fun `restart node with DB map cache`() {
val alice = startNodesWithPort(listOf(ALICE))[0] val alice = startNodesWithPort(listOf(ALICE))[0]
val partyNodes = alice.services.networkMapCache.allNodes val partyNodes = alice.services.networkMapCache.allNodes
assertEquals(infos.size, partyNodes.size) assertEquals(infos.size, partyNodes.size)
assertEquals(infos.flatMap { it.legalIdentities }.toSet(), partyNodes.flatMap { it.legalIdentities }.toSet()) assertEquals(infos.flatMap { it.legalIdentities }.toSet(), partyNodes.flatMap { it.legalIdentities }.toSet())
} }
@Test
fun `start 2 nodes without pointing at NetworkMapService and communicate with each other`() {
val parties = partiesList.subList(1, partiesList.size)
val nodes = startNodesWithPort(parties)
nodes.forEach {
val partyNodes = it.services.networkMapCache.allNodes
assertEquals(infos.size, partyNodes.size)
assertEquals(infos.flatMap { it.legalIdentities }.toSet(), partyNodes.flatMap { it.legalIdentities }.toSet())
}
checkConnectivity(nodes)
}
@Test
fun `start 2 nodes pointing at NetworkMapService but don't start network map node`() {
val parties = partiesList.subList(1, partiesList.size)
val nodes = startNodesWithPort(parties)
nodes.forEach {
val partyNodes = it.services.networkMapCache.allNodes
assertEquals(infos.size, partyNodes.size)
assertEquals(infos.flatMap { it.legalIdentities }.toSet(), partyNodes.flatMap { it.legalIdentities }.toSet())
}
checkConnectivity(nodes)
}
@Test
fun `start node and network map communicate`() {
val parties = partiesList.subList(0, 2)
val nodes = startNodesWithPort(parties)
checkConnectivity(nodes)
}
@Test
fun `start node without networkMapService and no database - success`() {
startNode(CHARLIE.name).getOrThrow(2.seconds)
}
// HELPERS // HELPERS
// Helper function to restart nodes with the same host and port. // Helper function to restart nodes with the same host and port.
private fun startNodesWithPort(nodesToStart: List<Party>, customRetryIntervalMs: Long? = null): List<StartedNode<Node>> { private fun startNodesWithPort(nodesToStart: List<Party>, customRetryIntervalMs: Long? = null): List<StartedNode<Node>> {
return nodesToStart.map { party -> return nodesToStart.map { party ->
val configOverrides = (addressesMap[party.name]?.let { mapOf("p2pAddress" to it.toString()) } ?: emptyMap()) + val configOverrides = (addressesMap[party.name]?.let { mapOf("p2pAddress" to it.toString()) } ?: emptyMap()) +
(customRetryIntervalMs?.let { mapOf("activeMQServer.bridge.retryIntervalMs" to it.toString()) } ?: emptyMap()) (customRetryIntervalMs?.let { mapOf("activeMQServer.bridge.retryIntervalMs" to it.toString()) } ?: emptyMap())
startNode(party.name, startNode(party.name, configOverrides = configOverrides)
configOverrides = configOverrides,
waitForConnection = false).getOrThrow()
}
}
// Check that nodes are functional, communicate each with each.
private fun checkConnectivity(nodes: List<StartedNode<*>>) {
nodes.forEach { node1 ->
nodes.forEach { node2 ->
if (!(node1 === node2)) { // Do not check connectivity to itself
node2.internals.registerInitiatedFlow(SendBackFlow::class.java)
val resultFuture = node1.services.startFlow(SendFlow(node2.info.chooseIdentity())).resultFuture
assertThat(resultFuture.getOrThrow()).isEqualTo("Hello!")
}
}
}
}
@InitiatingFlow
private class SendFlow(val otherParty: Party) : FlowLogic<String>() {
@Suspendable
override fun call(): String {
logger.info("SEND FLOW to $otherParty")
logger.info("Party key ${otherParty.owningKey.toBase58String()}")
val session = initiateFlow(otherParty)
return session.sendAndReceive<String>("Hi!").unwrap { it }
}
}
@InitiatedBy(SendFlow::class)
private class SendBackFlow(val otherSideSession: FlowSession) : FlowLogic<Unit>() {
@Suspendable
override fun call() {
logger.info("SEND BACK FLOW to ${otherSideSession.counterparty}")
logger.info("Party key ${otherSideSession.counterparty.owningKey.toBase58String()}")
otherSideSession.send("Hello!")
} }
} }
} }

View File

@ -5,22 +5,20 @@ import net.corda.core.flows.FlowLogic
import net.corda.core.flows.FlowSession import net.corda.core.flows.FlowSession
import net.corda.core.flows.InitiatingFlow import net.corda.core.flows.InitiatingFlow
import net.corda.core.identity.Party import net.corda.core.identity.Party
import net.corda.core.internal.concurrent.transpose
import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.getOrThrow
import net.corda.core.utilities.unwrap import net.corda.core.utilities.unwrap
import net.corda.testing.ALICE import net.corda.testing.ALICE
import net.corda.testing.BOB import net.corda.testing.BOB
import net.corda.testing.chooseIdentity import net.corda.testing.chooseIdentity
import net.corda.testing.node.NodeBasedTest import net.corda.testing.internal.NodeBasedTest
import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThat
import org.junit.Test import org.junit.Test
class FlowVersioningTest : NodeBasedTest() { class FlowVersioningTest : NodeBasedTest() {
@Test @Test
fun `getFlowContext returns the platform version for core flows`() { fun `getFlowContext returns the platform version for core flows`() {
val (alice, bob) = listOf( val alice = startNode(ALICE.name, platformVersion = 2)
startNode(ALICE.name, platformVersion = 2), val bob = startNode(BOB.name, platformVersion = 3)
startNode(BOB.name, platformVersion = 3)).transpose().getOrThrow()
bob.internals.installCoreFlow(PretendInitiatingCoreFlow::class, ::PretendInitiatedCoreFlow) bob.internals.installCoreFlow(PretendInitiatingCoreFlow::class, ::PretendInitiatedCoreFlow)
val (alicePlatformVersionAccordingToBob, bobPlatformVersionAccordingToAlice) = alice.services.startFlow( val (alicePlatformVersionAccordingToBob, bobPlatformVersionAccordingToAlice) = alice.services.startFlow(
PretendInitiatingCoreFlow(bob.info.chooseIdentity())).resultFuture.getOrThrow() PretendInitiatingCoreFlow(bob.info.chooseIdentity())).resultFuture.getOrThrow()

View File

@ -28,8 +28,8 @@ import net.corda.testing.ALICE
import net.corda.testing.BOB import net.corda.testing.BOB
import net.corda.testing.chooseIdentity import net.corda.testing.chooseIdentity
import net.corda.testing.configureTestSSL import net.corda.testing.configureTestSSL
import net.corda.testing.internal.NodeBasedTest
import net.corda.testing.messaging.SimpleMQClient import net.corda.testing.messaging.SimpleMQClient
import net.corda.testing.node.NodeBasedTest
import org.apache.activemq.artemis.api.core.ActiveMQNonExistentQueueException import org.apache.activemq.artemis.api.core.ActiveMQNonExistentQueueException
import org.apache.activemq.artemis.api.core.ActiveMQSecurityException import org.apache.activemq.artemis.api.core.ActiveMQSecurityException
import org.apache.activemq.artemis.api.core.SimpleString import org.apache.activemq.artemis.api.core.SimpleString
@ -52,7 +52,7 @@ abstract class MQSecurityTest : NodeBasedTest() {
@Before @Before
fun start() { fun start() {
alice = startNode(ALICE.name, rpcUsers = extraRPCUsers + rpcUser).getOrThrow() alice = startNode(ALICE.name, rpcUsers = extraRPCUsers + rpcUser)
attacker = createAttacker() attacker = createAttacker()
startAttacker(attacker) startAttacker(attacker)
} }
@ -87,7 +87,7 @@ abstract class MQSecurityTest : NodeBasedTest() {
@Test @Test
fun `create queue for peer which has not been communicated with`() { fun `create queue for peer which has not been communicated with`() {
val bob = startNode(BOB.name).getOrThrow() val bob = startNode(BOB.name)
assertAllQueueCreationAttacksFail("$PEERS_PREFIX${bob.info.chooseIdentity().owningKey.toBase58String()}") assertAllQueueCreationAttacksFail("$PEERS_PREFIX${bob.info.chooseIdentity().owningKey.toBase58String()}")
} }
@ -219,7 +219,7 @@ abstract class MQSecurityTest : NodeBasedTest() {
} }
private fun startBobAndCommunicateWithAlice(): Party { private fun startBobAndCommunicateWithAlice(): Party {
val bob = startNode(BOB.name).getOrThrow() val bob = startNode(BOB.name)
bob.internals.registerInitiatedFlow(ReceiveFlow::class.java) bob.internals.registerInitiatedFlow(ReceiveFlow::class.java)
val bobParty = bob.info.chooseIdentity() val bobParty = bob.info.chooseIdentity()
// Perform a protocol exchange to force the peer queue to be created // Perform a protocol exchange to force the peer queue to be created

View File

@ -3,10 +3,8 @@ package net.corda.services.messaging
import net.corda.core.concurrent.CordaFuture import net.corda.core.concurrent.CordaFuture
import net.corda.core.crypto.random63BitValue import net.corda.core.crypto.random63BitValue
import net.corda.core.identity.CordaX500Name import net.corda.core.identity.CordaX500Name
import net.corda.core.internal.concurrent.transpose import net.corda.core.internal.concurrent.map
import net.corda.core.internal.elapsedTime
import net.corda.core.internal.randomOrNull import net.corda.core.internal.randomOrNull
import net.corda.core.internal.times
import net.corda.core.messaging.MessageRecipients import net.corda.core.messaging.MessageRecipients
import net.corda.core.messaging.SingleMessageRecipient import net.corda.core.messaging.SingleMessageRecipient
import net.corda.core.serialization.CordaSerializable import net.corda.core.serialization.CordaSerializable
@ -14,47 +12,40 @@ import net.corda.core.serialization.deserialize
import net.corda.core.serialization.serialize import net.corda.core.serialization.serialize
import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.getOrThrow
import net.corda.core.utilities.seconds import net.corda.core.utilities.seconds
import net.corda.node.internal.Node
import net.corda.node.internal.StartedNode import net.corda.node.internal.StartedNode
import net.corda.node.services.messaging.* import net.corda.node.services.messaging.*
import net.corda.node.services.transactions.RaftValidatingNotaryService import net.corda.node.services.transactions.RaftValidatingNotaryService
import net.corda.testing.* import net.corda.testing.ALICE
import net.corda.testing.node.NodeBasedTest import net.corda.testing.chooseIdentity
import net.corda.testing.driver.DriverDSLExposedInterface
import net.corda.testing.driver.NodeHandle
import net.corda.testing.driver.driver
import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThat
import org.junit.Ignore
import org.junit.Test import org.junit.Test
import java.util.* import java.util.*
import java.util.concurrent.CountDownLatch import java.util.concurrent.CountDownLatch
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
import java.util.concurrent.atomic.AtomicInteger import java.util.concurrent.atomic.AtomicInteger
class P2PMessagingTest : NodeBasedTest() { class P2PMessagingTest {
private companion object { private companion object {
val DISTRIBUTED_SERVICE_NAME = CordaX500Name(RaftValidatingNotaryService.id, "DistributedService", "London", "GB") val DISTRIBUTED_SERVICE_NAME = CordaX500Name(RaftValidatingNotaryService.id, "DistributedService", "London", "GB")
} }
@Test
fun `network map will work after restart`() {
val identities = listOf(DUMMY_BANK_A, DUMMY_BANK_B, DUMMY_NOTARY)
fun startNodes() = identities.map { startNode(it.name) }.transpose()
val startUpDuration = elapsedTime { startNodes().getOrThrow() }
// Start the network map a second time - this will restore message queues from the journal.
// This will hang and fail prior the fix. https://github.com/corda/corda/issues/37
clearAllNodeInfoDb() // Clear network map data from nodes databases.
stopAllNodes()
startNodes().getOrThrow(timeout = startUpDuration * 3)
}
@Test @Test
fun `communicating with a distributed service which we're part of`() { fun `communicating with a distributed service which we're part of`() {
val distributedService = startNotaryCluster(DISTRIBUTED_SERVICE_NAME, 2).getOrThrow() driver(startNodesInProcess = true) {
val distributedService = startDistributedService()
assertAllNodesAreUsed(distributedService, DISTRIBUTED_SERVICE_NAME, distributedService[0]) assertAllNodesAreUsed(distributedService, DISTRIBUTED_SERVICE_NAME, distributedService[0])
} }
}
@Test @Test
fun `distributed service requests are retried if one of the nodes in the cluster goes down without sending a response`() { fun `distributed service requests are retried if one of the nodes in the cluster goes down without sending a response`() {
val distributedServiceNodes = startNotaryCluster(DISTRIBUTED_SERVICE_NAME, 2).getOrThrow() driver(startNodesInProcess = true) {
val alice = startNode(ALICE.name, configOverrides = mapOf("messageRedeliveryDelaySeconds" to 1)).getOrThrow() val distributedServiceNodes = startDistributedService()
val alice = startAlice()
val serviceAddress = alice.services.networkMapCache.run { val serviceAddress = alice.services.networkMapCache.run {
val notaryParty = notaryIdentities.randomOrNull()!! val notaryParty = notaryIdentities.randomOrNull()!!
alice.network.getAddressOfParty(getPartyInfo(notaryParty)!!) alice.network.getAddressOfParty(getPartyInfo(notaryParty)!!)
@ -82,12 +73,13 @@ class P2PMessagingTest : NodeBasedTest() {
val response = responseFuture.getOrThrow(10.seconds) val response = responseFuture.getOrThrow(10.seconds)
assertThat(response).isEqualTo(responseMessage) assertThat(response).isEqualTo(responseMessage)
} }
}
@Test @Test
@Ignore("Fails on Team City due to issues with restaring nodes.")
fun `distributed service request retries are persisted across client node restarts`() { fun `distributed service request retries are persisted across client node restarts`() {
val distributedServiceNodes = startNotaryCluster(DISTRIBUTED_SERVICE_NAME, 2).getOrThrow() driver(startNodesInProcess = true) {
val alice = startNode(ALICE.name, configOverrides = mapOf("messageRedeliveryDelaySeconds" to 1)).getOrThrow() val distributedServiceNodes = startDistributedService()
val alice = startAlice()
val serviceAddress = alice.services.networkMapCache.run { val serviceAddress = alice.services.networkMapCache.run {
val notaryParty = notaryIdentities.randomOrNull()!! val notaryParty = notaryIdentities.randomOrNull()!!
alice.network.getAddressOfParty(getPartyInfo(notaryParty)!!) alice.network.getAddressOfParty(getPartyInfo(notaryParty)!!)
@ -110,8 +102,6 @@ class P2PMessagingTest : NodeBasedTest() {
// Wait until the first request is received // Wait until the first request is received
crashingNodes.firstRequestReceived.await(5, TimeUnit.SECONDS) crashingNodes.firstRequestReceived.await(5, TimeUnit.SECONDS)
// Stop alice's node after we ensured that the first request was delivered and ignored. // Stop alice's node after we ensured that the first request was delivered and ignored.
alice.services.networkMapCache.clearNetworkMapCache()
alice.dispose() alice.dispose()
val numberOfRequestsReceived = crashingNodes.requestsReceived.get() val numberOfRequestsReceived = crashingNodes.requestsReceived.get()
assertThat(numberOfRequestsReceived).isGreaterThanOrEqualTo(1) assertThat(numberOfRequestsReceived).isGreaterThanOrEqualTo(1)
@ -119,12 +109,26 @@ class P2PMessagingTest : NodeBasedTest() {
crashingNodes.ignoreRequests = false crashingNodes.ignoreRequests = false
// Restart the node and expect a response // Restart the node and expect a response
val aliceRestarted = startNode(ALICE.name, waitForConnection = true, configOverrides = mapOf("messageRedeliveryDelaySeconds" to 5)).getOrThrow() val aliceRestarted = startAlice()
val response = aliceRestarted.network.onNext<Any>(dummyTopic, sessionId).getOrThrow(5.seconds) val response = aliceRestarted.network.onNext<Any>(dummyTopic, sessionId).getOrThrow(5.seconds)
assertThat(crashingNodes.requestsReceived.get()).isGreaterThan(numberOfRequestsReceived) assertThat(crashingNodes.requestsReceived.get()).isGreaterThan(numberOfRequestsReceived)
assertThat(response).isEqualTo(responseMessage) assertThat(response).isEqualTo(responseMessage)
} }
}
private fun DriverDSLExposedInterface.startDistributedService(): List<StartedNode<Node>> {
return startNotaryCluster(DISTRIBUTED_SERVICE_NAME, 2)
.getOrThrow()
.second
.map { (it as NodeHandle.InProcess).node }
}
private fun DriverDSLExposedInterface.startAlice(): StartedNode<Node> {
return startNode(providedName = ALICE.name, customOverrides = mapOf("messageRedeliveryDelaySeconds" to 1))
.map { (it as NodeHandle.InProcess).node }
.getOrThrow()
}
data class CrashingNodes( data class CrashingNodes(
val firstRequestReceived: CountDownLatch, val firstRequestReceived: CountDownLatch,

View File

@ -1,19 +1,17 @@
package net.corda.traderdemo package net.corda.traderdemo
import net.corda.client.rpc.CordaRPCClient import net.corda.client.rpc.CordaRPCClient
import net.corda.core.internal.packageName
import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.getOrThrow
import net.corda.core.utilities.millis import net.corda.core.utilities.millis
import net.corda.finance.DOLLARS import net.corda.finance.DOLLARS
import net.corda.finance.flows.CashIssueFlow import net.corda.finance.flows.CashIssueFlow
import net.corda.finance.flows.CashPaymentFlow import net.corda.finance.flows.CashPaymentFlow
import net.corda.finance.schemas.CashSchemaV1 import net.corda.node.services.FlowPermissions
import net.corda.finance.schemas.CommercialPaperSchemaV1
import net.corda.node.services.FlowPermissions.Companion.startFlowPermission
import net.corda.nodeapi.User import net.corda.nodeapi.User
import net.corda.testing.* import net.corda.testing.*
import net.corda.testing.driver.NodeHandle
import net.corda.testing.driver.driver
import net.corda.testing.driver.poll import net.corda.testing.driver.poll
import net.corda.testing.node.NodeBasedTest
import net.corda.traderdemo.flow.BuyerFlow import net.corda.traderdemo.flow.BuyerFlow
import net.corda.traderdemo.flow.CommercialPaperIssueFlow import net.corda.traderdemo.flow.CommercialPaperIssueFlow
import net.corda.traderdemo.flow.SellerFlow import net.corda.traderdemo.flow.SellerFlow
@ -21,21 +19,21 @@ import org.assertj.core.api.Assertions.assertThat
import org.junit.Test import org.junit.Test
import java.util.concurrent.Executors import java.util.concurrent.Executors
class TraderDemoTest : NodeBasedTest(listOf( class TraderDemoTest {
"net.corda.finance.contracts.asset", "net.corda.finance.contracts",
CashSchemaV1::class.packageName, CommercialPaperSchemaV1::class.packageName)) {
@Test @Test
fun `runs trader demo`() { fun `runs trader demo`() {
val demoUser = User("demo", "demo", setOf(startFlowPermission<SellerFlow>())) val demoUser = User("demo", "demo", setOf(FlowPermissions.startFlowPermission<SellerFlow>()))
val bankUser = User("user1", "test", permissions = setOf( val bankUser = User("user1", "test", permissions = setOf(
startFlowPermission<CashIssueFlow>(), FlowPermissions.startFlowPermission<CashIssueFlow>(),
startFlowPermission<CashPaymentFlow>(), FlowPermissions.startFlowPermission<CashPaymentFlow>(),
startFlowPermission<CommercialPaperIssueFlow>())) FlowPermissions.startFlowPermission<CommercialPaperIssueFlow>()))
val notaryFuture = startNotaryNode(DUMMY_NOTARY.name, validating = false) driver(startNodesInProcess = true, extraCordappPackagesToScan = listOf("net.corda.finance")) {
val nodeAFuture = startNode(DUMMY_BANK_A.name, rpcUsers = listOf(demoUser)) val (nodeA, nodeB, bankNode) = listOf(
val nodeBFuture = startNode(DUMMY_BANK_B.name, rpcUsers = listOf(demoUser)) startNode(providedName = DUMMY_BANK_A.name, rpcUsers = listOf(demoUser)),
val bankNodeFuture = startNode(BOC.name, rpcUsers = listOf(bankUser)) startNode(providedName = DUMMY_BANK_B.name, rpcUsers = listOf(demoUser)),
val (nodeA, nodeB, bankNode) = listOf(nodeAFuture, nodeBFuture, bankNodeFuture, notaryFuture).map { it.getOrThrow() } startNode(providedName = BOC.name, rpcUsers = listOf(bankUser)),
startNotaryNode(DUMMY_NOTARY.name, validating = false))
.map { (it.getOrThrow() as NodeHandle.InProcess).node }
nodeA.internals.registerInitiatedFlow(BuyerFlow::class.java) nodeA.internals.registerInitiatedFlow(BuyerFlow::class.java)
val (nodeARpc, nodeBRpc) = listOf(nodeA, nodeB).map { val (nodeARpc, nodeBRpc) = listOf(nodeA, nodeB).map {
@ -71,3 +69,4 @@ class TraderDemoTest : NodeBasedTest(listOf(
assertThat(clientB.dollarCashBalance).isEqualTo(5.DOLLARS) assertThat(clientB.dollarCashBalance).isEqualTo(5.DOLLARS)
} }
} }
}

View File

@ -0,0 +1,115 @@
package net.corda.testing.internal
import net.corda.core.identity.CordaX500Name
import net.corda.core.internal.concurrent.fork
import net.corda.core.internal.concurrent.transpose
import net.corda.core.internal.createDirectories
import net.corda.core.internal.div
import net.corda.core.node.NodeInfo
import net.corda.core.utilities.getOrThrow
import net.corda.node.internal.Node
import net.corda.node.internal.StartedNode
import net.corda.node.internal.cordapp.CordappLoader
import net.corda.node.services.config.ConfigHelper
import net.corda.node.services.config.configOf
import net.corda.node.services.config.parseAsNodeConfiguration
import net.corda.node.services.config.plus
import net.corda.nodeapi.User
import net.corda.testing.SerializationEnvironmentRule
import net.corda.testing.driver.addressMustNotBeBoundFuture
import net.corda.testing.getFreeLocalPorts
import net.corda.testing.node.MockServices
import org.apache.logging.log4j.Level
import org.junit.After
import org.junit.Rule
import org.junit.rules.TemporaryFolder
import java.nio.file.Path
import java.util.concurrent.Executors
import kotlin.concurrent.thread
// TODO Some of the logic here duplicates what's in the driver
abstract class NodeBasedTest(private val cordappPackages: List<String> = emptyList()) {
companion object {
private val WHITESPACE = "\\s++".toRegex()
}
@Rule
@JvmField
val testSerialization = SerializationEnvironmentRule()
@Rule
@JvmField
val tempFolder = TemporaryFolder()
private val nodes = mutableListOf<StartedNode<Node>>()
private val nodeInfos = mutableListOf<NodeInfo>()
init {
System.setProperty("consoleLogLevel", Level.DEBUG.name().toLowerCase())
}
/**
* Stops the network map node and all the nodes started by [startNode]. This is called automatically after each test
* but can also be called manually within a test.
*/
@After
fun stopAllNodes() {
val shutdownExecutor = Executors.newScheduledThreadPool(nodes.size)
nodes.map { shutdownExecutor.fork(it::dispose) }.transpose().getOrThrow()
// Wait until ports are released
val portNotBoundChecks = nodes.flatMap {
listOf(
it.internals.configuration.p2pAddress.let { addressMustNotBeBoundFuture(shutdownExecutor, it) },
it.internals.configuration.rpcAddress?.let { addressMustNotBeBoundFuture(shutdownExecutor, it) }
)
}.filterNotNull()
nodes.clear()
portNotBoundChecks.transpose().getOrThrow()
}
@JvmOverloads
fun startNode(legalName: CordaX500Name,
platformVersion: Int = 1,
rpcUsers: List<User> = emptyList(),
configOverrides: Map<String, Any> = emptyMap()): StartedNode<Node> {
val baseDirectory = baseDirectory(legalName).createDirectories()
val localPort = getFreeLocalPorts("localhost", 2)
val p2pAddress = configOverrides["p2pAddress"] ?: localPort[0].toString()
val config = ConfigHelper.loadConfig(
baseDirectory = baseDirectory,
allowMissingConfig = true,
configOverrides = configOf(
"myLegalName" to legalName.toString(),
"p2pAddress" to p2pAddress,
"rpcAddress" to localPort[1].toString(),
"rpcUsers" to rpcUsers.map { it.toMap() }
) + configOverrides
)
val parsedConfig = config.parseAsNodeConfiguration()
val node = Node(
parsedConfig,
MockServices.MOCK_VERSION_INFO.copy(platformVersion = platformVersion),
initialiseSerialization = false,
cordappLoader = CordappLoader.createDefaultWithTestPackages(parsedConfig, cordappPackages)).start()
nodes += node
ensureAllNetworkMapCachesHaveAllNodeInfos()
thread(name = legalName.organisation) {
node.internals.run()
}
return node
}
protected fun baseDirectory(legalName: CordaX500Name): Path {
return tempFolder.root.toPath() / legalName.organisation.replace(WHITESPACE, "")
}
private fun ensureAllNetworkMapCachesHaveAllNodeInfos() {
val runningNodes = nodes.filter { it.internals.started != null }
val runningNodesInfo = runningNodes.map { it.info }
for (node in runningNodes)
for (nodeInfo in runningNodesInfo) {
node.services.networkMapCache.addNode(nodeInfo)
}
}
}

View File

@ -1,181 +0,0 @@
package net.corda.testing.node
import net.corda.core.concurrent.CordaFuture
import net.corda.core.identity.CordaX500Name
import net.corda.core.internal.concurrent.*
import net.corda.core.internal.createDirectories
import net.corda.core.internal.div
import net.corda.core.node.NodeInfo
import net.corda.core.utilities.NetworkHostAndPort
import net.corda.core.utilities.getOrThrow
import net.corda.node.internal.Node
import net.corda.node.internal.StartedNode
import net.corda.node.internal.cordapp.CordappLoader
import net.corda.node.services.config.*
import net.corda.node.utilities.ServiceIdentityGenerator
import net.corda.nodeapi.User
import net.corda.nodeapi.config.toConfig
import net.corda.testing.driver.addressMustNotBeBoundFuture
import net.corda.testing.getFreeLocalPorts
import net.corda.testing.node.MockServices.Companion.MOCK_VERSION_INFO
import net.corda.testing.SerializationEnvironmentRule
import org.apache.logging.log4j.Level
import org.junit.After
import org.junit.Rule
import org.junit.rules.TemporaryFolder
import java.util.concurrent.Executors
import kotlin.concurrent.thread
/**
* Extend this class if you need to run nodes in a test. You could use the driver DSL but it's extremely slow for testing
* purposes. Use the driver if you need to run the nodes in separate processes otherwise this class will suffice.
*/
// TODO Some of the logic here duplicates what's in the driver
abstract class NodeBasedTest(private val cordappPackages: List<String> = emptyList()) {
companion object {
private val WHITESPACE = "\\s++".toRegex()
}
@Rule
@JvmField
val testSerialization = SerializationEnvironmentRule()
@Rule
@JvmField
val tempFolder = TemporaryFolder()
private val nodes = mutableListOf<StartedNode<Node>>()
private val nodeInfos = mutableListOf<NodeInfo>()
init {
System.setProperty("consoleLogLevel", Level.DEBUG.name().toLowerCase())
}
/**
* Stops the network map node and all the nodes started by [startNode]. This is called automatically after each test
* but can also be called manually within a test.
*/
@After
fun stopAllNodes() {
val shutdownExecutor = Executors.newScheduledThreadPool(nodes.size)
nodes.map { shutdownExecutor.fork(it::dispose) }.transpose().getOrThrow()
// Wait until ports are released
val portNotBoundChecks = nodes.flatMap {
listOf(
it.internals.configuration.p2pAddress.let { addressMustNotBeBoundFuture(shutdownExecutor, it) },
it.internals.configuration.rpcAddress?.let { addressMustNotBeBoundFuture(shutdownExecutor, it) }
)
}.filterNotNull()
nodes.clear()
portNotBoundChecks.transpose().getOrThrow()
}
/**
* Clear network map data from nodes' databases.
*/
fun clearAllNodeInfoDb() {
nodes.forEach { it.services.networkMapCache.clearNetworkMapCache() }
}
@JvmOverloads
fun startNode(legalName: CordaX500Name,
platformVersion: Int = 1,
rpcUsers: List<User> = emptyList(),
configOverrides: Map<String, Any> = emptyMap(),
waitForConnection: Boolean = true): CordaFuture<StartedNode<Node>> {
val node = startNodeInternal(
legalName,
platformVersion,
rpcUsers,
configOverrides)
return if (waitForConnection) node.internals.nodeReadyFuture.map { node } else doneFuture(node)
}
// TODO This method has been added temporarily, to be deleted once the set of notaries is defined at the network level.
fun startNotaryNode(name: CordaX500Name,
rpcUsers: List<User> = emptyList(),
validating: Boolean = true): CordaFuture<StartedNode<Node>> {
return startNode(name, rpcUsers = rpcUsers, configOverrides = mapOf("notary" to mapOf("validating" to validating)))
}
fun startNotaryCluster(notaryName: CordaX500Name, clusterSize: Int): CordaFuture<List<StartedNode<Node>>> {
fun notaryConfig(nodeAddress: NetworkHostAndPort, clusterAddress: NetworkHostAndPort? = null): Map<String, Any> {
val clusterAddresses = if (clusterAddress != null) listOf(clusterAddress) else emptyList()
val config = NotaryConfig(validating = true, raft = RaftConfig(nodeAddress = nodeAddress, clusterAddresses = clusterAddresses))
return mapOf("notary" to config.toConfig().root().unwrapped())
}
ServiceIdentityGenerator.generateToDisk(
(0 until clusterSize).map { baseDirectory(notaryName.copy(organisation = "${notaryName.organisation}-$it")) },
notaryName)
val nodeAddresses = getFreeLocalPorts("localhost", clusterSize)
val masterNodeFuture = startNode(
CordaX500Name(organisation = "${notaryName.organisation}-0", locality = notaryName.locality, country = notaryName.country),
configOverrides = notaryConfig(nodeAddresses[0]) + mapOf(
"database" to mapOf(
"serverNameTablePrefix" to if (clusterSize > 1) "${notaryName.organisation}0".replace(Regex("[^0-9A-Za-z]+"), "") else ""
)
)
)
val remainingNodesFutures = (1 until clusterSize).map {
startNode(
CordaX500Name(organisation = "${notaryName.organisation}-$it", locality = notaryName.locality, country = notaryName.country),
configOverrides = notaryConfig(nodeAddresses[it], nodeAddresses[0]) + mapOf(
"database" to mapOf(
"serverNameTablePrefix" to "${notaryName.organisation}$it".replace(Regex("[^0-9A-Za-z]+"), "")
)
)
)
}
return remainingNodesFutures.transpose().flatMap { remainingNodes ->
masterNodeFuture.map { masterNode -> listOf(masterNode) + remainingNodes }
}
}
protected fun baseDirectory(legalName: CordaX500Name) = tempFolder.root.toPath() / legalName.organisation.replace(WHITESPACE, "")
private fun ensureAllNetworkMapCachesHaveAllNodeInfos() {
val runningNodes = nodes.filter { it.internals.started != null }
val runningNodesInfo = runningNodes.map { it.info }
for (node in runningNodes)
for (nodeInfo in runningNodesInfo) {
node.services.networkMapCache.addNode(nodeInfo)
}
}
private fun startNodeInternal(legalName: CordaX500Name,
platformVersion: Int,
rpcUsers: List<User>,
configOverrides: Map<String, Any>): StartedNode<Node> {
val baseDirectory = baseDirectory(legalName).createDirectories()
val localPort = getFreeLocalPorts("localhost", 2)
val p2pAddress = configOverrides["p2pAddress"] ?: localPort[0].toString()
val config = ConfigHelper.loadConfig(
baseDirectory = baseDirectory,
allowMissingConfig = true,
configOverrides = configOf(
"myLegalName" to legalName.toString(),
"p2pAddress" to p2pAddress,
"rpcAddress" to localPort[1].toString(),
"rpcUsers" to rpcUsers.map { it.toMap() }
) + configOverrides
)
val parsedConfig = config.parseAsNodeConfiguration()
val node = Node(
parsedConfig,
MOCK_VERSION_INFO.copy(platformVersion = platformVersion),
initialiseSerialization = false,
cordappLoader = CordappLoader.createDefaultWithTestPackages(parsedConfig, cordappPackages)).start()
nodes += node
ensureAllNetworkMapCachesHaveAllNodeInfos()
thread(name = legalName.organisation) {
node.internals.run()
}
return node
}
}