mirror of
https://github.com/corda/corda.git
synced 2025-06-17 06:38:21 +00:00
Added a method to NodeHandle to simplify using RPC in the Driver
This commit is contained in:
@ -12,28 +12,21 @@ import net.corda.node.driver.DriverBasedTest
|
|||||||
import net.corda.node.driver.NodeHandle
|
import net.corda.node.driver.NodeHandle
|
||||||
import net.corda.node.driver.driver
|
import net.corda.node.driver.driver
|
||||||
import net.corda.node.services.User
|
import net.corda.node.services.User
|
||||||
import net.corda.node.services.config.configureTestSSL
|
|
||||||
import net.corda.node.services.messaging.ArtemisMessagingComponent.Companion.toHostAndPort
|
|
||||||
import net.corda.node.services.messaging.CordaRPCClient
|
import net.corda.node.services.messaging.CordaRPCClient
|
||||||
import net.corda.node.services.startFlowPermission
|
import net.corda.node.services.startFlowPermission
|
||||||
import net.corda.node.services.transactions.ValidatingNotaryService
|
import net.corda.node.services.transactions.ValidatingNotaryService
|
||||||
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.Before
|
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import java.util.concurrent.CountDownLatch
|
|
||||||
import kotlin.concurrent.thread
|
|
||||||
|
|
||||||
class CordaRPCClientTest : DriverBasedTest() {
|
class CordaRPCClientTest : DriverBasedTest() {
|
||||||
|
|
||||||
private val rpcUser = User("user1", "test", permissions = setOf(startFlowPermission<CashFlow>()))
|
private val rpcUser = User("user1", "test", permissions = setOf(startFlowPermission<CashFlow>()))
|
||||||
|
private lateinit var node: NodeHandle
|
||||||
private lateinit var client: CordaRPCClient
|
private lateinit var client: CordaRPCClient
|
||||||
private lateinit var driverInfo: NodeHandle
|
|
||||||
|
|
||||||
override fun setup() = driver(isDebug = true) {
|
override fun setup() = driver(isDebug = true) {
|
||||||
driverInfo = startNode(rpcUsers = listOf(rpcUser), advertisedServices = setOf(ServiceInfo(ValidatingNotaryService.type))).getOrThrow()
|
node = startNode(rpcUsers = listOf(rpcUser), advertisedServices = setOf(ServiceInfo(ValidatingNotaryService.type))).getOrThrow()
|
||||||
client = CordaRPCClient(toHostAndPort(driverInfo.nodeInfo.address), configureTestSSL())
|
client = node.rpcClientToNode()
|
||||||
runTest()
|
runTest()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -63,7 +56,9 @@ class CordaRPCClientTest : DriverBasedTest() {
|
|||||||
println("Creating proxy")
|
println("Creating proxy")
|
||||||
val proxy = client.proxy()
|
val proxy = client.proxy()
|
||||||
println("Starting flow")
|
println("Starting flow")
|
||||||
val flowHandle = proxy.startFlow(::CashFlow, CashCommand.IssueCash(20.DOLLARS, OpaqueBytes.of(0), driverInfo.nodeInfo.legalIdentity, driverInfo.nodeInfo.legalIdentity))
|
val flowHandle = proxy.startFlow(
|
||||||
|
::CashFlow,
|
||||||
|
CashCommand.IssueCash(20.DOLLARS, OpaqueBytes.of(0), node.nodeInfo.legalIdentity, node.nodeInfo.legalIdentity))
|
||||||
println("Started flow, waiting on result")
|
println("Started flow, waiting on result")
|
||||||
flowHandle.progress.subscribe {
|
flowHandle.progress.subscribe {
|
||||||
println("PROGRESS $it")
|
println("PROGRESS $it")
|
||||||
|
@ -3,13 +3,12 @@
|
|||||||
Client RPC API tutorial
|
Client RPC API tutorial
|
||||||
=======================
|
=======================
|
||||||
|
|
||||||
In this tutorial we will build a simple command line utility that
|
In this tutorial we will build a simple command line utility that connects to a node, creates some Cash transactions and
|
||||||
connects to a node, creates some Cash transactions and meanwhile dumps
|
meanwhile dumps the transaction graph to the standard output. We will then put some simple visualisation on top. For an
|
||||||
the transaction graph to the standard output. We will then put some
|
explanation on how the RPC works see :doc:`clientrpc`.
|
||||||
simple visualisation on top. For an explanation on how the RPC works
|
|
||||||
see :doc:`clientrpc`.
|
|
||||||
|
|
||||||
We start off by connecting to the node itself. For the purposes of the tutorial we will use the Driver to start up a notary and a node that issues/exits and moves Cash around for herself. To authenticate we will use the certificates of the nodes directly.
|
We start off by connecting to the node itself. For the purposes of the tutorial we will use the Driver to start up a notary
|
||||||
|
and a node that issues/exits and moves Cash around for herself.
|
||||||
|
|
||||||
Note how we configure the node to create a user that has permission to start the CashFlow.
|
Note how we configure the node to create a user that has permission to start the CashFlow.
|
||||||
|
|
||||||
@ -25,14 +24,16 @@ Now we can connect to the node itself using a valid RPC login. We login using th
|
|||||||
:start-after: START 2
|
:start-after: START 2
|
||||||
:end-before: END 2
|
:end-before: END 2
|
||||||
|
|
||||||
We start generating transactions in a different thread (``generateTransactions`` to be defined later) using ``proxy``, which exposes the full RPC interface of the node:
|
We start generating transactions in a different thread (``generateTransactions`` to be defined later) using ``proxy``,
|
||||||
|
which exposes the full RPC interface of the node:
|
||||||
|
|
||||||
.. literalinclude:: ../../node/src/main/kotlin/net/corda/node/services/messaging/CordaRPCOps.kt
|
.. literalinclude:: ../../node/src/main/kotlin/net/corda/node/services/messaging/CordaRPCOps.kt
|
||||||
:language: kotlin
|
:language: kotlin
|
||||||
:start-after: interface CordaRPCOps
|
:start-after: interface CordaRPCOps
|
||||||
:end-before: }
|
:end-before: }
|
||||||
|
|
||||||
.. warning:: This API is evolving and will continue to grow as new functionality and features added to Corda are made available to RPC clients.
|
.. warning:: This API is evolving and will continue to grow as new functionality and features added to Corda are made
|
||||||
|
available to RPC clients.
|
||||||
|
|
||||||
The one we need in order to dump the transaction graph is ``verifiedTransactions``. The type signature tells us that the
|
The one we need in order to dump the transaction graph is ``verifiedTransactions``. The type signature tells us that the
|
||||||
RPC will return a list of transactions and an Observable stream. This is a general pattern, we query some data and the
|
RPC will return a list of transactions and an Observable stream. This is a general pattern, we query some data and the
|
||||||
@ -65,9 +66,12 @@ We utilise several RPC functions here to query things like the notaries in the n
|
|||||||
|
|
||||||
Then in a loop we generate randomly either an Issue, a Pay or an Exit transaction.
|
Then in a loop we generate randomly either an Issue, a Pay or an Exit transaction.
|
||||||
|
|
||||||
The RPC we need to initiate a Cash transaction is ``startFlowDynamic`` which may start an arbitrary flow, given sufficient permissions to do so. We won't use this function directly, but rather a type-safe wrapper around it ``startFlow`` that type-checks the arguments for us.
|
The RPC we need to initiate a Cash transaction is ``startFlowDynamic`` which may start an arbitrary flow, given sufficient
|
||||||
|
permissions to do so. We won't use this function directly, but rather a type-safe wrapper around it ``startFlow`` that
|
||||||
|
type-checks the arguments for us.
|
||||||
|
|
||||||
Finally we have everything in place: we start a couple of nodes, connect to them, and start creating transactions while listening on successfully created ones, which are dumped to the console. We just need to run it!:
|
Finally we have everything in place: we start a couple of nodes, connect to them, and start creating transactions while
|
||||||
|
listening on successfully created ones, which are dumped to the console. We just need to run it!:
|
||||||
|
|
||||||
.. code-block:: text
|
.. code-block:: text
|
||||||
|
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
package net.corda.docs
|
package net.corda.docs
|
||||||
|
|
||||||
|
import com.google.common.util.concurrent.Futures
|
||||||
import net.corda.contracts.asset.Cash
|
import net.corda.contracts.asset.Cash
|
||||||
import net.corda.core.contracts.DOLLARS
|
import net.corda.core.contracts.DOLLARS
|
||||||
import net.corda.core.contracts.issuedBy
|
import net.corda.core.contracts.issuedBy
|
||||||
|
import net.corda.core.getOrThrow
|
||||||
import net.corda.core.messaging.startFlow
|
import net.corda.core.messaging.startFlow
|
||||||
import net.corda.core.node.services.ServiceInfo
|
import net.corda.core.node.services.ServiceInfo
|
||||||
import net.corda.core.node.services.Vault
|
import net.corda.core.node.services.Vault
|
||||||
@ -12,9 +14,6 @@ import net.corda.flows.CashFlow
|
|||||||
import net.corda.flows.CashFlowResult
|
import net.corda.flows.CashFlowResult
|
||||||
import net.corda.node.driver.driver
|
import net.corda.node.driver.driver
|
||||||
import net.corda.node.services.User
|
import net.corda.node.services.User
|
||||||
import net.corda.node.services.config.configureTestSSL
|
|
||||||
import net.corda.node.services.messaging.ArtemisMessagingComponent
|
|
||||||
import net.corda.node.services.messaging.CordaRPCClient
|
|
||||||
import net.corda.node.services.startFlowPermission
|
import net.corda.node.services.startFlowPermission
|
||||||
import net.corda.node.services.transactions.ValidatingNotaryService
|
import net.corda.node.services.transactions.ValidatingNotaryService
|
||||||
import net.corda.testing.expect
|
import net.corda.testing.expect
|
||||||
@ -26,31 +25,25 @@ import kotlin.concurrent.thread
|
|||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
|
|
||||||
class IntegrationTestingTutorial {
|
class IntegrationTestingTutorial {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun aliceBobCashExchangeExample() {
|
fun `alice bob cash exchange example`() {
|
||||||
// START 1
|
// START 1
|
||||||
driver {
|
driver {
|
||||||
val testUser = User("testUser", "testPassword", permissions = setOf(startFlowPermission<CashFlow>()))
|
val testUser = User("testUser", "testPassword", permissions = setOf(startFlowPermission<CashFlow>()))
|
||||||
val aliceFuture = startNode("Alice", rpcUsers = listOf(testUser))
|
val (alice, bob, notary) = Futures.allAsList(
|
||||||
val bobFuture = startNode("Bob", rpcUsers = listOf(testUser))
|
startNode("Alice", rpcUsers = listOf(testUser)),
|
||||||
val notaryFuture = startNode("Notary", advertisedServices = setOf(ServiceInfo(ValidatingNotaryService.type)))
|
startNode("Bob", rpcUsers = listOf(testUser)),
|
||||||
val alice = aliceFuture.get()
|
startNode("Notary", advertisedServices = setOf(ServiceInfo(ValidatingNotaryService.type)))
|
||||||
val bob = bobFuture.get()
|
).getOrThrow()
|
||||||
val notary = notaryFuture.get()
|
|
||||||
// END 1
|
// END 1
|
||||||
|
|
||||||
// START 2
|
// START 2
|
||||||
val aliceClient = CordaRPCClient(
|
val aliceClient = alice.rpcClientToNode()
|
||||||
host = ArtemisMessagingComponent.toHostAndPort(alice.nodeInfo.address),
|
|
||||||
config = configureTestSSL()
|
|
||||||
)
|
|
||||||
aliceClient.start("testUser", "testPassword")
|
aliceClient.start("testUser", "testPassword")
|
||||||
val aliceProxy = aliceClient.proxy()
|
val aliceProxy = aliceClient.proxy()
|
||||||
val bobClient = CordaRPCClient(
|
val bobClient = bob.rpcClientToNode()
|
||||||
host = ArtemisMessagingComponent.toHostAndPort(bob.nodeInfo.address),
|
|
||||||
config = configureTestSSL()
|
|
||||||
)
|
|
||||||
bobClient.start("testUser", "testPassword")
|
bobClient.start("testUser", "testPassword")
|
||||||
val bobProxy = bobClient.proxy()
|
val bobProxy = bobClient.proxy()
|
||||||
// END 2
|
// END 2
|
||||||
|
@ -6,7 +6,6 @@ import net.corda.core.contracts.Amount
|
|||||||
import net.corda.core.contracts.Issued
|
import net.corda.core.contracts.Issued
|
||||||
import net.corda.core.contracts.PartyAndReference
|
import net.corda.core.contracts.PartyAndReference
|
||||||
import net.corda.core.contracts.USD
|
import net.corda.core.contracts.USD
|
||||||
import net.corda.core.div
|
|
||||||
import net.corda.core.messaging.CordaRPCOps
|
import net.corda.core.messaging.CordaRPCOps
|
||||||
import net.corda.core.messaging.startFlow
|
import net.corda.core.messaging.startFlow
|
||||||
import net.corda.core.node.CordaPluginRegistry
|
import net.corda.core.node.CordaPluginRegistry
|
||||||
@ -17,9 +16,6 @@ import net.corda.flows.CashCommand
|
|||||||
import net.corda.flows.CashFlow
|
import net.corda.flows.CashFlow
|
||||||
import net.corda.node.driver.driver
|
import net.corda.node.driver.driver
|
||||||
import net.corda.node.services.User
|
import net.corda.node.services.User
|
||||||
import net.corda.node.services.config.FullNodeConfiguration
|
|
||||||
import net.corda.node.services.config.NodeSSLConfiguration
|
|
||||||
import net.corda.node.services.messaging.CordaRPCClient
|
|
||||||
import net.corda.node.services.startFlowPermission
|
import net.corda.node.services.startFlowPermission
|
||||||
import net.corda.node.services.transactions.ValidatingNotaryService
|
import net.corda.node.services.transactions.ValidatingNotaryService
|
||||||
import org.graphstream.graph.Edge
|
import org.graphstream.graph.Edge
|
||||||
@ -41,9 +37,7 @@ enum class PrintOrVisualise {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun main(args: Array<String>) {
|
fun main(args: Array<String>) {
|
||||||
if (args.size < 1) {
|
require(args.isNotEmpty()) { "Usage: <binary> [Print|Visualise]" }
|
||||||
throw IllegalArgumentException("Usage: <binary> [Print|Visualise]")
|
|
||||||
}
|
|
||||||
val printOrVisualise = PrintOrVisualise.valueOf(args[0])
|
val printOrVisualise = PrintOrVisualise.valueOf(args[0])
|
||||||
|
|
||||||
val baseDirectory = Paths.get("build/rpc-api-tutorial")
|
val baseDirectory = Paths.get("build/rpc-api-tutorial")
|
||||||
@ -52,15 +46,10 @@ fun main(args: Array<String>) {
|
|||||||
driver(driverDirectory = baseDirectory) {
|
driver(driverDirectory = baseDirectory) {
|
||||||
startNode("Notary", advertisedServices = setOf(ServiceInfo(ValidatingNotaryService.type)))
|
startNode("Notary", advertisedServices = setOf(ServiceInfo(ValidatingNotaryService.type)))
|
||||||
val node = startNode("Alice", rpcUsers = listOf(user)).get()
|
val node = startNode("Alice", rpcUsers = listOf(user)).get()
|
||||||
val sslConfig = object : NodeSSLConfiguration {
|
|
||||||
override val certificatesPath = baseDirectory / "Alice" / "certificates"
|
|
||||||
override val keyStorePassword = "cordacadevpass"
|
|
||||||
override val trustStorePassword = "trustpass"
|
|
||||||
}
|
|
||||||
// END 1
|
// END 1
|
||||||
|
|
||||||
// START 2
|
// START 2
|
||||||
val client = CordaRPCClient(FullNodeConfiguration(node.config).artemisAddress, sslConfig)
|
val client = node.rpcClientToNode()
|
||||||
client.start("user", "password")
|
client.start("user", "password")
|
||||||
val proxy = client.proxy()
|
val proxy = client.proxy()
|
||||||
|
|
||||||
|
@ -35,7 +35,7 @@ notary directly, so there's no need to pass in the test ``User``.
|
|||||||
The ``startNode`` function returns a future that completes once the
|
The ``startNode`` function returns a future that completes once the
|
||||||
node is fully started. This allows starting of the nodes to be
|
node is fully started. This allows starting of the nodes to be
|
||||||
parallel. We wait on these futures as we need the information
|
parallel. We wait on these futures as we need the information
|
||||||
returned; their respective ``NodeInfo`` s.
|
returned; their respective ``NodeHandles`` s.
|
||||||
|
|
||||||
.. literalinclude:: example-code/src/integration-test/kotlin/net/corda/docs/IntegrationTestingTutorial.kt
|
.. literalinclude:: example-code/src/integration-test/kotlin/net/corda/docs/IntegrationTestingTutorial.kt
|
||||||
:language: kotlin
|
:language: kotlin
|
||||||
@ -46,9 +46,6 @@ Next we connect to Alice and Bob respectively from the test process
|
|||||||
using the test user we created. Then we establish RPC links that allow
|
using the test user we created. Then we establish RPC links that allow
|
||||||
us to start flows and query state.
|
us to start flows and query state.
|
||||||
|
|
||||||
Note that Driver nodes start up with test server certificates, so
|
|
||||||
it's enough to pass in ``configureTestSSL()`` for the clients.
|
|
||||||
|
|
||||||
.. literalinclude:: example-code/src/integration-test/kotlin/net/corda/docs/IntegrationTestingTutorial.kt
|
.. literalinclude:: example-code/src/integration-test/kotlin/net/corda/docs/IntegrationTestingTutorial.kt
|
||||||
:language: kotlin
|
:language: kotlin
|
||||||
:start-after: START 3
|
:start-after: START 3
|
||||||
|
@ -53,7 +53,7 @@ class DriverTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun randomFreePortAllocationWorks() {
|
fun randomFreePortAllocationWorks() {
|
||||||
val nodeInfo = driver(portAllocation = PortAllocation.RandomFree()) {
|
val nodeInfo = driver(portAllocation = PortAllocation.RandomFree) {
|
||||||
val nodeInfo = startNode("NoService")
|
val nodeInfo = startNode("NoService")
|
||||||
nodeMustBeUp(nodeInfo.getOrThrow().nodeInfo)
|
nodeMustBeUp(nodeInfo.getOrThrow().nodeInfo)
|
||||||
nodeInfo.getOrThrow()
|
nodeInfo.getOrThrow()
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package net.corda.node.services
|
package net.corda.node.services
|
||||||
|
|
||||||
import net.corda.core.bufferUntilSubscribed
|
import net.corda.core.bufferUntilSubscribed
|
||||||
|
import net.corda.core.contracts.Amount
|
||||||
import net.corda.core.contracts.POUNDS
|
import net.corda.core.contracts.POUNDS
|
||||||
import net.corda.core.contracts.issuedBy
|
import net.corda.core.contracts.issuedBy
|
||||||
import net.corda.core.crypto.Party
|
import net.corda.core.crypto.Party
|
||||||
@ -15,9 +16,6 @@ import net.corda.flows.CashFlowResult
|
|||||||
import net.corda.node.driver.DriverBasedTest
|
import net.corda.node.driver.DriverBasedTest
|
||||||
import net.corda.node.driver.NodeHandle
|
import net.corda.node.driver.NodeHandle
|
||||||
import net.corda.node.driver.driver
|
import net.corda.node.driver.driver
|
||||||
import net.corda.node.services.config.configureTestSSL
|
|
||||||
import net.corda.node.services.messaging.ArtemisMessagingComponent
|
|
||||||
import net.corda.node.services.messaging.CordaRPCClient
|
|
||||||
import net.corda.node.services.transactions.RaftValidatingNotaryService
|
import net.corda.node.services.transactions.RaftValidatingNotaryService
|
||||||
import net.corda.testing.expect
|
import net.corda.testing.expect
|
||||||
import net.corda.testing.expectEvents
|
import net.corda.testing.expectEvents
|
||||||
@ -28,14 +26,14 @@ import java.util.*
|
|||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
|
|
||||||
class DistributedServiceTests : DriverBasedTest() {
|
class DistributedServiceTests : DriverBasedTest() {
|
||||||
lateinit var alice: NodeInfo
|
lateinit var alice: NodeHandle
|
||||||
lateinit var notaries: List<NodeHandle>
|
lateinit var notaries: List<NodeHandle>
|
||||||
lateinit var aliceProxy: CordaRPCOps
|
lateinit var aliceProxy: CordaRPCOps
|
||||||
lateinit var raftNotaryIdentity: Party
|
lateinit var raftNotaryIdentity: Party
|
||||||
lateinit var notaryStateMachines: Observable<Pair<NodeInfo, StateMachineUpdate>>
|
lateinit var notaryStateMachines: Observable<Pair<NodeInfo, StateMachineUpdate>>
|
||||||
|
|
||||||
override fun setup() = driver {
|
override fun setup() = driver {
|
||||||
// Start Alice and 3 raft notaries
|
// Start Alice and 3 notaries in a RAFT cluster
|
||||||
val clusterSize = 3
|
val clusterSize = 3
|
||||||
val testUser = User("test", "test", permissions = setOf(startFlowPermission<CashFlow>()))
|
val testUser = User("test", "test", permissions = setOf(startFlowPermission<CashFlow>()))
|
||||||
val aliceFuture = startNode("Alice", rpcUsers = listOf(testUser))
|
val aliceFuture = startNode("Alice", rpcUsers = listOf(testUser))
|
||||||
@ -46,7 +44,7 @@ class DistributedServiceTests : DriverBasedTest() {
|
|||||||
type = RaftValidatingNotaryService.type
|
type = RaftValidatingNotaryService.type
|
||||||
)
|
)
|
||||||
|
|
||||||
alice = aliceFuture.get().nodeInfo
|
alice = aliceFuture.get()
|
||||||
val (notaryIdentity, notaryNodes) = notariesFuture.get()
|
val (notaryIdentity, notaryNodes) = notariesFuture.get()
|
||||||
raftNotaryIdentity = notaryIdentity
|
raftNotaryIdentity = notaryIdentity
|
||||||
notaries = notaryNodes
|
notaries = notaryNodes
|
||||||
@ -55,14 +53,14 @@ class DistributedServiceTests : DriverBasedTest() {
|
|||||||
assertEquals(notaries.size, notaries.map { it.nodeInfo.legalIdentity }.toSet().size)
|
assertEquals(notaries.size, notaries.map { it.nodeInfo.legalIdentity }.toSet().size)
|
||||||
|
|
||||||
// Connect to Alice and the notaries
|
// Connect to Alice and the notaries
|
||||||
fun connectRpc(node: NodeInfo): CordaRPCOps {
|
fun connectRpc(node: NodeHandle): CordaRPCOps {
|
||||||
val client = CordaRPCClient(ArtemisMessagingComponent.toHostAndPort(node.address), configureTestSSL())
|
val client = node.rpcClientToNode()
|
||||||
client.start("test", "test")
|
client.start("test", "test")
|
||||||
return client.proxy()
|
return client.proxy()
|
||||||
}
|
}
|
||||||
aliceProxy = connectRpc(alice)
|
aliceProxy = connectRpc(alice)
|
||||||
val notaryProxies = notaries.map { connectRpc(it.nodeInfo) }
|
val rpcClientsToNotaries = notaries.map(::connectRpc)
|
||||||
notaryStateMachines = Observable.from(notaryProxies.map { proxy ->
|
notaryStateMachines = Observable.from(rpcClientsToNotaries.map { proxy ->
|
||||||
proxy.stateMachinesAndUpdates().second.map { Pair(proxy.nodeIdentity(), it) }
|
proxy.stateMachinesAndUpdates().second.map { Pair(proxy.nodeIdentity(), it) }
|
||||||
}).flatMap { it.onErrorResumeNext(Observable.empty()) }.bufferUntilSubscribed()
|
}).flatMap { it.onErrorResumeNext(Observable.empty()) }.bufferUntilSubscribed()
|
||||||
|
|
||||||
@ -74,11 +72,10 @@ class DistributedServiceTests : DriverBasedTest() {
|
|||||||
@Test
|
@Test
|
||||||
fun `requests are distributed evenly amongst the nodes`() {
|
fun `requests are distributed evenly amongst the nodes`() {
|
||||||
// Issue 100 pounds, then pay ourselves 50x2 pounds
|
// Issue 100 pounds, then pay ourselves 50x2 pounds
|
||||||
val issueHandle = aliceProxy.startFlow(::CashFlow, CashCommand.IssueCash(100.POUNDS, OpaqueBytes.of(0), alice.legalIdentity, raftNotaryIdentity))
|
issueCash(100.POUNDS)
|
||||||
require(issueHandle.returnValue.toBlocking().first() is CashFlowResult.Success)
|
|
||||||
for (i in 1..50) {
|
for (i in 1..50) {
|
||||||
val payHandle = aliceProxy.startFlow(::CashFlow, CashCommand.PayCash(2.POUNDS.issuedBy(alice.legalIdentity.ref(0)), alice.legalIdentity))
|
paySelf(2.POUNDS)
|
||||||
require(payHandle.returnValue.toBlocking().first() is CashFlowResult.Success)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// The state machines added in the notaries should map one-to-one to notarisation requests
|
// The state machines added in the notaries should map one-to-one to notarisation requests
|
||||||
@ -104,11 +101,10 @@ class DistributedServiceTests : DriverBasedTest() {
|
|||||||
@Test
|
@Test
|
||||||
fun `cluster survives if a notary is killed`() {
|
fun `cluster survives if a notary is killed`() {
|
||||||
// Issue 100 pounds, then pay ourselves 10x5 pounds
|
// Issue 100 pounds, then pay ourselves 10x5 pounds
|
||||||
val issueHandle = aliceProxy.startFlow(::CashFlow, CashCommand.IssueCash(100.POUNDS, OpaqueBytes.of(0), alice.legalIdentity, raftNotaryIdentity))
|
issueCash(100.POUNDS)
|
||||||
require(issueHandle.returnValue.toBlocking().first() is CashFlowResult.Success)
|
|
||||||
for (i in 1..10) {
|
for (i in 1..10) {
|
||||||
val payHandle = aliceProxy.startFlow(::CashFlow, CashCommand.PayCash(5.POUNDS.issuedBy(alice.legalIdentity.ref(0)), alice.legalIdentity))
|
paySelf(5.POUNDS)
|
||||||
require(payHandle.returnValue.toBlocking().first() is CashFlowResult.Success)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now kill a notary
|
// Now kill a notary
|
||||||
@ -119,8 +115,7 @@ class DistributedServiceTests : DriverBasedTest() {
|
|||||||
|
|
||||||
// Pay ourselves another 20x5 pounds
|
// Pay ourselves another 20x5 pounds
|
||||||
for (i in 1..20) {
|
for (i in 1..20) {
|
||||||
val payHandle = aliceProxy.startFlow(::CashFlow, CashCommand.PayCash(5.POUNDS.issuedBy(alice.legalIdentity.ref(0)), alice.legalIdentity))
|
paySelf(5.POUNDS)
|
||||||
require(payHandle.returnValue.toBlocking().first() is CashFlowResult.Success)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val notarisationsPerNotary = HashMap<Party, Int>()
|
val notarisationsPerNotary = HashMap<Party, Int>()
|
||||||
@ -137,4 +132,18 @@ class DistributedServiceTests : DriverBasedTest() {
|
|||||||
println("Notarisation distribution: $notarisationsPerNotary")
|
println("Notarisation distribution: $notarisationsPerNotary")
|
||||||
require(notarisationsPerNotary.size == 3)
|
require(notarisationsPerNotary.size == 3)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun issueCash(amount: Amount<Currency>) {
|
||||||
|
val issueHandle = aliceProxy.startFlow(
|
||||||
|
::CashFlow,
|
||||||
|
CashCommand.IssueCash(amount, OpaqueBytes.of(0), alice.nodeInfo.legalIdentity, raftNotaryIdentity))
|
||||||
|
require(issueHandle.returnValue.toBlocking().first() is CashFlowResult.Success)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun paySelf(amount: Amount<Currency>) {
|
||||||
|
val payHandle = aliceProxy.startFlow(
|
||||||
|
::CashFlow,
|
||||||
|
CashCommand.PayCash(amount.issuedBy(alice.nodeInfo.legalIdentity.ref(0)), alice.nodeInfo.legalIdentity))
|
||||||
|
require(payHandle.returnValue.toBlocking().first() is CashFlowResult.Success)
|
||||||
|
}
|
||||||
}
|
}
|
@ -19,6 +19,7 @@ import net.corda.core.utilities.loggerFor
|
|||||||
import net.corda.node.services.User
|
import net.corda.node.services.User
|
||||||
import net.corda.node.services.config.ConfigHelper
|
import net.corda.node.services.config.ConfigHelper
|
||||||
import net.corda.node.services.config.FullNodeConfiguration
|
import net.corda.node.services.config.FullNodeConfiguration
|
||||||
|
import net.corda.node.services.messaging.CordaRPCClient
|
||||||
import net.corda.node.services.messaging.NodeMessagingClient
|
import net.corda.node.services.messaging.NodeMessagingClient
|
||||||
import net.corda.node.services.network.NetworkMapService
|
import net.corda.node.services.network.NetworkMapService
|
||||||
import net.corda.node.services.transactions.RaftValidatingNotaryService
|
import net.corda.node.services.transactions.RaftValidatingNotaryService
|
||||||
@ -93,9 +94,11 @@ interface DriverDSLInternalInterface : DriverDSLExposedInterface {
|
|||||||
|
|
||||||
data class NodeHandle(
|
data class NodeHandle(
|
||||||
val nodeInfo: NodeInfo,
|
val nodeInfo: NodeInfo,
|
||||||
val config: Config,
|
val configuration: FullNodeConfiguration,
|
||||||
val process: Process
|
val process: Process
|
||||||
)
|
) {
|
||||||
|
fun rpcClientToNode(): CordaRPCClient = CordaRPCClient(configuration.artemisAddress, configuration)
|
||||||
|
}
|
||||||
|
|
||||||
sealed class PortAllocation {
|
sealed class PortAllocation {
|
||||||
abstract fun nextPort(): Int
|
abstract fun nextPort(): Int
|
||||||
@ -106,7 +109,7 @@ sealed class PortAllocation {
|
|||||||
override fun nextPort() = portCounter.andIncrement
|
override fun nextPort() = portCounter.andIncrement
|
||||||
}
|
}
|
||||||
|
|
||||||
class RandomFree() : PortAllocation() {
|
object RandomFree : PortAllocation() {
|
||||||
override fun nextPort(): Int {
|
override fun nextPort(): Int {
|
||||||
return ServerSocket().use {
|
return ServerSocket().use {
|
||||||
it.bind(InetSocketAddress(0))
|
it.bind(InetSocketAddress(0))
|
||||||
@ -364,16 +367,16 @@ open class DriverDSL(
|
|||||||
}
|
}
|
||||||
) + customOverrides
|
) + customOverrides
|
||||||
|
|
||||||
val config = ConfigHelper.loadConfig(
|
val configuration = FullNodeConfiguration(ConfigHelper.loadConfig(
|
||||||
baseDirectoryPath = baseDirectory,
|
baseDirectoryPath = baseDirectory,
|
||||||
allowMissingConfig = true,
|
allowMissingConfig = true,
|
||||||
configOverrides = configOverrides
|
configOverrides = configOverrides
|
||||||
)
|
))
|
||||||
|
|
||||||
val startNode = startNode(executorService, FullNodeConfiguration(config), quasarJarPath, debugPort)
|
val startNode = startNode(executorService, configuration, quasarJarPath, debugPort)
|
||||||
registerProcess(startNode)
|
registerProcess(startNode)
|
||||||
return startNode.map {
|
return startNode.map {
|
||||||
NodeHandle(queryNodeInfo(apiAddress)!!, config, it)
|
NodeHandle(queryNodeInfo(apiAddress)!!, configuration, it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -61,6 +61,8 @@ class FullNodeConfiguration(val config: Config) : NodeConfiguration {
|
|||||||
val useHTTPS: Boolean by config
|
val useHTTPS: Boolean by config
|
||||||
val artemisAddress: HostAndPort by config
|
val artemisAddress: HostAndPort by config
|
||||||
val webAddress: HostAndPort by config
|
val webAddress: HostAndPort by config
|
||||||
|
// TODO This field is slightly redundant as artemisAddress is sufficient to hold the address of the node's MQ broker.
|
||||||
|
// Instead this should be a Boolean indicating whether that broker is an internal one started by the node or an external one
|
||||||
val messagingServerAddress: HostAndPort? by config.getOrElse { null }
|
val messagingServerAddress: HostAndPort? by config.getOrElse { null }
|
||||||
val extraAdvertisedServiceIds: String by config
|
val extraAdvertisedServiceIds: String by config
|
||||||
val useTestClock: Boolean by config.getOrElse { false }
|
val useTestClock: Boolean by config.getOrElse { false }
|
||||||
|
@ -1,31 +1,30 @@
|
|||||||
package net.corda.attachmentdemo
|
package net.corda.attachmentdemo
|
||||||
|
|
||||||
|
import com.google.common.util.concurrent.Futures
|
||||||
import net.corda.core.getOrThrow
|
import net.corda.core.getOrThrow
|
||||||
import net.corda.core.node.services.ServiceInfo
|
import net.corda.core.node.services.ServiceInfo
|
||||||
import net.corda.node.driver.driver
|
import net.corda.node.driver.driver
|
||||||
import net.corda.node.services.transactions.SimpleNotaryService
|
import net.corda.node.services.transactions.SimpleNotaryService
|
||||||
import net.corda.testing.getHostAndPort
|
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import kotlin.concurrent.thread
|
import kotlin.concurrent.thread
|
||||||
|
|
||||||
class AttachmentDemoTest {
|
class AttachmentDemoTest {
|
||||||
@Test fun `runs attachment demo`() {
|
@Test fun `runs attachment demo`() {
|
||||||
driver(dsl = {
|
driver(dsl = {
|
||||||
|
val (nodeA, nodeB) = Futures.allAsList(
|
||||||
|
startNode("Bank A"),
|
||||||
|
startNode("Bank B"),
|
||||||
startNode("Notary", setOf(ServiceInfo(SimpleNotaryService.Companion.type)))
|
startNode("Notary", setOf(ServiceInfo(SimpleNotaryService.Companion.type)))
|
||||||
val nodeAFuture = startNode("Bank A")
|
).getOrThrow()
|
||||||
val nodeBApiAddr = startNode("Bank B").getOrThrow().config.getHostAndPort("webAddress")
|
|
||||||
|
|
||||||
val nodeA = nodeAFuture.getOrThrow()
|
|
||||||
val nodeAApiAddr = nodeA.config.getHostAndPort("webAddress")
|
|
||||||
|
|
||||||
var recipientReturn: Boolean? = null
|
var recipientReturn: Boolean? = null
|
||||||
var senderReturn: Boolean? = null
|
var senderReturn: Boolean? = null
|
||||||
val recipientThread = thread {
|
val recipientThread = thread {
|
||||||
recipientReturn = AttachmentDemoClientApi(nodeAApiAddr).runRecipient()
|
recipientReturn = AttachmentDemoClientApi(nodeA.configuration.webAddress).runRecipient()
|
||||||
}
|
}
|
||||||
val senderThread = thread {
|
val senderThread = thread {
|
||||||
val counterpartyKey = nodeA.nodeInfo.legalIdentity.owningKey.toBase58String()
|
val counterpartyKey = nodeA.nodeInfo.legalIdentity.owningKey.toBase58String()
|
||||||
senderReturn = AttachmentDemoClientApi(nodeBApiAddr).runSender(counterpartyKey)
|
senderReturn = AttachmentDemoClientApi(nodeB.configuration.webAddress).runSender(counterpartyKey)
|
||||||
}
|
}
|
||||||
recipientThread.join()
|
recipientThread.join()
|
||||||
senderThread.join()
|
senderThread.join()
|
||||||
|
@ -1,19 +1,23 @@
|
|||||||
package net.corda.bank
|
package net.corda.bank
|
||||||
|
|
||||||
|
import com.google.common.util.concurrent.Futures
|
||||||
import net.corda.bank.api.BankOfCordaClientApi
|
import net.corda.bank.api.BankOfCordaClientApi
|
||||||
import net.corda.bank.api.BankOfCordaWebApi.IssueRequestParams
|
import net.corda.bank.api.BankOfCordaWebApi.IssueRequestParams
|
||||||
|
import net.corda.core.getOrThrow
|
||||||
import net.corda.core.node.services.ServiceInfo
|
import net.corda.core.node.services.ServiceInfo
|
||||||
import net.corda.node.driver.driver
|
import net.corda.node.driver.driver
|
||||||
import net.corda.node.services.transactions.SimpleNotaryService
|
import net.corda.node.services.transactions.SimpleNotaryService
|
||||||
import net.corda.testing.getHostAndPort
|
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
|
|
||||||
class BankOfCordaHttpAPITest {
|
class BankOfCordaHttpAPITest {
|
||||||
@Test fun `test issuer flow via Http`() {
|
@Test
|
||||||
|
fun `issuer flow via Http`() {
|
||||||
driver(dsl = {
|
driver(dsl = {
|
||||||
val nodeBankOfCorda = startNode("BankOfCorda", setOf(ServiceInfo(SimpleNotaryService.type))).get()
|
val (nodeBankOfCorda) = Futures.allAsList(
|
||||||
val nodeBankOfCordaApiAddr = nodeBankOfCorda.config.getHostAndPort("webAddress")
|
startNode("BankOfCorda", setOf(ServiceInfo(SimpleNotaryService.type))),
|
||||||
startNode("BigCorporation").get()
|
startNode("BigCorporation")
|
||||||
|
).getOrThrow()
|
||||||
|
val nodeBankOfCordaApiAddr = nodeBankOfCorda.configuration.webAddress
|
||||||
assert(BankOfCordaClientApi(nodeBankOfCordaApiAddr).requestWebIssue(IssueRequestParams(1000, "USD", "BigCorporation", "1", "BankOfCorda")))
|
assert(BankOfCordaClientApi(nodeBankOfCordaApiAddr).requestWebIssue(IssueRequestParams(1000, "USD", "BigCorporation", "1", "BankOfCorda")))
|
||||||
}, isDebug = true)
|
}, isDebug = true)
|
||||||
}
|
}
|
||||||
|
@ -1,39 +1,41 @@
|
|||||||
package net.corda.bank
|
package net.corda.bank
|
||||||
|
|
||||||
|
import com.google.common.util.concurrent.Futures
|
||||||
import net.corda.core.contracts.DOLLARS
|
import net.corda.core.contracts.DOLLARS
|
||||||
|
import net.corda.core.getOrThrow
|
||||||
import net.corda.core.messaging.startFlow
|
import net.corda.core.messaging.startFlow
|
||||||
import net.corda.core.node.services.ServiceInfo
|
import net.corda.core.node.services.ServiceInfo
|
||||||
import net.corda.core.transactions.SignedTransaction
|
import net.corda.core.transactions.SignedTransaction
|
||||||
import net.corda.flows.IssuerFlow.IssuanceRequester
|
import net.corda.flows.IssuerFlow.IssuanceRequester
|
||||||
import net.corda.node.driver.driver
|
import net.corda.node.driver.driver
|
||||||
import net.corda.node.services.User
|
import net.corda.node.services.User
|
||||||
import net.corda.node.services.config.configureTestSSL
|
|
||||||
import net.corda.node.services.messaging.CordaRPCClient
|
|
||||||
import net.corda.node.services.startFlowPermission
|
import net.corda.node.services.startFlowPermission
|
||||||
import net.corda.node.services.transactions.SimpleNotaryService
|
import net.corda.node.services.transactions.SimpleNotaryService
|
||||||
import net.corda.testing.*
|
import net.corda.testing.BOC_PARTY_REF
|
||||||
|
import net.corda.testing.expect
|
||||||
|
import net.corda.testing.expectEvents
|
||||||
|
import net.corda.testing.sequence
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import kotlin.test.assertTrue
|
import kotlin.test.assertTrue
|
||||||
|
|
||||||
class BankOfCordaRPCClientTest {
|
class BankOfCordaRPCClientTest {
|
||||||
|
@Test
|
||||||
@Test fun `test issuer flow via RPC`() {
|
fun `issuer flow via RPC`() {
|
||||||
driver(dsl = {
|
driver(dsl = {
|
||||||
val user = User("user1", "test", permissions = setOf(startFlowPermission<IssuanceRequester>()))
|
val user = User("user1", "test", permissions = setOf(startFlowPermission<IssuanceRequester>()))
|
||||||
val nodeBankOfCorda = startNode("BankOfCorda", setOf(ServiceInfo(SimpleNotaryService.type)), arrayListOf(user)).get()
|
val (nodeBankOfCorda, nodeBigCorporation) = Futures.allAsList(
|
||||||
val nodeBankOfCordaApiAddr = nodeBankOfCorda.config.getHostAndPort("artemisAddress")
|
startNode("BankOfCorda", setOf(ServiceInfo(SimpleNotaryService.type)), listOf(user)),
|
||||||
val bankOfCordaParty = nodeBankOfCorda.nodeInfo.legalIdentity
|
startNode("BigCorporation", rpcUsers = listOf(user))
|
||||||
val nodeBigCorporation = startNode("BigCorporation", rpcUsers = arrayListOf(user)).get()
|
).getOrThrow()
|
||||||
val bigCorporationParty = nodeBigCorporation.nodeInfo.legalIdentity
|
|
||||||
|
|
||||||
// Bank of Corda RPC Client
|
// Bank of Corda RPC Client
|
||||||
val bocClient = CordaRPCClient(nodeBankOfCordaApiAddr, configureTestSSL())
|
val bocClient = nodeBankOfCorda.rpcClientToNode()
|
||||||
bocClient.start("user1","test")
|
bocClient.start("user1", "test")
|
||||||
val bocProxy = bocClient.proxy()
|
val bocProxy = bocClient.proxy()
|
||||||
|
|
||||||
// Big Corporation RPC Client
|
// Big Corporation RPC Client
|
||||||
val bigCorpClient = CordaRPCClient(nodeBankOfCordaApiAddr, configureTestSSL())
|
val bigCorpClient = nodeBankOfCorda.rpcClientToNode() // TODO This test is broken as this should be nodeBigCorporation
|
||||||
bigCorpClient.start("user1","test")
|
bigCorpClient.start("user1", "test")
|
||||||
val bigCorpProxy = bigCorpClient.proxy()
|
val bigCorpProxy = bigCorpClient.proxy()
|
||||||
|
|
||||||
// Register for Bank of Corda Vault updates
|
// Register for Bank of Corda Vault updates
|
||||||
@ -43,7 +45,12 @@ class BankOfCordaRPCClientTest {
|
|||||||
val vaultUpdatesBigCorp = bigCorpProxy.vaultAndUpdates().second
|
val vaultUpdatesBigCorp = bigCorpProxy.vaultAndUpdates().second
|
||||||
|
|
||||||
// Kick-off actual Issuer Flow
|
// Kick-off actual Issuer Flow
|
||||||
val result = bocProxy.startFlow(::IssuanceRequester, 1000.DOLLARS, bigCorporationParty, BOC_PARTY_REF, bankOfCordaParty).returnValue.toBlocking().first()
|
val result = bocProxy.startFlow(
|
||||||
|
::IssuanceRequester,
|
||||||
|
1000.DOLLARS,
|
||||||
|
nodeBigCorporation.nodeInfo.legalIdentity,
|
||||||
|
BOC_PARTY_REF,
|
||||||
|
nodeBankOfCorda.nodeInfo.legalIdentity).returnValue.toBlocking().first()
|
||||||
assertTrue { result is SignedTransaction }
|
assertTrue { result is SignedTransaction }
|
||||||
|
|
||||||
// Check Bank of Corda Vault Updates
|
// Check Bank of Corda Vault Updates
|
||||||
@ -51,13 +58,13 @@ class BankOfCordaRPCClientTest {
|
|||||||
sequence(
|
sequence(
|
||||||
// ISSUE
|
// ISSUE
|
||||||
expect { update ->
|
expect { update ->
|
||||||
require(update.consumed.size == 0) { update.consumed.size }
|
require(update.consumed.isEmpty()) { update.consumed.size }
|
||||||
require(update.produced.size == 1) { update.produced.size }
|
require(update.produced.size == 1) { update.produced.size }
|
||||||
},
|
},
|
||||||
// MOVE
|
// MOVE
|
||||||
expect { update ->
|
expect { update ->
|
||||||
require(update.consumed.size == 1) { update.consumed.size }
|
require(update.consumed.size == 1) { update.consumed.size }
|
||||||
require(update.produced.size == 0) { update.produced.size }
|
require(update.produced.isEmpty()) { update.produced.size }
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -67,13 +74,13 @@ class BankOfCordaRPCClientTest {
|
|||||||
sequence(
|
sequence(
|
||||||
// ISSUE
|
// ISSUE
|
||||||
expect { update ->
|
expect { update ->
|
||||||
require(update.consumed.size == 0) { update.consumed.size }
|
require(update.consumed.isEmpty()) { update.consumed.size }
|
||||||
require(update.produced.size == 1) { update.produced.size }
|
require(update.produced.size == 1) { update.produced.size }
|
||||||
},
|
},
|
||||||
// MOVE
|
// MOVE
|
||||||
expect { update ->
|
expect { update ->
|
||||||
require(update.consumed.size == 1) { update.consumed.size }
|
require(update.consumed.size == 1) { update.consumed.size }
|
||||||
require(update.produced.size == 0) { update.produced.size }
|
require(update.produced.isEmpty()) { update.produced.size }
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package net.corda.irs
|
package net.corda.irs
|
||||||
|
|
||||||
import com.google.common.net.HostAndPort
|
import com.google.common.net.HostAndPort
|
||||||
import com.typesafe.config.Config
|
import com.google.common.util.concurrent.Futures
|
||||||
import net.corda.core.getOrThrow
|
import net.corda.core.getOrThrow
|
||||||
import net.corda.core.node.services.ServiceInfo
|
import net.corda.core.node.services.ServiceInfo
|
||||||
import net.corda.irs.api.NodeInterestRates
|
import net.corda.irs.api.NodeInterestRates
|
||||||
@ -15,17 +15,18 @@ import org.apache.commons.io.IOUtils
|
|||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import java.net.URL
|
import java.net.URL
|
||||||
|
|
||||||
class IRSDemoTest: IntegrationTestCategory {
|
class IRSDemoTest : IntegrationTestCategory {
|
||||||
fun Config.getHostAndPort(name: String): HostAndPort = HostAndPort.fromString(getString(name))!!
|
@Test
|
||||||
|
fun `runs IRS demo`() {
|
||||||
@Test fun `runs IRS demo`() {
|
|
||||||
driver(dsl = {
|
driver(dsl = {
|
||||||
val controller = startNode("Notary", setOf(ServiceInfo(SimpleNotaryService.type), ServiceInfo(NodeInterestRates.type))).getOrThrow()
|
val (controller, nodeA, nodeB) = Futures.allAsList(
|
||||||
val nodeA = startNode("Bank A").getOrThrow()
|
startNode("Notary", setOf(ServiceInfo(SimpleNotaryService.type), ServiceInfo(NodeInterestRates.type))),
|
||||||
val nodeB = startNode("Bank B").getOrThrow()
|
startNode("Bank A"),
|
||||||
runUploadRates(controller.config.getHostAndPort("webAddress"))
|
startNode("Bank B")
|
||||||
runTrade(nodeA.config.getHostAndPort("webAddress"))
|
).getOrThrow()
|
||||||
runDateChange(nodeB.config.getHostAndPort("webAddress"))
|
runUploadRates(controller.configuration.webAddress)
|
||||||
|
runTrade(nodeA.configuration.webAddress)
|
||||||
|
runDateChange(nodeB.configuration.webAddress)
|
||||||
}, useTestClock = true, isDebug = true)
|
}, useTestClock = true, isDebug = true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,6 @@ import net.corda.node.driver.NodeHandle
|
|||||||
import net.corda.node.driver.driver
|
import net.corda.node.driver.driver
|
||||||
import net.corda.node.services.transactions.SimpleNotaryService
|
import net.corda.node.services.transactions.SimpleNotaryService
|
||||||
import net.corda.testing.IntegrationTestCategory
|
import net.corda.testing.IntegrationTestCategory
|
||||||
import net.corda.testing.getHostAndPort
|
|
||||||
import net.corda.testing.http.HttpApi
|
import net.corda.testing.http.HttpApi
|
||||||
import net.corda.vega.api.PortfolioApi
|
import net.corda.vega.api.PortfolioApi
|
||||||
import net.corda.vega.api.PortfolioApiUtils
|
import net.corda.vega.api.PortfolioApiUtils
|
||||||
@ -45,7 +44,7 @@ class SimmValuationTest: IntegrationTestCategory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun getSimmNodeApi(futureNode: Future<NodeHandle>): HttpApi {
|
private fun getSimmNodeApi(futureNode: Future<NodeHandle>): HttpApi {
|
||||||
val nodeAddr = futureNode.getOrThrow().config.getHostAndPort("webAddress")
|
val nodeAddr = futureNode.getOrThrow().configuration.webAddress
|
||||||
return HttpApi.fromHostAndPort(nodeAddr, "api/simmvaluationdemo")
|
return HttpApi.fromHostAndPort(nodeAddr, "api/simmvaluationdemo")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package net.corda.traderdemo
|
package net.corda.traderdemo
|
||||||
|
|
||||||
|
import com.google.common.util.concurrent.Futures
|
||||||
import net.corda.core.getOrThrow
|
import net.corda.core.getOrThrow
|
||||||
import net.corda.core.node.services.ServiceInfo
|
import net.corda.core.node.services.ServiceInfo
|
||||||
import net.corda.flows.IssuerFlow
|
import net.corda.flows.IssuerFlow
|
||||||
@ -7,21 +8,21 @@ import net.corda.node.driver.driver
|
|||||||
import net.corda.node.services.User
|
import net.corda.node.services.User
|
||||||
import net.corda.node.services.startFlowPermission
|
import net.corda.node.services.startFlowPermission
|
||||||
import net.corda.node.services.transactions.SimpleNotaryService
|
import net.corda.node.services.transactions.SimpleNotaryService
|
||||||
import net.corda.testing.getHostAndPort
|
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
|
|
||||||
class TraderDemoTest {
|
class TraderDemoTest {
|
||||||
@Test fun `runs trader demo`() {
|
@Test fun `runs trader demo`() {
|
||||||
driver(dsl = {
|
driver(dsl = {
|
||||||
startNode("Notary", setOf(ServiceInfo(SimpleNotaryService.type)))
|
|
||||||
val nodeA = startNode("Bank A").getOrThrow()
|
|
||||||
val nodeAApiAddr = nodeA.config.getHostAndPort("webAddress")
|
|
||||||
val nodeBApiAddr = startNode("Bank B").getOrThrow().config.getHostAndPort("webAddress")
|
|
||||||
val user = User("user1", "test", permissions = setOf(startFlowPermission<IssuerFlow.IssuanceRequester>()))
|
val user = User("user1", "test", permissions = setOf(startFlowPermission<IssuerFlow.IssuanceRequester>()))
|
||||||
startNode("BankOfCorda", rpcUsers = listOf(user)).getOrThrow()
|
val (nodeA, nodeB) = Futures.allAsList(
|
||||||
|
startNode("Bank A"),
|
||||||
|
startNode("Bank B"),
|
||||||
|
startNode("BankOfCorda", rpcUsers = listOf(user)),
|
||||||
|
startNode("Notary", setOf(ServiceInfo(SimpleNotaryService.type)))
|
||||||
|
).getOrThrow()
|
||||||
|
|
||||||
assert(TraderDemoClientApi(nodeAApiAddr).runBuyer())
|
assert(TraderDemoClientApi(nodeA.configuration.webAddress).runBuyer())
|
||||||
assert(TraderDemoClientApi(nodeBApiAddr).runSeller(counterparty = nodeA.nodeInfo.legalIdentity.name))
|
assert(TraderDemoClientApi(nodeB.configuration.webAddress).runSeller(counterparty = nodeA.nodeInfo.legalIdentity.name))
|
||||||
}, isDebug = true)
|
}, isDebug = true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,6 @@ package net.corda.testing
|
|||||||
import com.google.common.net.HostAndPort
|
import com.google.common.net.HostAndPort
|
||||||
import com.google.common.util.concurrent.ListenableFuture
|
import com.google.common.util.concurrent.ListenableFuture
|
||||||
import com.google.common.util.concurrent.SettableFuture
|
import com.google.common.util.concurrent.SettableFuture
|
||||||
import com.typesafe.config.Config
|
|
||||||
import net.corda.core.contracts.StateRef
|
import net.corda.core.contracts.StateRef
|
||||||
import net.corda.core.crypto.*
|
import net.corda.core.crypto.*
|
||||||
import net.corda.core.flows.FlowLogic
|
import net.corda.core.flows.FlowLogic
|
||||||
@ -165,8 +164,6 @@ inline fun <reified P : FlowLogic<*>> AbstractNode.initiateSingleShotFlow(
|
|||||||
return future
|
return future
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Config.getHostAndPort(name: String) = HostAndPort.fromString(getString(name))
|
|
||||||
|
|
||||||
inline fun elapsedTime(block: () -> Unit): Duration {
|
inline fun elapsedTime(block: () -> Unit): Duration {
|
||||||
val start = System.nanoTime()
|
val start = System.nanoTime()
|
||||||
block()
|
block()
|
||||||
|
@ -26,9 +26,7 @@ import net.corda.flows.IssuerFlow.IssuanceRequester
|
|||||||
import net.corda.node.driver.PortAllocation
|
import net.corda.node.driver.PortAllocation
|
||||||
import net.corda.node.driver.driver
|
import net.corda.node.driver.driver
|
||||||
import net.corda.node.services.User
|
import net.corda.node.services.User
|
||||||
import net.corda.node.services.config.FullNodeConfiguration
|
|
||||||
import net.corda.node.services.messaging.ArtemisMessagingComponent
|
import net.corda.node.services.messaging.ArtemisMessagingComponent
|
||||||
import net.corda.node.services.messaging.CordaRPCClient
|
|
||||||
import net.corda.node.services.startFlowPermission
|
import net.corda.node.services.startFlowPermission
|
||||||
import net.corda.node.services.transactions.SimpleNotaryService
|
import net.corda.node.services.transactions.SimpleNotaryService
|
||||||
import org.apache.commons.lang.SystemUtils
|
import org.apache.commons.lang.SystemUtils
|
||||||
@ -143,19 +141,19 @@ fun main(args: Array<String>) {
|
|||||||
println("Running simulation mode ...")
|
println("Running simulation mode ...")
|
||||||
|
|
||||||
// Register with alice to use alice's RPC proxy to create random events.
|
// Register with alice to use alice's RPC proxy to create random events.
|
||||||
val aliceClient = CordaRPCClient(ArtemisMessagingComponent.toHostAndPort(aliceNode.nodeInfo.address), FullNodeConfiguration(aliceNode.config))
|
val aliceClient = aliceNode.rpcClientToNode()
|
||||||
aliceClient.start(user.username, user.password)
|
aliceClient.start(user.username, user.password)
|
||||||
val aliceRPC = aliceClient.proxy()
|
val aliceRPC = aliceClient.proxy()
|
||||||
|
|
||||||
val bobClient = CordaRPCClient(ArtemisMessagingComponent.toHostAndPort(bobNode.nodeInfo.address), FullNodeConfiguration(bobNode.config))
|
val bobClient = bobNode.rpcClientToNode()
|
||||||
bobClient.start(user.username, user.password)
|
bobClient.start(user.username, user.password)
|
||||||
val bobRPC = bobClient.proxy()
|
val bobRPC = bobClient.proxy()
|
||||||
|
|
||||||
val issuerClientGBP = CordaRPCClient(ArtemisMessagingComponent.toHostAndPort(issuerNodeGBP.nodeInfo.address), FullNodeConfiguration(issuerNodeGBP.config))
|
val issuerClientGBP = issuerNodeGBP.rpcClientToNode()
|
||||||
issuerClientGBP.start(manager.username, manager.password)
|
issuerClientGBP.start(manager.username, manager.password)
|
||||||
val issuerRPCGBP = issuerClientGBP.proxy()
|
val issuerRPCGBP = issuerClientGBP.proxy()
|
||||||
|
|
||||||
val issuerClientUSD = CordaRPCClient(ArtemisMessagingComponent.toHostAndPort(issuerNodeGBP.nodeInfo.address), FullNodeConfiguration(issuerNodeUSD.config))
|
val issuerClientUSD = issuerNodeGBP.rpcClientToNode() // TODO This should be issuerNodeUSD
|
||||||
issuerClientUSD.start(manager.username, manager.password)
|
issuerClientUSD.start(manager.username, manager.password)
|
||||||
val issuerRPCUSD = issuerClientUSD.proxy()
|
val issuerRPCUSD = issuerClientUSD.proxy()
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user