diff --git a/scripts/irs-demo.sh b/scripts/irs-demo.sh index 1402a670c1..d6f3334ca4 100755 --- a/scripts/irs-demo.sh +++ b/scripts/irs-demo.sh @@ -20,9 +20,7 @@ if [[ "$mode" == "nodeA" ]]; then 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 + build/install/r3prototyping/bin/irsdemo --role=NodeA RC=$? done elif [[ "$mode" == "nodeB" ]]; then @@ -37,9 +35,7 @@ elif [[ "$mode" == "nodeB" ]]; then 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 & + build/install/r3prototyping/bin/irsdemo --role=NodeB & 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 diff --git a/src/main/kotlin/com/r3corda/demos/IRSDemo.kt b/src/main/kotlin/com/r3corda/demos/IRSDemo.kt index 6f6ec8b184..67b0d91121 100644 --- a/src/main/kotlin/com/r3corda/demos/IRSDemo.kt +++ b/src/main/kotlin/com/r3corda/demos/IRSDemo.kt @@ -1,24 +1,24 @@ 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.services.transactions.SimpleNotaryService -import com.typesafe.config.ConfigFactory import joptsimple.OptionParser import java.nio.file.Files import java.nio.file.Path @@ -29,17 +29,34 @@ import kotlin.system.exitProcess // // TODO: Please see TBD +enum class IRSDemoRole { + NodeA, + NodeB +} + +class NodeParams() { + public var dir : Path = Paths.get("") + public var address : String = "" + public var mapAddress: String = "" + public var identityFile: Path = Paths.get("") + public var tradeWithAddrs: List = listOf() + public var tradeWithIdentities: List = listOf() +} + fun main(args: Array) { 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() + val roleArg = parser.accepts("role").withRequiredArg().ofType(IRSDemoRole::class.java).required() + + val networkAddressArg = parser.accepts("network-address").withOptionalArg() + val dirArg = parser.accepts("directory").withOptionalArg() + + val networkMapIdentityFile = parser.accepts("network-map-identity-file").withOptionalArg() + val networkMapNetAddr = parser.accepts("network-map-address").withRequiredArg().defaultsTo("localhost") // 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 fakeTradeWithAddr = parser.accepts("fake-trade-with-address").withOptionalArg() + val fakeTradeWithIdentityFile = parser.accepts("fake-trade-with-identity-file").withOptionalArg() val options = try { parser.parse(*args) @@ -52,50 +69,37 @@ fun main(args: Array) { // 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") - - if (!Files.exists(dir)) { - Files.createDirectory(dir) + val role = options.valueOf(roleArg)!! + val nodeParams = when(role) { + IRSDemoRole.NodeA -> createNodeAParams() + IRSDemoRole.NodeB -> createNodeBParams() } - val config = loadConfigFile(configFile) - val advertisedServices: Set - 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 + nodeParams.mapAddress = options.valueOf(networkMapNetAddr) + if(options.has(dirArg)) { + nodeParams.dir = Paths.get(options.valueOf(dirArg)) + } + if(options.has(networkAddressArg)) { + nodeParams.address = options.valueOf(networkAddressArg) + } + nodeParams.identityFile = if(options.has(networkMapIdentityFile)) { + Paths.get(options.valueOf(networkMapIdentityFile)) } else { - advertisedServices = setOf(NodeInterestRates.Type) - try { - nodeInfo(options.valueOf(networkMapNetAddr), options.valueOf(networkMapIdentityFile), setOf(NetworkMapService.Type, SimpleNotaryService.Type)) - } catch (e: Exception) { - null - } + nodeParams.dir.resolve("identity-public") + } + if(options.has(fakeTradeWithIdentityFile)) { + nodeParams.tradeWithIdentities = options.valuesOf(fakeTradeWithIdentityFile).map { Paths.get(it) } + } + if(options.has(fakeTradeWithAddr)) { + nodeParams.tradeWithAddrs = options.valuesOf(fakeTradeWithAddr) } - 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})") - } - 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\".") - } - } + runNode(nodeParams) + exitProcess(0) +} +fun runNode(nodeParams : NodeParams) : Unit { + val node = startNode(nodeParams) // Register handlers for the demo AutoOfferProtocol.Handler.register(node) UpdateBusinessDayProtocol.Handler.register(node) @@ -106,13 +110,84 @@ fun main(args: Array) { } catch(e: InterruptedException) { node.stop() } - exitProcess(0) } -fun nodeInfo(hostAndPortString: String, identityFile: String, advertisedServices: Set = emptySet()): NodeInfo { +fun createNodeAParams() : NodeParams { + val params = NodeParams() + params.dir = Paths.get("nodeA") + params.address = "localhost" + params.tradeWithAddrs = listOf("localhost:31340") + params.tradeWithIdentities = listOf(getRoleDir(IRSDemoRole.NodeB).resolve("identity-public")) + return params +} + +fun createNodeBParams() : NodeParams { + val params = NodeParams() + params.dir = Paths.get("nodeB") + params.address = "localhost:31340" + params.tradeWithAddrs = listOf("localhost") + params.tradeWithIdentities = listOf(getRoleDir(IRSDemoRole.NodeA).resolve("identity-public")) + return params +} + +fun startNode(params : NodeParams) : Node { + if (!Files.exists(params.dir)) { + Files.createDirectory(params.dir) + } + + val configFile = params.dir.resolve("config") + val config = loadConfigFile(configFile) + val advertisedServices: Set + 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, NotaryService.Type) + null + } else { + advertisedServices = setOf(NodeInterestRates.Type) + + try { + nodeInfo(params.mapAddress, params.identityFile, setOf(NetworkMapService.Type, SimpleNotaryService.Type)) + } catch (e: Exception) { + null + } + } + + 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)) { + try { + val peerId = nodeInfo(hostAndPortString, identityFile) + node.services.identityService.registerIdentity(peerId.identity) + } catch (e: Exception) { + println("Could not load peer identity file \"$identityFile\".") + } + } + + return node +} + +fun getRoleDir(role: IRSDemoRole) : Path { + when(role) { + IRSDemoRole.NodeA -> return Paths.get("nodeA") + IRSDemoRole.NodeB -> return Paths.get("nodeB") + else -> { + return Paths.get("nodedata") + } + } +} + +fun nodeInfo(hostAndPortString: String, identityFile: Path, advertisedServices: Set = emptySet()): NodeInfo { try { val addr = HostAndPort.fromString(hostAndPortString).withDefaultPort(Node.DEFAULT_PORT) - val path = Paths.get(identityFile) + val path = identityFile val party = Files.readAllBytes(path).deserialize() return NodeInfo(ArtemisMessagingService.makeRecipient(addr), party, advertisedServices) } catch (e: Exception) {