mirror of
https://github.com/corda/corda.git
synced 2025-02-21 01:42:24 +00:00
Merged in irsdemowindows (pull request #125)
IRS Demo support on Windows - Replaced shell scripts with Kotlin demo runner.
This commit is contained in:
commit
ff467beaec
@ -12,23 +12,35 @@ so far. We have:
|
||||
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.
|
||||
|
||||
.. warning:: Corda is developed on MacOS and works best on UNIX systems. The trader demo is easily run on Windows but
|
||||
you won't get the nice coloured output. The IRS demo relies on a shell script wrapper and isn't so easily run on
|
||||
Windows currently: we will fix this soon.
|
||||
.. note:: Corda is developed on MacOS and works best on UNIX systems. Both demos are easily run on Windows but
|
||||
you won't get the nice coloured output.
|
||||
|
||||
Trader demo
|
||||
-----------
|
||||
|
||||
.. note:: On Windows, use the same commands, but run the batch file instead of the shell file (add .bat to the command)
|
||||
Open two terminals, and in the first run:
|
||||
|
||||
Open two terminals, and in the first run:::
|
||||
.. note:: If you are planning to use non-default configuration you will need to run with --role=SetupA and --role=SetupB
|
||||
beforehand with the same parameters you plan to supply to the respective nodes.
|
||||
|
||||
gradle installDist && ./build/install/r3prototyping/bin/trader-demo --role=BUYER
|
||||
**Windows**::
|
||||
|
||||
gradlew.bat & .\build\install\r3prototyping\bin\trader-demo --role=BUYER
|
||||
|
||||
**Other**::
|
||||
|
||||
Other: ./gradlew installDist && ./build/install/r3prototyping/bin/trader-demo --role=BUYER
|
||||
|
||||
It will compile things, if necessary, then create a directory named trader-demo/buyer with a bunch of files inside and
|
||||
start the node. You should see it waiting for a trade to begin.
|
||||
|
||||
In the second terminal, run::
|
||||
In the second terminal, run:
|
||||
|
||||
**Windows**::
|
||||
|
||||
.\build\install\r3prototyping\bin\trader-demo --role=SELLER
|
||||
|
||||
**Other**::
|
||||
|
||||
./build/install/r3prototyping/bin/trader-demo --role=SELLER
|
||||
|
||||
@ -41,26 +53,48 @@ If it doesn't work, jump on the mailing list and let us know.
|
||||
IRS demo
|
||||
--------
|
||||
|
||||
.. warning:: This demo currently works best on MacOS or Linux
|
||||
Open three terminals. In the first run:
|
||||
|
||||
Open three terminals. In the first run:::
|
||||
**Windows**::
|
||||
|
||||
./scripts/irs-demo.sh nodeA
|
||||
gradlew.bat installDist & .\build\install\r3prototyping\bin\irsdemo.bat --role=NodeA
|
||||
|
||||
And in the second run:::
|
||||
**Other**::
|
||||
|
||||
./scripts/irs-demo.sh nodeB
|
||||
./gradlew installDist && ./build/install/r3prototyping/bin/irsdemo --role=NodeA
|
||||
|
||||
And in the second run:
|
||||
|
||||
**Windows**::
|
||||
|
||||
.\build\install\r3prototyping\bin\irsdemo.bat --role=NodeB
|
||||
|
||||
**Other**::
|
||||
|
||||
./build/install/r3prototyping/bin/irsdemo --role=NodeB
|
||||
|
||||
The node in the first terminal will complain that it didn't know about nodeB, so restart it. It'll then find the
|
||||
location and identity keys of nodeA and be happy. NodeB also doubles up as the interest rates oracle and you should
|
||||
see some rates data get loaded.
|
||||
|
||||
Now in the third terminal run:::
|
||||
Now in the third terminal run:
|
||||
|
||||
./scripts/irs-demo.sh trade trade1
|
||||
**Windows**::
|
||||
|
||||
.\build\install\r3prototyping\bin\irsdemo.bat --role=Trade trade1
|
||||
|
||||
**Other**::
|
||||
|
||||
./build/install/r3prototyping/bin/irsdemo --role=Trade trade1
|
||||
|
||||
You should see some activity in the other two terminals as they set up the deal. You can now run this command in
|
||||
a separate window to roll the fake clock forward and trigger lots of fixing events. Things go fast so make sure you
|
||||
can see the other terminals whilst you run this command!::
|
||||
can see the other terminals whilst you run this command!:
|
||||
|
||||
./scripts/irs-demo.sh date 2017-01-30
|
||||
**Windows**::
|
||||
|
||||
.\build\install\r3prototyping\bin\irsdemo.bat --role=Date 2017-01-30
|
||||
|
||||
**Other**::
|
||||
|
||||
./build/install/r3prototyping/bin/irsdemo --role=Date 2017-01-30
|
||||
|
@ -1,69 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
mode=$1
|
||||
|
||||
if [ ! -e ./gradlew ]; then
|
||||
echo "Run from the root directory please"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -d build/install/r3prototyping ] && [[ "$SKIP_INSTALL" == "" ]]; then
|
||||
./gradlew installDist
|
||||
fi
|
||||
|
||||
if [[ "$mode" == "nodeA" ]]; then
|
||||
if [ ! -d nodeA ]; then
|
||||
mkdir nodeA
|
||||
echo "myLegalName = Bank A" >nodeA/config
|
||||
fi
|
||||
|
||||
RC=83
|
||||
while [ $RC -eq 83 ]
|
||||
do
|
||||
build/install/r3prototyping/bin/irsdemo --dir=nodeA --network-address=localhost \
|
||||
--fake-trade-with-address=localhost:31340 --fake-trade-with-identity=nodeB/identity-public \
|
||||
--network-map-identity-file=nodeA/identity-public --network-map-address=localhost
|
||||
RC=$?
|
||||
done
|
||||
elif [[ "$mode" == "nodeB" ]]; then
|
||||
if [ ! -d nodeB ]; then
|
||||
mkdir nodeB
|
||||
echo "myLegalName = Bank B" >nodeB/config
|
||||
fi
|
||||
|
||||
# enable job control
|
||||
set -o monitor
|
||||
|
||||
RC=83
|
||||
while [ $RC -eq 83 ]
|
||||
do
|
||||
build/install/r3prototyping/bin/irsdemo --dir=nodeB --network-address=localhost:31340 \
|
||||
--fake-trade-with-address=localhost --fake-trade-with-identity=nodeA/identity-public \
|
||||
--network-map-identity-file=nodeA/identity-public --network-map-address=localhost &
|
||||
while ! curl -F rates=@scripts/example.rates.txt http://localhost:31341/upload/interest-rates; do
|
||||
echo "Retry to upload interest rates to oracle after 5 seconds"
|
||||
sleep 5
|
||||
done
|
||||
fg %1
|
||||
RC=$?
|
||||
done
|
||||
elif [[ "$mode" == "trade" && "$2" != "" ]]; then
|
||||
tradeID=$2
|
||||
echo "Uploading tradeID ${tradeID}"
|
||||
sed "s/tradeXXX/${tradeID}/g" scripts/example-irs-trade.json | curl -H "Content-Type: application/json" -d @- http://localhost:31338/api/irs/deals
|
||||
elif [[ "$mode" == "date" && "$2" != "" ]]; then
|
||||
demodate=$2
|
||||
echo "Setting demo date to ${demodate}"
|
||||
echo "\"$demodate\"" | curl -H "Content-Type: application/json" -X PUT -d @- http://localhost:31338/api/irs/demodate
|
||||
else
|
||||
echo "Run like this, one in each tab:"
|
||||
echo
|
||||
echo " scripts/irs-demo.sh nodeA"
|
||||
echo " scripts/irs-demo.sh nodeB"
|
||||
echo
|
||||
echo "To upload a trade as e.g. trade10"
|
||||
echo " scripts/irs-demo.sh trade trade10"
|
||||
echo
|
||||
echo "To set the demo date, and post fixings in the interval, to e.g. 2017-01-30"
|
||||
echo " scripts/irs-demo.sh date 2017-01-30"
|
||||
fi
|
@ -1,46 +1,91 @@
|
||||
package com.r3corda.demos
|
||||
|
||||
import com.google.common.net.HostAndPort
|
||||
import com.typesafe.config.ConfigFactory
|
||||
import com.r3corda.core.crypto.Party
|
||||
import com.r3corda.core.logElapsedTime
|
||||
import com.r3corda.node.internal.Node
|
||||
import com.r3corda.node.services.config.NodeConfiguration
|
||||
import com.r3corda.node.services.config.NodeConfigurationFromConfig
|
||||
import com.r3corda.core.node.NodeInfo
|
||||
import com.r3corda.node.services.network.NetworkMapService
|
||||
import com.r3corda.node.services.clientapi.NodeInterestRates
|
||||
import com.r3corda.node.services.transactions.NotaryService
|
||||
import com.r3corda.core.node.services.ServiceType
|
||||
import com.r3corda.node.services.messaging.ArtemisMessagingService
|
||||
import com.r3corda.core.serialization.deserialize
|
||||
import com.r3corda.core.utilities.BriefLogFormatter
|
||||
import com.r3corda.demos.api.InterestRateSwapAPI
|
||||
import com.r3corda.demos.protocols.AutoOfferProtocol
|
||||
import com.r3corda.demos.protocols.ExitServerProtocol
|
||||
import com.r3corda.demos.protocols.UpdateBusinessDayProtocol
|
||||
import com.r3corda.node.internal.Node
|
||||
import com.r3corda.node.services.clientapi.NodeInterestRates
|
||||
import com.r3corda.node.services.config.NodeConfiguration
|
||||
import com.r3corda.node.services.config.NodeConfigurationFromConfig
|
||||
import com.r3corda.node.services.messaging.ArtemisMessagingService
|
||||
import com.r3corda.node.services.network.NetworkMapService
|
||||
import com.r3corda.node.internal.AbstractNode
|
||||
import com.r3corda.node.internal.testing.MockNetwork
|
||||
import com.r3corda.node.services.network.InMemoryMessagingNetwork
|
||||
import com.r3corda.node.services.transactions.SimpleNotaryService
|
||||
import com.typesafe.config.ConfigFactory
|
||||
import joptsimple.OptionParser
|
||||
import joptsimple.OptionSet
|
||||
import joptsimple.OptionSpec
|
||||
import java.io.DataOutputStream
|
||||
import java.io.File
|
||||
import java.net.HttpURLConnection
|
||||
import java.net.URL
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Path
|
||||
import java.nio.file.Paths
|
||||
import java.util.*
|
||||
import kotlin.concurrent.fixedRateTimer
|
||||
import kotlin.system.exitProcess
|
||||
import org.apache.commons.io.IOUtils
|
||||
import java.io.FileNotFoundException
|
||||
|
||||
// IRS DEMO
|
||||
//
|
||||
// TODO: Please see TBD
|
||||
// Please see docs/build/html/running-the-trading-demo.html
|
||||
//
|
||||
// TODO: TBD
|
||||
//
|
||||
// The different roles in the scenario this program can adopt are:
|
||||
|
||||
enum class IRSDemoRole {
|
||||
SetupNodeA,
|
||||
SetupNodeB,
|
||||
NodeA,
|
||||
NodeB,
|
||||
Trade,
|
||||
Date
|
||||
}
|
||||
|
||||
private class NodeParams() {
|
||||
var id: Int = -1
|
||||
var dir : Path = Paths.get("")
|
||||
var address : String = ""
|
||||
var mapAddress: String = ""
|
||||
var identityFile: Path = Paths.get("")
|
||||
var tradeWithAddrs: List<String> = listOf()
|
||||
var tradeWithIdentities: List<Path> = listOf()
|
||||
var uploadRates: Boolean = false
|
||||
var defaultLegalName: String = ""
|
||||
}
|
||||
|
||||
private class DemoArgs() {
|
||||
lateinit var roleArg: OptionSpec<IRSDemoRole>
|
||||
lateinit var networkAddressArg: OptionSpec<String>
|
||||
lateinit var dirArg: OptionSpec<String>
|
||||
lateinit var networkMapIdentityFile: OptionSpec<String>
|
||||
lateinit var networkMapNetAddr: OptionSpec<String>
|
||||
lateinit var fakeTradeWithAddr: OptionSpec<String>
|
||||
lateinit var fakeTradeWithIdentityFile: OptionSpec<String>
|
||||
lateinit var nonOptions: OptionSpec<String>
|
||||
}
|
||||
|
||||
private class NotSetupException: Throwable {
|
||||
constructor(message: String): super(message) {}
|
||||
}
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
val parser = OptionParser()
|
||||
val networkAddressArg = parser.accepts("network-address").withRequiredArg().required()
|
||||
val dirArg = parser.accepts("directory").withRequiredArg().defaultsTo("nodedata")
|
||||
|
||||
val networkMapIdentityFile = parser.accepts("network-map-identity-file").withRequiredArg()
|
||||
val networkMapNetAddr = parser.accepts("network-map-address").requiredIf(networkMapIdentityFile).withRequiredArg()
|
||||
|
||||
// Use these to list one or more peers (again, will be superseded by discovery implementation)
|
||||
val fakeTradeWithAddr = parser.accepts("fake-trade-with-address").withRequiredArg().required()
|
||||
val fakeTradeWithIdentityFile = parser.accepts("fake-trade-with-identity-file").withRequiredArg().required()
|
||||
|
||||
val demoArgs = setupArgs(parser)
|
||||
val options = try {
|
||||
parser.parse(*args)
|
||||
} catch (e: Exception) {
|
||||
@ -52,112 +97,384 @@ fun main(args: Array<String>) {
|
||||
// Suppress the Artemis MQ noise, and activate the demo logging.
|
||||
BriefLogFormatter.initVerbose("+demo.irsdemo", "+api-call", "+platform.deal", "-org.apache.activemq")
|
||||
|
||||
val dir = Paths.get(options.valueOf(dirArg))
|
||||
val configFile = dir.resolve("config")
|
||||
val role = options.valueOf(demoArgs.roleArg)!!
|
||||
if(role == IRSDemoRole.SetupNodeA) {
|
||||
val nodeParams = configureNodeParams(IRSDemoRole.NodeA, demoArgs, options)
|
||||
setup(nodeParams)
|
||||
} else if(role == IRSDemoRole.SetupNodeB) {
|
||||
val nodeParams = configureNodeParams(IRSDemoRole.NodeB, demoArgs, options)
|
||||
setup(nodeParams)
|
||||
} else if(role == IRSDemoRole.Trade) {
|
||||
val tradeIdArgs = options.valuesOf(demoArgs.nonOptions)
|
||||
if (tradeIdArgs.size > 0) {
|
||||
val tradeId = tradeIdArgs[0]
|
||||
val host = if (options.has(demoArgs.networkAddressArg)) {
|
||||
options.valueOf(demoArgs.networkAddressArg)
|
||||
} else {
|
||||
"http://localhost:" + (Node.DEFAULT_PORT + 1)
|
||||
}
|
||||
|
||||
if (!Files.exists(dir)) {
|
||||
Files.createDirectory(dir)
|
||||
}
|
||||
if (runTrade(tradeId, host)) {
|
||||
exitProcess(0)
|
||||
} else {
|
||||
exitProcess(1)
|
||||
}
|
||||
} else {
|
||||
println("Please provide a trade ID")
|
||||
exitProcess(1)
|
||||
}
|
||||
} else if(role == IRSDemoRole.Date) {
|
||||
val dateStrArgs = options.valuesOf(demoArgs.nonOptions)
|
||||
if (dateStrArgs.size > 0) {
|
||||
val dateStr = dateStrArgs[0]
|
||||
val host = if (options.has(demoArgs.networkAddressArg)) {
|
||||
options.valueOf(demoArgs.networkAddressArg)
|
||||
} else {
|
||||
"http://localhost:" + (Node.DEFAULT_PORT + 1)
|
||||
}
|
||||
|
||||
val config = loadConfigFile(configFile)
|
||||
val advertisedServices: Set<ServiceType>
|
||||
val myNetAddr = HostAndPort.fromString(options.valueOf(networkAddressArg)).withDefaultPort(Node.DEFAULT_PORT)
|
||||
|
||||
val networkMapId = if (options.valueOf(networkMapNetAddr).equals(options.valueOf(networkAddressArg))) {
|
||||
// This node provides network map and notary services
|
||||
advertisedServices = setOf(NetworkMapService.Type, SimpleNotaryService.Type)
|
||||
null
|
||||
runDateChange(dateStr, host)
|
||||
} else {
|
||||
println("Please provide a date")
|
||||
exitProcess(1)
|
||||
}
|
||||
} else {
|
||||
advertisedServices = setOf(NodeInterestRates.Type)
|
||||
// If these directory and identity file arguments aren't specified then we can assume a default setup and
|
||||
// create everything that is needed without needing to run setup.
|
||||
if(!options.has(demoArgs.dirArg) && !options.has(demoArgs.fakeTradeWithIdentityFile)) {
|
||||
createNodeConfig(createNodeAParams());
|
||||
createNodeConfig(createNodeBParams());
|
||||
}
|
||||
|
||||
try {
|
||||
nodeInfo(options.valueOf(networkMapNetAddr), options.valueOf(networkMapIdentityFile), setOf(NetworkMapService.Type, SimpleNotaryService.Type))
|
||||
} catch (e: Exception) {
|
||||
null
|
||||
runNode(configureNodeParams(role, demoArgs, options))
|
||||
} catch (e: NotSetupException) {
|
||||
println(e.message)
|
||||
exitProcess(1)
|
||||
}
|
||||
|
||||
exitProcess(0)
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupArgs(parser: OptionParser): DemoArgs {
|
||||
val args = DemoArgs()
|
||||
|
||||
args.roleArg = parser.accepts("role").withRequiredArg().ofType(IRSDemoRole::class.java).required()
|
||||
args.networkAddressArg = parser.accepts("network-address").withOptionalArg()
|
||||
args.dirArg = parser.accepts("directory").withOptionalArg()
|
||||
args.networkMapIdentityFile = parser.accepts("network-map-identity-file").withOptionalArg()
|
||||
args.networkMapNetAddr = parser.accepts("network-map-address").withRequiredArg().defaultsTo("localhost")
|
||||
// Use these to list one or more peers (again, will be superseded by discovery implementation)
|
||||
args.fakeTradeWithAddr = parser.accepts("fake-trade-with-address").withOptionalArg()
|
||||
args.fakeTradeWithIdentityFile = parser.accepts("fake-trade-with-identity-file").withOptionalArg()
|
||||
args.nonOptions = parser.nonOptions().ofType(String::class.java)
|
||||
|
||||
return args
|
||||
}
|
||||
|
||||
private fun setup(params: NodeParams) {
|
||||
createNodeConfig(params)
|
||||
}
|
||||
|
||||
private fun runDateChange(date: String, host: String) : Boolean {
|
||||
val url = URL(host + "/api/irs/demodate")
|
||||
if(putJson(url, "\"" + date + "\"")) {
|
||||
println("Date changed")
|
||||
return true
|
||||
} else {
|
||||
println("Date failed to change")
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
private fun runTrade(tradeId: String, host: String) : Boolean {
|
||||
println("Uploading tradeID " + tradeId)
|
||||
val fileContents = IOUtils.toString(NodeParams::class.java.getResourceAsStream("example-irs-trade.json"))
|
||||
val tradeFile = fileContents.replace("tradeXXX", tradeId)
|
||||
val url = URL(host + "/api/irs/deals")
|
||||
if(postJson(url, tradeFile)) {
|
||||
println("Trade sent")
|
||||
return true
|
||||
} else {
|
||||
println("Trade failed to send")
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
private fun configureNodeParams(role: IRSDemoRole, args: DemoArgs, options: OptionSet): NodeParams {
|
||||
val nodeParams = when (role) {
|
||||
IRSDemoRole.NodeA -> createNodeAParams()
|
||||
IRSDemoRole.NodeB -> createNodeBParams()
|
||||
else -> {
|
||||
throw IllegalArgumentException()
|
||||
}
|
||||
}
|
||||
|
||||
val node = logElapsedTime("Node startup") { Node(dir, myNetAddr, config, networkMapId,
|
||||
advertisedServices, DemoClock(),
|
||||
listOf(InterestRateSwapAPI::class.java)).start() }
|
||||
|
||||
// TODO: This should all be replaced by the identity service being updated
|
||||
// as the network map changes.
|
||||
val hostAndPortStrings = options.valuesOf(fakeTradeWithAddr)
|
||||
val identityFiles = options.valuesOf(fakeTradeWithIdentityFile)
|
||||
if (hostAndPortStrings.size != identityFiles.size) {
|
||||
throw IllegalArgumentException("Different number of peer addresses (${hostAndPortStrings.size}) and identities (${identityFiles.size})")
|
||||
nodeParams.mapAddress = options.valueOf(args.networkMapNetAddr)
|
||||
if (options.has(args.dirArg)) {
|
||||
nodeParams.dir = Paths.get(options.valueOf(args.dirArg))
|
||||
}
|
||||
for ((hostAndPortString, identityFile) in hostAndPortStrings.zip(identityFiles)) {
|
||||
try {
|
||||
val peerId = nodeInfo(hostAndPortString, identityFile)
|
||||
node.services.identityService.registerIdentity(peerId.identity)
|
||||
} catch (e: Exception) {
|
||||
println("Could not load peer identity file \"$identityFile\".")
|
||||
}
|
||||
if (options.has(args.networkAddressArg)) {
|
||||
nodeParams.address = options.valueOf(args.networkAddressArg)
|
||||
}
|
||||
nodeParams.identityFile = if (options.has(args.networkMapIdentityFile)) {
|
||||
Paths.get(options.valueOf(args.networkMapIdentityFile))
|
||||
} else {
|
||||
nodeParams.dir.resolve(AbstractNode.PUBLIC_IDENTITY_FILE_NAME)
|
||||
}
|
||||
if (options.has(args.fakeTradeWithIdentityFile)) {
|
||||
nodeParams.tradeWithIdentities = options.valuesOf(args.fakeTradeWithIdentityFile).map { Paths.get(it) }
|
||||
}
|
||||
if (options.has(args.fakeTradeWithAddr)) {
|
||||
nodeParams.tradeWithAddrs = options.valuesOf(args.fakeTradeWithAddr)
|
||||
}
|
||||
|
||||
return nodeParams
|
||||
}
|
||||
|
||||
private fun runNode(nodeParams : NodeParams) : Unit {
|
||||
val node = startNode(nodeParams)
|
||||
// Register handlers for the demo
|
||||
AutoOfferProtocol.Handler.register(node)
|
||||
UpdateBusinessDayProtocol.Handler.register(node)
|
||||
ExitServerProtocol.Handler.register(node)
|
||||
|
||||
if(nodeParams.uploadRates) {
|
||||
runUploadRates()
|
||||
}
|
||||
|
||||
try {
|
||||
while (true) Thread.sleep(Long.MAX_VALUE)
|
||||
} catch(e: InterruptedException) {
|
||||
node.stop()
|
||||
}
|
||||
exitProcess(0)
|
||||
}
|
||||
|
||||
fun nodeInfo(hostAndPortString: String, identityFile: String, advertisedServices: Set<ServiceType> = emptySet()): NodeInfo {
|
||||
try {
|
||||
val addr = HostAndPort.fromString(hostAndPortString).withDefaultPort(Node.DEFAULT_PORT)
|
||||
val path = Paths.get(identityFile)
|
||||
val party = Files.readAllBytes(path).deserialize<Party>()
|
||||
return NodeInfo(ArtemisMessagingService.makeRecipient(addr), party, advertisedServices)
|
||||
} catch (e: Exception) {
|
||||
println("Could not find identify file $identityFile. If the file has just been created as part of starting the demo, please restart this node")
|
||||
throw e
|
||||
private fun runUploadRates() {
|
||||
val fileContents = IOUtils.toString(NodeParams::class.java.getResource("example.rates.txt"))
|
||||
var timer : Timer? = null
|
||||
timer = fixedRateTimer("upload-rates", false, 0, 5000, {
|
||||
try {
|
||||
val url = URL("http://localhost:31341/upload/interest-rates")
|
||||
if(uploadFile(url, fileContents)) {
|
||||
timer!!.cancel()
|
||||
println("Rates uploaded successfully")
|
||||
} else {
|
||||
print("Could not upload rates. Retrying in 5 seconds. ")
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
println("Could not upload rates due to exception. Retrying in 5 seconds")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Todo: Use a simpler library function for this and handle timeout exceptions
|
||||
private fun sendJson(url: URL, data: String, method: String) : Boolean {
|
||||
val connection = url.openConnection() as HttpURLConnection
|
||||
connection.doOutput = true
|
||||
connection.useCaches = false
|
||||
connection.requestMethod = method
|
||||
connection.connectTimeout = 5000
|
||||
connection.readTimeout = 5000
|
||||
connection.setRequestProperty("Connection", "Keep-Alive")
|
||||
connection.setRequestProperty("Cache-Control", "no-cache")
|
||||
connection.setRequestProperty("Content-Type", "application/json")
|
||||
connection.setRequestProperty("Content-Length", data.length.toString())
|
||||
val outStream = DataOutputStream(connection.outputStream)
|
||||
outStream.writeBytes(data)
|
||||
outStream.close()
|
||||
|
||||
return when(connection.responseCode) {
|
||||
200 -> true
|
||||
201 -> true
|
||||
else -> {
|
||||
println("Failed to " + method + " data. Status Code: " + connection.responseCode + ". Mesage: " + connection.responseMessage)
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
private fun putJson(url: URL, data: String) : Boolean {
|
||||
return sendJson(url, data, "PUT")
|
||||
}
|
||||
|
||||
private fun postJson(url: URL, data: String) : Boolean {
|
||||
return sendJson(url, data, "POST")
|
||||
}
|
||||
|
||||
// Todo: Use a simpler library function for this and handle timeout exceptions
|
||||
private fun uploadFile(url: URL, file: String) : Boolean {
|
||||
val boundary = "===" + System.currentTimeMillis() + "==="
|
||||
val hyphens = "--"
|
||||
val clrf = "\r\n"
|
||||
|
||||
val connection = url.openConnection() as HttpURLConnection
|
||||
connection.doOutput = true
|
||||
connection.doInput = true
|
||||
connection.useCaches = false
|
||||
connection.requestMethod = "POST"
|
||||
connection.connectTimeout = 5000
|
||||
connection.readTimeout = 5000
|
||||
connection.setRequestProperty("Connection", "Keep-Alive")
|
||||
connection.setRequestProperty("Cache-Control", "no-cache")
|
||||
connection.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary)
|
||||
|
||||
val request = DataOutputStream(connection.outputStream)
|
||||
request.writeBytes(hyphens + boundary + clrf)
|
||||
request.writeBytes("Content-Disposition: form-data; name=\"rates\" filename=\"example.rates.txt\"" + clrf)
|
||||
request.writeBytes(clrf)
|
||||
request.writeBytes(file)
|
||||
request.writeBytes(clrf)
|
||||
request.writeBytes(hyphens + boundary + hyphens + clrf)
|
||||
|
||||
if (connection.responseCode == 200) {
|
||||
return true
|
||||
} else {
|
||||
println("Could not upload file. Status Code: " + connection + ". Mesage: " + connection.responseMessage)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
private fun createNodeAParams() : NodeParams {
|
||||
val params = NodeParams()
|
||||
params.id = 0
|
||||
params.dir = Paths.get("nodeA")
|
||||
params.address = "localhost"
|
||||
params.tradeWithAddrs = listOf("localhost:31340")
|
||||
params.tradeWithIdentities = listOf(getRoleDir(IRSDemoRole.NodeB).resolve(AbstractNode.PUBLIC_IDENTITY_FILE_NAME))
|
||||
params.defaultLegalName = "Bank A"
|
||||
return params
|
||||
}
|
||||
|
||||
private fun createNodeBParams() : NodeParams {
|
||||
val params = NodeParams()
|
||||
params.id = 1
|
||||
params.dir = Paths.get("nodeB")
|
||||
params.address = "localhost:31340"
|
||||
params.tradeWithAddrs = listOf("localhost")
|
||||
params.tradeWithIdentities = listOf(getRoleDir(IRSDemoRole.NodeA).resolve(AbstractNode.PUBLIC_IDENTITY_FILE_NAME))
|
||||
params.defaultLegalName = "Bank B"
|
||||
params.uploadRates = true
|
||||
return params
|
||||
}
|
||||
|
||||
private fun createNodeConfig(params: NodeParams) : NodeConfiguration {
|
||||
if (!Files.exists(params.dir)) {
|
||||
Files.createDirectory(params.dir)
|
||||
}
|
||||
|
||||
val defaultLegalName = "Global MegaCorp, Ltd."
|
||||
|
||||
if (!Files.exists(configFile)) {
|
||||
createDefaultConfigFile(configFile, defaultLegalName)
|
||||
askAdminToEditConfig(configFile)
|
||||
val configFile = params.dir.resolve("config").toFile()
|
||||
val config = loadConfigFile(configFile, params.defaultLegalName)
|
||||
if(!Files.exists(params.dir.resolve(AbstractNode.PUBLIC_IDENTITY_FILE_NAME))) {
|
||||
createIdentities(params, config)
|
||||
}
|
||||
|
||||
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.
|
||||
private fun getNodeConfig(params: NodeParams): NodeConfiguration {
|
||||
if(!Files.exists(params.dir)) {
|
||||
throw NotSetupException("Missing config directory. Please run node setup before running the node")
|
||||
}
|
||||
|
||||
myLegalName = $defaultLegalName
|
||||
if(!Files.exists(params.dir.resolve(AbstractNode.PUBLIC_IDENTITY_FILE_NAME))) {
|
||||
throw NotSetupException("Missing identity file. Please run node setup before running the node")
|
||||
}
|
||||
|
||||
val configFile = params.dir.resolve("config").toFile()
|
||||
return loadConfigFile(configFile, params.defaultLegalName)
|
||||
}
|
||||
|
||||
private fun startNode(params : NodeParams) : Node {
|
||||
val config = getNodeConfig(params)
|
||||
val advertisedServices: Set<ServiceType>
|
||||
val myNetAddr = HostAndPort.fromString(params.address).withDefaultPort(Node.DEFAULT_PORT)
|
||||
val networkMapId = if (params.mapAddress.equals(params.address)) {
|
||||
// This node provides network map and notary services
|
||||
advertisedServices = setOf(NetworkMapService.Type, SimpleNotaryService.Type)
|
||||
null
|
||||
} else {
|
||||
advertisedServices = setOf(NodeInterestRates.Type)
|
||||
nodeInfo(params.mapAddress, params.identityFile, setOf(NetworkMapService.Type, SimpleNotaryService.Type))
|
||||
}
|
||||
|
||||
val node = logElapsedTime("Node startup") { Node(params.dir, myNetAddr, config, networkMapId,
|
||||
advertisedServices, DemoClock(),
|
||||
listOf(InterestRateSwapAPI::class.java)).start() }
|
||||
|
||||
// TODO: This should all be replaced by the identity service being updated
|
||||
// as the network map changes.
|
||||
if (params.tradeWithAddrs.size != params.tradeWithIdentities.size) {
|
||||
throw IllegalArgumentException("Different number of peer addresses (${params.tradeWithAddrs.size}) and identities (${params.tradeWithIdentities.size})")
|
||||
}
|
||||
for ((hostAndPortString, identityFile) in params.tradeWithAddrs.zip(params.tradeWithIdentities)) {
|
||||
val peerId = nodeInfo(hostAndPortString, identityFile)
|
||||
node.services.identityService.registerIdentity(peerId.identity)
|
||||
}
|
||||
|
||||
return node
|
||||
}
|
||||
|
||||
private fun getRoleDir(role: IRSDemoRole) : Path {
|
||||
when(role) {
|
||||
IRSDemoRole.NodeA -> return Paths.get("nodeA")
|
||||
IRSDemoRole.NodeB -> return Paths.get("nodeB")
|
||||
else -> {
|
||||
throw IllegalArgumentException()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun nodeInfo(hostAndPortString: String, identityFile: Path, advertisedServices: Set<ServiceType> = emptySet()): NodeInfo {
|
||||
try {
|
||||
val addr = HostAndPort.fromString(hostAndPortString).withDefaultPort(Node.DEFAULT_PORT)
|
||||
val path = identityFile
|
||||
val party = Files.readAllBytes(path).deserialize<Party>()
|
||||
return NodeInfo(ArtemisMessagingService.makeRecipient(addr), party, advertisedServices)
|
||||
} catch (e: Exception) {
|
||||
println("Could not find identify file $identityFile.")
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
||||
private fun nodeInfo(handle: InMemoryMessagingNetwork.Handle, identityFile: Path, advertisedServices: Set<ServiceType> = emptySet()): NodeInfo {
|
||||
try {
|
||||
val path = identityFile
|
||||
val party = Files.readAllBytes(path).deserialize<Party>()
|
||||
return NodeInfo(handle, party, advertisedServices)
|
||||
} catch (e: Exception) {
|
||||
println("Could not find identify file $identityFile.")
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
||||
private fun loadConfigFile(configFile: File, defaultLegalName: String): NodeConfiguration {
|
||||
if (!configFile.exists()) {
|
||||
createDefaultConfigFile(configFile, defaultLegalName)
|
||||
println("Default config created at $configFile.")
|
||||
}
|
||||
|
||||
val config = ConfigFactory.parseFile(configFile).withFallback(ConfigFactory.load())
|
||||
return NodeConfigurationFromConfig(config)
|
||||
}
|
||||
|
||||
private fun createIdentities(params: NodeParams, nodeConf: NodeConfiguration) {
|
||||
val mockNetwork = MockNetwork(false)
|
||||
val node = MockNetwork.MockNode(params.dir, nodeConf, mockNetwork, null, setOf(NetworkMapService.Type, SimpleNotaryService.Type), params.id, null)
|
||||
node.start()
|
||||
node.stop()
|
||||
}
|
||||
|
||||
private fun createDefaultConfigFile(configFile: File, legalName: String) {
|
||||
configFile.writeBytes(
|
||||
"""
|
||||
myLegalName = $legalName
|
||||
""".trimIndent().toByteArray())
|
||||
}
|
||||
|
||||
private fun printHelp() {
|
||||
println("""
|
||||
Please refer to the documentation that doesn't yet exist 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())
|
||||
}
|
||||
|
@ -3,7 +3,7 @@
|
||||
"fixedRatePayer": "Bank A",
|
||||
"notional": {
|
||||
"quantity": 2500000000,
|
||||
"token": "USD"
|
||||
"token": "EUR"
|
||||
},
|
||||
"paymentFrequency": "SemiAnnual",
|
||||
"effectiveDate": "2016-03-11",
|
||||
@ -28,7 +28,7 @@
|
||||
"floatingRatePayer": "Bank B",
|
||||
"notional": {
|
||||
"quantity": 2500000000,
|
||||
"token": "USD"
|
||||
"token": "EUR"
|
||||
},
|
||||
"paymentFrequency": "Quarterly",
|
||||
"effectiveDate": "2016-03-11",
|
Loading…
x
Reference in New Issue
Block a user