CORDA-2671: Remove broken ExplorerSimulator from Node Explorer. (#4811)

* CORDA-2671: Remove broken ExplorerSimulator from Node Explorer.

* CORDA-2671: Document that Node Explorer is included with DemoBench.
This commit is contained in:
Chris Rankin 2019-02-25 18:11:06 +00:00 committed by josecoll
parent 7b93cc8477
commit 111acc5d6e
6 changed files with 29 additions and 327 deletions

View File

@ -26,68 +26,29 @@ Running the UI
Running demo nodes
------------------
A demonstration Corda network topology is configured with 5 nodes playing the following roles:
Node Explorer is included with the :doc:`demobench` application, which allows
you to create local Corda networks on your desktop. For example:
1. Notary
2. Issuer nodes, representing two fictional central banks (UK Bank Plc issuer of GBP and USA Bank Corp issuer of USD)
3. Participant nodes, representing two users (Alice and Bob)
* Notary
* Bank of Breakfast Tea (*Issuer node* for GBP)
* Bank of Big Apples (*Issuer node* for USD)
* Alice (Participant node, for user Alice)
* Bob (Participant node, for user Bob)
DemoBench will deploy all nodes with Corda's Finance CorDapp automatically, and
allow you to launch an instance of Node Explorer for each. You will be logged
into the Node Explorer automatically.
When connected to an *Issuer* node, a user can execute cash transaction commands to issue and move cash to itself or other
parties on the network or to exit cash (for itself only).
When connected to a *Participant* node a user can only execute cash transaction commands to move cash to other parties on the network.
The Demo Nodes can be started in one of two modes:
The Node Explorer is also available as a stand-alone JavaFX application. It is
available from the Corda repositories as ``corda-tools-explorer``, and can be
run as
1. Normal
Fresh clean environment empty of transactions.
Firstly, launch an Explorer instance to login to one of the Issuer nodes and issue some cash to the other participants (Bob and Alice).
Then launch another Explorer instance to login to a participant node and start making payments (eg. move cash).
You will only be able to exit (eg. redeem from the ledger) cash as an issuer node.
**Windows**::
gradlew.bat tools:explorer:runDemoNodes
**Other**::
./gradlew tools:explorer:runDemoNodes
2. Simulation
In this mode Nodes will automatically commence executing commands as part of a random generation process.
The simulation start with pre-allocating chunks of cash to each of the party in 2 currencies (USD, GBP), then it enter a loop to generate random events.
In each iteration, the issuers will execute a Cash Issue or Cash Exit command (at a 9:1 ratio) and a random party will execute a move of cash to another random party.
**Windows**::
gradlew.bat tools:explorer:runSimulationNodes
**Other**::
./gradlew tools:explorer:runSimulationNodes
.. note:: 5 Corda nodes will be created on the following port on localhost by default.
* Notary -> 20005 (Does not accept logins)
* UK Bank Plc -> 20011 (*Issuer node*)
* USA Bank Corp -> 20008 (*Issuer node*)
* Alice -> 20017
* Bob -> 20014
Explorer login credentials to the Issuer nodes are defaulted to ``manager`` and ``test``.
Explorer login credentials to the Participants nodes are defaulted to ``user1`` and ``test``.
Please note you are not allowed to login to the notary.
.. note:: When you start the nodes in Windows using the command prompt, they might not be killed when you close the
window or terminate the task. If that happens you need to manually terminate the Java processes running the nodes.
.. note:: Alternatively, you may start the demo nodes from within IntelliJ using either of the run configurations
``Explorer - demo nodes`` or ``Explorer - demo nodes (simulation)``
.. note:: It is also possible to start the Explorer GUI from IntelliJ via ``Explorer - GUI`` run configuration, provided that the optional TornadoFX plugin has been installed first.
java -jar corda-tools-explorer.jar
.. note:: Use the Explorer in conjunction with the Trader Demo and Bank of Corda samples to use other *Issuer* nodes.

View File

@ -13,36 +13,19 @@ The user can execute cash transaction commands to issue and move cash to other p
./gradlew tools:explorer:run
## Running Demo Nodes
A demonstration Corda network topology is configured with 5 nodes playing the following roles:
1. Notary
2. Issuer nodes (representing two fictional central banks - UK Bank Plc issuer of GBP and USA Bank Corp issuer of USD)
3. Participant nodes (representing two users - Alice and Bob)
Node Explorer is included with the [DemoBench](https://docs.corda.net/demobench.html) application,
which allows you to create local Corda networks on your desktop. For example:
The Issuer nodes have the ability to issue, move and exit cash amounts.
The Participant nodes are only able to spend cash (eg. move cash).
* Notary
* Bank of Breakfast Tea (*Issuer node* for GBP)
* Bank of Big Apples (*Issuer node* for USD)
* Alice
* Bob
**Windows:**
gradlew.bat tools:explorer:runDemoNodes
**Other:**
./gradlew tools:explorer:runDemoNodes
**These Corda nodes will be created on the following port on localhost.**
* Notary -> 20005 (Does not accept logins)
* UK Bank Plc -> 20011 (*Issuer node*)
* USA Bank Corp -> 20008 (*Issuer node*)
* Alice -> 20017
* Bob -> 20014
Explorer login credentials to the Issuer nodes are defaulted to ``manager`` and ``test``.
Explorer login credentials to the Participants nodes are defaulted to ``user1`` and ``test``.
Please note you are not allowed to login to the notary.
DemoBench will deploy all nodes with Corda's Finance CorDapp automatically, and allow you to launch an
instance of Node Explorer for each.
## TODOs:
- Shows more useful information in the dashboard.

View File

@ -22,12 +22,13 @@ dependencies {
// Corda Core: Data structures and basic types needed to work with Corda.
compile project(':core')
compile project(':client:jfx')
compile project(':client:mock')
compile project(':node-driver')
compile project(':finance:contracts')
compile project(':finance:workflows')
compile project(':tools:worldmap')
// Log4J: logging framework (with SLF4J bindings)
compile "org.apache.logging.log4j:log4j-slf4j-impl:$log4j_version"
// Capsule is a library for building independently executable fat JARs.
// We only need this dependency to compile our Caplet against.
compileOnly "co.paralleluniverse:capsule:$capsule_version"
@ -60,21 +61,10 @@ tasks.withType(JavaCompile) {
options.compilerArgs << '-proc:none'
}
task runDemoNodes(dependsOn: 'classes', type: JavaExec) {
main = 'net.corda.explorer.MainKt'
classpath = sourceSets.main.runtimeClasspath
}
task runSimulationNodes(dependsOn: 'classes', type: JavaExec) {
main = 'net.corda.explorer.MainKt'
classpath = sourceSets.main.runtimeClasspath
args '-S'
}
jar {
manifest {
attributes(
'Automatic-Module-Name': 'net.corda.tools.explorer'
'Automatic-Module-Name': 'net.corda.tools.explorer'
)
}
}

View File

@ -1,212 +0,0 @@
package net.corda.explorer
import joptsimple.OptionSet
import net.corda.client.mock.ErrorFlowsEventGenerator
import net.corda.client.mock.EventGenerator
import net.corda.client.mock.Generator
import net.corda.client.rpc.CordaRPCClient
import net.corda.client.rpc.CordaRPCConnection
import net.corda.core.contracts.Amount
import net.corda.core.identity.CordaX500Name
import net.corda.core.identity.Party
import net.corda.core.internal.concurrent.thenMatch
import net.corda.core.messaging.CordaRPCOps
import net.corda.core.messaging.FlowHandle
import net.corda.core.messaging.startFlow
import net.corda.core.utilities.OpaqueBytes
import net.corda.core.utilities.getOrThrow
import net.corda.finance.GBP
import net.corda.finance.USD
import net.corda.finance.contracts.asset.Cash
import net.corda.finance.flows.AbstractCashFlow
import net.corda.finance.flows.CashExitFlow
import net.corda.finance.flows.CashExitFlow.ExitRequest
import net.corda.finance.flows.CashIssueAndPaymentFlow
import net.corda.finance.flows.CashIssueAndPaymentFlow.IssueAndPaymentRequest
import net.corda.finance.flows.CashPaymentFlow
import net.corda.finance.internal.CashConfigDataFlow
import net.corda.node.services.Permissions.Companion.startFlow
import net.corda.testing.core.ALICE_NAME
import net.corda.testing.core.BOB_NAME
import net.corda.testing.driver.*
import net.corda.testing.driver.internal.incrementalPortAllocation
import net.corda.testing.node.User
import net.corda.testing.node.internal.FINANCE_CORDAPPS
import net.corda.testing.node.internal.FINANCE_WORKFLOWS_CORDAPP
import java.time.Instant
import java.util.*
class ExplorerSimulation(private val options: OptionSet) {
private val user = User("user1", "test", permissions = setOf(
startFlow<CashPaymentFlow>(),
startFlow<CashConfigDataFlow>()
))
private val manager = User("manager", "test", permissions = setOf(
startFlow<CashIssueAndPaymentFlow>(),
startFlow<CashPaymentFlow>(),
startFlow<CashExitFlow>(),
startFlow<CashConfigDataFlow>())
)
private lateinit var notaryNode: NodeHandle
private lateinit var aliceNode: NodeHandle
private lateinit var bobNode: NodeHandle
private lateinit var issuerNodeGBP: NodeHandle
private lateinit var issuerNodeUSD: NodeHandle
private lateinit var notary: Party
private val RPCConnections = ArrayList<CordaRPCConnection>()
private val issuers = HashMap<Currency, CordaRPCOps>()
private val parties = ArrayList<Pair<Party, CordaRPCOps>>()
init {
startDemoNodes()
}
private fun onEnd() {
println("Closing RPC connections")
RPCConnections.forEach { it.close() }
}
private fun startDemoNodes() {
val portAllocation = incrementalPortAllocation(20000)
driver(DriverParameters(
portAllocation = portAllocation,
cordappsForAllNodes = FINANCE_CORDAPPS,
waitForAllNodesToFinish = true,
jmxPolicy = JmxPolicy.defaultEnabled()
)) {
// TODO : Supported flow should be exposed somehow from the node instead of set of ServiceInfo.
val alice = startNode(providedName = ALICE_NAME, rpcUsers = listOf(user))
val bob = startNode(providedName = BOB_NAME, rpcUsers = listOf(user))
val ukBankName = CordaX500Name(organisation = "UK Bank Plc", locality = "London", country = "GB")
val usaBankName = CordaX500Name(organisation = "USA Bank Corp", locality = "New York", country = "US")
val issuerGBP = startNode(NodeParameters(
providedName = ukBankName,
rpcUsers = listOf(manager),
additionalCordapps = listOf(FINANCE_WORKFLOWS_CORDAPP.copy(config = mapOf("issuableCurrencies" to listOf("GBP"))))
))
val issuerUSD = startNode(NodeParameters(
providedName = usaBankName,
rpcUsers = listOf(manager),
additionalCordapps = listOf(FINANCE_WORKFLOWS_CORDAPP.copy(config = mapOf("issuableCurrencies" to listOf("USD"))))
))
notaryNode = defaultNotaryNode.get()
aliceNode = alice.get()
bobNode = bob.get()
issuerNodeGBP = issuerGBP.get()
issuerNodeUSD = issuerUSD.get()
arrayOf(notaryNode, aliceNode, bobNode, issuerNodeGBP, issuerNodeUSD).forEach {
println("${it.nodeInfo.legalIdentities.first()} started on ${it.rpcAddress}")
}
when {
options.has("S") -> startNormalSimulation()
options.has("F") -> startErrorFlowsSimulation()
}
}
}
private fun setUpRPC() {
// Register with alice to use alice's RPC proxy to create random events.
val aliceClient = CordaRPCClient(aliceNode.rpcAddress)
val aliceConnection = aliceClient.start(user.username, user.password)
val aliceRPC = aliceConnection.proxy
val bobClient = CordaRPCClient(bobNode.rpcAddress)
val bobConnection = bobClient.start(user.username, user.password)
val bobRPC = bobConnection.proxy
val issuerClientGBP = CordaRPCClient(issuerNodeGBP.rpcAddress)
val issuerGBPConnection = issuerClientGBP.start(manager.username, manager.password)
val issuerRPCGBP = issuerGBPConnection.proxy
val issuerClientUSD = CordaRPCClient(issuerNodeUSD.rpcAddress)
val issuerUSDConnection = issuerClientUSD.start(manager.username, manager.password)
val issuerRPCUSD = issuerUSDConnection.proxy
RPCConnections.addAll(listOf(aliceConnection, bobConnection, issuerGBPConnection, issuerUSDConnection))
issuers.putAll(mapOf(USD to issuerRPCUSD, GBP to issuerRPCGBP))
parties.addAll(listOf(aliceNode.nodeInfo.legalIdentities.first() to aliceRPC,
bobNode.nodeInfo.legalIdentities.first() to bobRPC,
issuerNodeGBP.nodeInfo.legalIdentities.first() to issuerRPCGBP,
issuerNodeUSD.nodeInfo.legalIdentities.first() to issuerRPCUSD))
}
private fun startSimulation(eventGenerator: EventGenerator, maxIterations: Int) {
// Log to logger when flow finish.
fun FlowHandle<AbstractCashFlow.Result>.log(seq: Int, name: String) {
val out = "[$seq] $name $id :"
returnValue.thenMatch({ (stx) ->
Main.log.info("$out ${stx.id} ${(stx.tx.outputs.first().data as Cash.State).amount}") // XXX: Why Main's log?
}, {
Main.log.info("$out ${it.message}")
})
}
for (i in 0..maxIterations) {
Thread.sleep(1000)
// Issuer requests.
eventGenerator.issuerGenerator.map { request ->
when (request) {
is IssueAndPaymentRequest -> issuers[request.amount.token]?.let {
println("${Instant.now()} [$i] ISSUING ${request.amount} with ref ${request.issueRef} to ${request.recipient}")
it.startFlow(::CashIssueAndPaymentFlow, request).log(i, "${request.amount.token}Issuer")
}
is ExitRequest -> issuers[request.amount.token]?.let {
println("${Instant.now()} [$i] EXITING ${request.amount} with ref ${request.issuerRef}")
it.startFlow(::CashExitFlow, request).log(i, "${request.amount.token}Exit")
}
else -> throw IllegalArgumentException("Unsupported command: $request")
}
}.generate(SplittableRandom())
// Party pay requests.
eventGenerator.moveCashGenerator.combine(Generator.pickOne(parties)) { request, (party, rpc) ->
println("${Instant.now()} [$i] SENDING ${request.amount} from $party to ${request.recipient}")
rpc.startFlow(::CashPaymentFlow, request).log(i, party.name.toString())
}.generate(SplittableRandom())
}
println("Simulation completed")
}
private fun startNormalSimulation() {
println("Running simulation mode ...")
setUpRPC()
notary = aliceNode.rpc.notaryIdentities().first()
val eventGenerator = EventGenerator(
parties = parties.map { it.first },
notary = notary,
currencies = listOf(GBP, USD)
)
val maxIterations = 100_000
val anonymous = true
// Pre allocate some money to each party.
eventGenerator.parties.forEach {
for (ref in 0..1) {
for ((currency, issuer) in issuers) {
val amount = Amount(1_000_000, currency)
issuer.startFlow(::CashIssueAndPaymentFlow, amount, OpaqueBytes.of( ref.toByte() ),
it, anonymous, notary).returnValue.getOrThrow()
}
}
}
startSimulation(eventGenerator, maxIterations)
onEnd()
}
private fun startErrorFlowsSimulation() {
println("Running flows with errors simulation mode ...")
setUpRPC()
val eventGenerator = ErrorFlowsEventGenerator(
parties = parties.map { it.first },
notary = notary,
currencies = listOf(GBP, USD)
)
val maxIterations = 10_000
startSimulation(eventGenerator, maxIterations)
onEnd()
}
}

View File

@ -8,11 +8,9 @@ import javafx.scene.control.ButtonType
import javafx.scene.image.Image
import javafx.stage.Stage
import jfxtras.resources.JFXtrasFontRoboto
import joptsimple.OptionParser
import net.corda.client.jfx.model.Models
import net.corda.client.jfx.model.NodeMonitorModel
import net.corda.client.jfx.model.observableValue
import net.corda.core.utilities.contextLogger
import net.corda.explorer.model.CordaViewModel
import net.corda.explorer.model.SettingsModel
import net.corda.explorer.views.*
@ -31,10 +29,6 @@ class Main : App(MainView::class) {
private val loginView by inject<LoginView>()
private val fullscreen by observableValue(SettingsModel::fullscreenProperty)
companion object {
internal val log = contextLogger()
}
override fun start(stage: Stage) {
var nodeModel: NodeMonitorModel? = null
@ -122,16 +116,3 @@ class Main : App(MainView::class) {
FontAwesomeIconFactory.get() // Force initialisation.
}
}
/**
* This main method will start 5 nodes (Notary, USA Bank, UK Bank, Bob and Alice) locally for UI testing,
* which will bind to ports 20005, 20008, 20011, 20014 and 20017 locally.
*
* The simulation starts by pre-allocating chunks of cash to each of the parties in 2 currencies (USD, GBP), then it enters a loop which generates random events.
* On each iteration, the issuers will execute a Cash Issue or Cash Exit flow (at a 9:1 ratio) and a random party will execute a move of cash to another random party.
*/
fun main(args: Array<String>) {
val parser = OptionParser("SF")
val options = parser.parse(*args)
ExplorerSimulation(options)
}

View File

@ -39,7 +39,6 @@ import net.corda.finance.flows.CashIssueAndPaymentFlow
import net.corda.finance.flows.CashIssueAndPaymentFlow.IssueAndPaymentRequest
import net.corda.finance.flows.CashPaymentFlow
import net.corda.finance.flows.CashPaymentFlow.PaymentRequest
import net.corda.testing.core.singleIdentityAndCert
import org.controlsfx.dialog.ExceptionDialog
import tornadofx.*
import java.math.BigDecimal
@ -183,7 +182,7 @@ class NewTransaction : Fragment() {
partyBLabel.textProperty().bind(transactionTypeCB.valueProperty().map { it?.partyNameB?.let { "$it : " } })
partyBChoiceBox.apply {
visibleProperty().bind(transactionTypeCB.valueProperty().map { it?.partyNameB }.isNotNull())
items = FXCollections.observableList(parties.map { it.singleIdentityAndCert() }).sorted()
items = FXCollections.observableList(parties.map { it.legalIdentitiesAndCerts.single() }).sorted()
converter = stringConverter { it?.let { PartyNameFormatter.short.format(it.name) } ?: "" }
}
// Issuer