mirror of
https://github.com/corda/corda.git
synced 2025-05-01 16:29:50 +00:00
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:
parent
3aeacef3bf
commit
6ebba9dea1
3
.idea/compiler.xml
generated
3
.idea/compiler.xml
generated
@ -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" />
|
||||||
|
@ -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()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -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!!)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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 {
|
||||||
|
@ -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()
|
||||||
}
|
}
|
||||||
|
@ -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!")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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()
|
||||||
|
@ -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
|
||||||
|
@ -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,
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
x
Reference in New Issue
Block a user