mirror of
https://github.com/corda/corda.git
synced 2025-06-22 17:09:00 +00:00
Modify the trader demo so it doesn't need a shell script to start it anymore. Now all configuration is in the driver program.
This commit is contained in:
11
build.gradle
11
build.gradle
@ -84,7 +84,7 @@ tasks.withType(JavaExec) {
|
|||||||
jvmArgs "-Dco.paralleluniverse.fibers.verifyInstrumentation"
|
jvmArgs "-Dco.paralleluniverse.fibers.verifyInstrumentation"
|
||||||
}
|
}
|
||||||
|
|
||||||
// Package up the other demo programs.
|
// Package up the demo programs.
|
||||||
task getRateFixDemo(type: CreateStartScripts) {
|
task getRateFixDemo(type: CreateStartScripts) {
|
||||||
mainClassName = "demos.RateFixDemoKt"
|
mainClassName = "demos.RateFixDemoKt"
|
||||||
applicationName = "get-rate-fix"
|
applicationName = "get-rate-fix"
|
||||||
@ -101,6 +101,14 @@ task getIRSDemo(type: CreateStartScripts) {
|
|||||||
classpath = jar.outputs.files + project.configurations.runtime
|
classpath = jar.outputs.files + project.configurations.runtime
|
||||||
}
|
}
|
||||||
|
|
||||||
|
task getTraderDemo(type: CreateStartScripts) {
|
||||||
|
mainClassName = "demos.TraderDemoKt"
|
||||||
|
applicationName = "trader-demo"
|
||||||
|
defaultJvmOpts = ["-javaagent:${configurations.quasar.singleFile}"]
|
||||||
|
outputDir = new File(project.buildDir, 'scripts')
|
||||||
|
classpath = jar.outputs.files + project.configurations.runtime
|
||||||
|
}
|
||||||
|
|
||||||
// Force windows script classpath to wildcard path to avoid the 'Command Line Is Too Long' issues
|
// Force windows script classpath to wildcard path to avoid the 'Command Line Is Too Long' issues
|
||||||
// with generated scripts. Include Jolokia .war explicitly as this isn't picked up by wildcard
|
// with generated scripts. Include Jolokia .war explicitly as this isn't picked up by wildcard
|
||||||
tasks.withType(CreateStartScripts)
|
tasks.withType(CreateStartScripts)
|
||||||
@ -138,5 +146,6 @@ jar.dependsOn quasarScan
|
|||||||
applicationDistribution.into("bin") {
|
applicationDistribution.into("bin") {
|
||||||
from(getRateFixDemo)
|
from(getRateFixDemo)
|
||||||
from(getIRSDemo)
|
from(getIRSDemo)
|
||||||
|
from(getTraderDemo)
|
||||||
fileMode = 0755
|
fileMode = 0755
|
||||||
}
|
}
|
@ -14,29 +14,27 @@ The demos have only been tested on MacOS X and Ubuntu Linux. If you have success
|
|||||||
The demos create node data directories in the root of the project. If something goes wrong with them, blow away the
|
The demos create node data directories in the root of the project. If something goes wrong with them, blow away the
|
||||||
directories and try again.
|
directories and try again.
|
||||||
|
|
||||||
For Windows users, the contents of the shell scripts are very trivial and can easily be done by hand from a command
|
|
||||||
window. Essentially, it just runs Gradle to create the startup scripts, and then starts the node with one set of
|
|
||||||
flags or another. Alternatively you could play with the new Linux syscall support in Windows 10!
|
|
||||||
|
|
||||||
Trader demo
|
Trader demo
|
||||||
-----------
|
-----------
|
||||||
|
|
||||||
Open two terminals, and in the first run:::
|
Open two terminals, and in the first run:::
|
||||||
|
|
||||||
./scripts/trader-demo.sh buyer
|
gradle installDist && ./build/install/r3prototyping/trader-demo.sh --mode=buyer
|
||||||
|
|
||||||
It will compile things, if necessary, then create a directory named "buyer" with a bunch of files inside and start
|
It will compile things, if necessary, then create a directory named trader-demo/buyer with a bunch of files inside and
|
||||||
the node. You should see it waiting for a trade to begin.
|
start the node. You should see it waiting for a trade to begin.
|
||||||
|
|
||||||
In the second terminal, run::
|
In the second terminal, run::
|
||||||
|
|
||||||
./scripts/trader-demo.sh seller
|
./build/install/r3prototyping/trader-demo.sh --mode=seller
|
||||||
|
|
||||||
You should see some log lines scroll past, and within a few seconds the messages "Purchase complete - we are a
|
You should see some log lines scroll past, and within a few seconds the messages "Purchase complete - we are a
|
||||||
happy customer!" and "Sale completed - we have a happy customer!" should be printed.
|
happy customer!" and "Sale completed - we have a happy customer!" should be printed.
|
||||||
|
|
||||||
If it doesn't work, jump on the mailing list and let us know.
|
If it doesn't work, jump on the mailing list and let us know.
|
||||||
|
|
||||||
|
On Windows, use the same commands, but run the batch file instead of the shell file.
|
||||||
|
|
||||||
|
|
||||||
IRS demo
|
IRS demo
|
||||||
--------
|
--------
|
||||||
|
@ -94,10 +94,15 @@ class StateMachineManager(val serviceHub: ServiceHub, val executor: AffinityExec
|
|||||||
field.get(null)
|
field.get(null)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
var restoreCheckpointsOnStart = true
|
||||||
|
}
|
||||||
|
|
||||||
init {
|
init {
|
||||||
Fiber.setDefaultUncaughtExceptionHandler { fiber, throwable ->
|
Fiber.setDefaultUncaughtExceptionHandler { fiber, throwable ->
|
||||||
(fiber as ProtocolStateMachineImpl<*>).logger.error("Caught exception from protocol", throwable)
|
(fiber as ProtocolStateMachineImpl<*>).logger.error("Caught exception from protocol", throwable)
|
||||||
}
|
}
|
||||||
|
if (restoreCheckpointsOnStart)
|
||||||
restoreCheckpoints()
|
restoreCheckpoints()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,33 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
mode=$1
|
|
||||||
|
|
||||||
if [ ! -e ./gradlew ]; then
|
|
||||||
echo "Run from the root directory please"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ "$SKIP_INSTALL" == "" ]]; then
|
|
||||||
./gradlew installDist
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ "$mode" == "buyer" ]]; then
|
|
||||||
if [ ! -d buyer ]; then
|
|
||||||
mkdir buyer
|
|
||||||
echo "myLegalName = Bank A" >buyer/config
|
|
||||||
fi
|
|
||||||
|
|
||||||
build/install/r3prototyping/bin/r3prototyping --dir=buyer --service-fake-trades --network-address=localhost
|
|
||||||
elif [[ "$mode" == "seller" ]]; then
|
|
||||||
if [ ! -d seller ]; then
|
|
||||||
mkdir seller
|
|
||||||
echo "myLegalName = Bank B" >seller/config
|
|
||||||
fi
|
|
||||||
|
|
||||||
build/install/r3prototyping/bin/r3prototyping --dir=seller --fake-trade-with=localhost --network-address=localhost:31340 --network-map-identity-file=buyer/identity-public --network-map-address=localhost
|
|
||||||
else
|
|
||||||
echo "Run like this, one in each tab:"
|
|
||||||
echo
|
|
||||||
echo " scripts/trader-demo.sh buyer"
|
|
||||||
echo " scripts/trader-demo.sh seller"
|
|
||||||
fi
|
|
@ -4,14 +4,15 @@ import co.paralleluniverse.fibers.Suspendable
|
|||||||
import com.google.common.net.HostAndPort
|
import com.google.common.net.HostAndPort
|
||||||
import com.typesafe.config.ConfigFactory
|
import com.typesafe.config.ConfigFactory
|
||||||
import contracts.CommercialPaper
|
import contracts.CommercialPaper
|
||||||
import core.*
|
|
||||||
import core.contracts.*
|
import core.contracts.*
|
||||||
import core.crypto.Party
|
import core.crypto.Party
|
||||||
import core.crypto.SecureHash
|
import core.crypto.SecureHash
|
||||||
import core.crypto.generateKeyPair
|
import core.crypto.generateKeyPair
|
||||||
|
import core.days
|
||||||
|
import core.logElapsedTime
|
||||||
import core.messaging.SingleMessageRecipient
|
import core.messaging.SingleMessageRecipient
|
||||||
|
import core.messaging.StateMachineManager
|
||||||
import core.node.Node
|
import core.node.Node
|
||||||
import core.node.NodeConfiguration
|
|
||||||
import core.node.NodeConfigurationFromConfig
|
import core.node.NodeConfigurationFromConfig
|
||||||
import core.node.NodeInfo
|
import core.node.NodeInfo
|
||||||
import core.node.services.NetworkMapService
|
import core.node.services.NetworkMapService
|
||||||
@ -21,6 +22,8 @@ import core.node.services.ServiceType
|
|||||||
import core.node.subsystems.ArtemisMessagingService
|
import core.node.subsystems.ArtemisMessagingService
|
||||||
import core.node.subsystems.NodeWalletService
|
import core.node.subsystems.NodeWalletService
|
||||||
import core.protocols.ProtocolLogic
|
import core.protocols.ProtocolLogic
|
||||||
|
import core.random63BitValue
|
||||||
|
import core.seconds
|
||||||
import core.serialization.deserialize
|
import core.serialization.deserialize
|
||||||
import core.utilities.ANSIProgressRenderer
|
import core.utilities.ANSIProgressRenderer
|
||||||
import core.utilities.BriefLogFormatter
|
import core.utilities.BriefLogFormatter
|
||||||
@ -43,17 +46,10 @@ import kotlin.test.assertEquals
|
|||||||
|
|
||||||
fun main(args: Array<String>) {
|
fun main(args: Array<String>) {
|
||||||
val parser = OptionParser()
|
val parser = OptionParser()
|
||||||
val networkAddressArg = parser.accepts("network-address").withRequiredArg().required()
|
|
||||||
val dirArg = parser.accepts("directory").withRequiredArg().defaultsTo("nodedata")
|
|
||||||
|
|
||||||
// Some dummy functionality that won't last long ...
|
val modeArg = parser.accepts("mode").withRequiredArg().required()
|
||||||
|
val myNetworkAddress = parser.accepts("network-address").withRequiredArg().defaultsTo("localhost")
|
||||||
// Mode flags for the first demo.
|
val theirNetworkAddress = parser.accepts("other-network-address").withRequiredArg().defaultsTo("localhost")
|
||||||
val serviceFakeTradesArg = parser.accepts("service-fake-trades")
|
|
||||||
val fakeTradeWithArg = parser.accepts("fake-trade-with").requiredUnless(serviceFakeTradesArg).withRequiredArg()
|
|
||||||
|
|
||||||
val networkMapIdentityFile = parser.accepts("network-map-identity-file").requiredIf(fakeTradeWithArg).withRequiredArg()
|
|
||||||
val networkMapNetAddr = parser.accepts("network-map-address").requiredIf(networkMapIdentityFile).withRequiredArg()
|
|
||||||
|
|
||||||
val options = try {
|
val options = try {
|
||||||
parser.parse(*args)
|
parser.parse(*args)
|
||||||
@ -63,39 +59,46 @@ fun main(args: Array<String>) {
|
|||||||
exitProcess(1)
|
exitProcess(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Suppress the Artemis MQ noise, and activate the demo logging.
|
val mode = options.valueOf(modeArg)
|
||||||
BriefLogFormatter.initVerbose("+demo.buyer", "+demo.seller", "-org.apache.activemq")
|
|
||||||
|
|
||||||
val dir = Paths.get(options.valueOf(dirArg))
|
val DIRNAME = "trader-demo"
|
||||||
val configFile = dir.resolve("config")
|
val BUYER = "buyer"
|
||||||
|
val SELLER = "seller"
|
||||||
|
|
||||||
if (!Files.exists(dir)) {
|
if (mode !in setOf(BUYER, SELLER)) {
|
||||||
Files.createDirectory(dir)
|
printHelp()
|
||||||
}
|
|
||||||
|
|
||||||
val config = loadConfigFile(configFile)
|
|
||||||
|
|
||||||
val advertisedServices: Set<ServiceType>
|
|
||||||
val myNetAddr = HostAndPort.fromString(options.valueOf(networkAddressArg)).withDefaultPort(Node.DEFAULT_PORT)
|
|
||||||
val listening = options.has(serviceFakeTradesArg)
|
|
||||||
|
|
||||||
if (listening && config.myLegalName != "Bank A") {
|
|
||||||
println("The buyer node must have a legal name of 'Bank A'. Please edit the config file.")
|
|
||||||
exitProcess(1)
|
exitProcess(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
val networkMapId = if (options.has(networkMapIdentityFile)) {
|
// Suppress the Artemis MQ noise, and activate the demo logging.
|
||||||
val addr = HostAndPort.fromString(options.valueOf(networkMapNetAddr)).withDefaultPort(Node.DEFAULT_PORT)
|
BriefLogFormatter.initVerbose("+demo.buyer", "+demo.seller", "-org.apache.activemq")
|
||||||
val path = Paths.get(options.valueOf(networkMapIdentityFile))
|
|
||||||
|
val dir = Paths.get(DIRNAME, mode)
|
||||||
|
Files.createDirectories(dir)
|
||||||
|
|
||||||
|
val advertisedServices: Set<ServiceType>
|
||||||
|
val myNetAddr = HostAndPort.fromString(options.valueOf(myNetworkAddress)).withDefaultPort(if (mode == BUYER) Node.DEFAULT_PORT else 31340)
|
||||||
|
val theirNetAddr = HostAndPort.fromString(options.valueOf(theirNetworkAddress)).withDefaultPort(if (mode == SELLER) Node.DEFAULT_PORT else 31340)
|
||||||
|
|
||||||
|
val listening = mode == BUYER
|
||||||
|
val config = run {
|
||||||
|
val override = ConfigFactory.parseString("""myLegalName = ${ if (mode == BUYER) "Bank A" else "Bank B" }""")
|
||||||
|
NodeConfigurationFromConfig(override.withFallback(ConfigFactory.load()))
|
||||||
|
}
|
||||||
|
|
||||||
|
val networkMapId = if (mode == SELLER) {
|
||||||
|
val path = Paths.get(DIRNAME, BUYER, "identity-public")
|
||||||
val party = Files.readAllBytes(path).deserialize<Party>()
|
val party = Files.readAllBytes(path).deserialize<Party>()
|
||||||
advertisedServices = emptySet()
|
advertisedServices = emptySet()
|
||||||
NodeInfo(ArtemisMessagingService.makeRecipient(addr), party, setOf(NetworkMapService.Type))
|
NodeInfo(ArtemisMessagingService.makeRecipient(theirNetAddr), party, setOf(NetworkMapService.Type))
|
||||||
} else {
|
} else {
|
||||||
// We must be the network map service
|
// We must be the network map service
|
||||||
advertisedServices = setOf(NetworkMapService.Type, NotaryService.Type)
|
advertisedServices = setOf(NetworkMapService.Type, NotaryService.Type)
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Remove this once checkpoint resume works.
|
||||||
|
StateMachineManager.restoreCheckpointsOnStart = false
|
||||||
val node = logElapsedTime("Node startup") { Node(dir, myNetAddr, config, networkMapId, advertisedServices).start() }
|
val node = logElapsedTime("Node startup") { Node(dir, myNetAddr, config, networkMapId, advertisedServices).start() }
|
||||||
|
|
||||||
if (listening) {
|
if (listening) {
|
||||||
@ -110,11 +113,6 @@ fun main(args: Array<String>) {
|
|||||||
ANSIProgressRenderer.progressTracker = buyer.progressTracker
|
ANSIProgressRenderer.progressTracker = buyer.progressTracker
|
||||||
node.smm.add("demo.buyer", buyer).get() // This thread will halt forever here.
|
node.smm.add("demo.buyer", buyer).get() // This thread will halt forever here.
|
||||||
} else {
|
} else {
|
||||||
if (!options.has(fakeTradeWithArg)) {
|
|
||||||
println("Need the --fake-trade-with command line argument")
|
|
||||||
exitProcess(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure we have the transaction prospectus attachment loaded into our store.
|
// Make sure we have the transaction prospectus attachment loaded into our store.
|
||||||
if (node.storage.attachments.openAttachment(TraderDemoProtocolSeller.PROSPECTUS_HASH) == null) {
|
if (node.storage.attachments.openAttachment(TraderDemoProtocolSeller.PROSPECTUS_HASH) == null) {
|
||||||
TraderDemoProtocolSeller::class.java.getResourceAsStream("bank-of-london-cp.jar").use {
|
TraderDemoProtocolSeller::class.java.getResourceAsStream("bank-of-london-cp.jar").use {
|
||||||
@ -123,8 +121,7 @@ fun main(args: Array<String>) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val peerAddr = HostAndPort.fromString(options.valuesOf(fakeTradeWithArg).single()).withDefaultPort(Node.DEFAULT_PORT)
|
val otherSide = ArtemisMessagingService.makeRecipient(theirNetAddr)
|
||||||
val otherSide = ArtemisMessagingService.makeRecipient(peerAddr)
|
|
||||||
val seller = TraderDemoProtocolSeller(myNetAddr, otherSide)
|
val seller = TraderDemoProtocolSeller(myNetAddr, otherSide)
|
||||||
ANSIProgressRenderer.progressTracker = seller.progressTracker
|
ANSIProgressRenderer.progressTracker = seller.progressTracker
|
||||||
node.smm.add("demo.seller", seller).get()
|
node.smm.add("demo.seller", seller).get()
|
||||||
@ -277,43 +274,6 @@ class TraderDemoProtocolSeller(val myAddress: HostAndPort,
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun loadConfigFile(configFile: Path): NodeConfiguration {
|
|
||||||
fun askAdminToEditConfig(configFile: Path?) {
|
|
||||||
println()
|
|
||||||
println("This is the first run, so you should edit the config file in $configFile and then start the node again.")
|
|
||||||
println()
|
|
||||||
exitProcess(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
val defaultLegalName = "Global MegaCorp, Ltd."
|
|
||||||
|
|
||||||
if (!Files.exists(configFile)) {
|
|
||||||
createDefaultConfigFile(configFile, defaultLegalName)
|
|
||||||
askAdminToEditConfig(configFile)
|
|
||||||
}
|
|
||||||
|
|
||||||
System.setProperty("config.file", configFile.toAbsolutePath().toString())
|
|
||||||
val config = NodeConfigurationFromConfig(ConfigFactory.load())
|
|
||||||
|
|
||||||
// Make sure admin did actually edit at least the legal name.
|
|
||||||
if (config.myLegalName == defaultLegalName)
|
|
||||||
askAdminToEditConfig(configFile)
|
|
||||||
|
|
||||||
return config
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun createDefaultConfigFile(configFile: Path?, defaultLegalName: String) {
|
|
||||||
Files.write(configFile,
|
|
||||||
"""
|
|
||||||
# Node configuration: give the buyer node the name 'Bank of Zurich' (no quotes)
|
|
||||||
# The seller node can be named whatever you like.
|
|
||||||
|
|
||||||
myLegalName = $defaultLegalName
|
|
||||||
""".trimIndent().toByteArray())
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun printHelp() {
|
private fun printHelp() {
|
||||||
println("""
|
println("Please refer to the documentation in docs/build/index.html to learn how to run the demo.")
|
||||||
Please refer to the documentation in docs/build/index.html to learn how to run the demo.
|
|
||||||
""".trimIndent())
|
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user