Removed extraAdvertisedServiceIds config

The remaining use for it was the finance CorDapp for permissioning CCY issuers. Instead this is now taken from a custom config in node.conf.
This commit is contained in:
Shams Asari
2017-10-09 18:57:09 +01:00
parent 34dbbe626b
commit bd53a22efa
70 changed files with 441 additions and 906 deletions

1
.idea/compiler.xml generated
View File

@ -43,6 +43,7 @@
<module name="explorer-capsule_test" target="1.6" /> <module name="explorer-capsule_test" target="1.6" />
<module name="explorer_main" target="1.8" /> <module name="explorer_main" target="1.8" />
<module name="explorer_test" target="1.8" /> <module name="explorer_test" target="1.8" />
<module name="finance_integrationTest" target="1.8" />
<module name="finance_main" target="1.8" /> <module name="finance_main" target="1.8" />
<module name="finance_test" target="1.8" /> <module name="finance_test" target="1.8" />
<module name="gradle-plugins-cordform-common_main" target="1.8" /> <module name="gradle-plugins-cordform-common_main" target="1.8" />

View File

@ -235,13 +235,11 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) {
node { node {
name "O=Controller,OU=corda,L=London,C=GB" name "O=Controller,OU=corda,L=London,C=GB"
notary = [validating : true] notary = [validating : true]
advertisedServices = []
p2pPort 10002 p2pPort 10002
cordapps = [] cordapps = []
} }
node { node {
name "O=Bank A,OU=corda,L=London,C=GB" name "O=Bank A,OU=corda,L=London,C=GB"
advertisedServices = []
p2pPort 10012 p2pPort 10012
rpcPort 10013 rpcPort 10013
webPort 10014 webPort 10014
@ -249,7 +247,6 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) {
} }
node { node {
name "O=Bank B,OU=corda,L=London,C=GB" name "O=Bank B,OU=corda,L=London,C=GB"
advertisedServices = []
p2pPort 10007 p2pPort 10007
rpcPort 10008 rpcPort 10008
webPort 10009 webPort 10009

View File

@ -4,7 +4,6 @@ trustStorePassword : "trustpass"
p2pAddress : "localhost:10002" p2pAddress : "localhost:10002"
rpcAddress : "localhost:10003" rpcAddress : "localhost:10003"
webAddress : "localhost:10004" webAddress : "localhost:10004"
extraAdvertisedServiceIds : [ "corda.interest_rates" ]
networkMapService : { networkMapService : {
address : "localhost:10000" address : "localhost:10000"
legalName : "O=Network Map Service,OU=corda,L=London,C=GB" legalName : "O=Network Map Service,OU=corda,L=London,C=GB"

View File

@ -4,7 +4,6 @@ trustStorePassword : "trustpass"
p2pAddress : "localhost:10005" p2pAddress : "localhost:10005"
rpcAddress : "localhost:10006" rpcAddress : "localhost:10006"
webAddress : "localhost:10007" webAddress : "localhost:10007"
extraAdvertisedServiceIds : [ "corda.interest_rates" ]
networkMapService : { networkMapService : {
address : "localhost:10000" address : "localhost:10000"
legalName : "O=Network Map Service,OU=corda,L=London,C=GB" legalName : "O=Network Map Service,OU=corda,L=London,C=GB"

View File

@ -1,4 +1,4 @@
gradlePluginsVersion=2.0.0 gradlePluginsVersion=2.0.1
kotlinVersion=1.1.50 kotlinVersion=1.1.50
guavaVersion=21.0 guavaVersion=21.0
bouncycastleVersion=1.57 bouncycastleVersion=1.57

View File

@ -118,10 +118,9 @@ class AttachmentTests {
// Make a node that doesn't do sanity checking at load time. // Make a node that doesn't do sanity checking at load time.
val aliceNode = mockNet.createNotaryNode(legalName = ALICE.name, nodeFactory = object : MockNetwork.Factory<MockNetwork.MockNode> { val aliceNode = mockNet.createNotaryNode(legalName = ALICE.name, nodeFactory = object : MockNetwork.Factory<MockNetwork.MockNode> {
override fun create(config: NodeConfiguration, network: MockNetwork, networkMapAddr: SingleMessageRecipient?, override fun create(config: NodeConfiguration, network: MockNetwork, networkMapAddr: SingleMessageRecipient?,
advertisedServices: Set<ServiceInfo>, id: Int, id: Int, notaryIdentity: Pair<ServiceInfo, KeyPair>?,
notaryIdentity: Pair<ServiceInfo, KeyPair>?,
entropyRoot: BigInteger): MockNetwork.MockNode { entropyRoot: BigInteger): MockNetwork.MockNode {
return object : MockNetwork.MockNode(config, network, networkMapAddr, advertisedServices, id, notaryIdentity, entropyRoot) { return object : MockNetwork.MockNode(config, network, networkMapAddr, id, notaryIdentity, entropyRoot) {
override fun start() = super.start().apply { attachments.checkAttachmentsOnLoad = false } override fun start() = super.start().apply { attachments.checkAttachmentsOnLoad = false }
} }
} }

View File

@ -162,9 +162,8 @@ class AttachmentSerializationTest {
client.dispose() client.dispose()
client = mockNet.createNode(client.internals.id, object : MockNetwork.Factory<MockNetwork.MockNode> { client = mockNet.createNode(client.internals.id, object : MockNetwork.Factory<MockNetwork.MockNode> {
override fun create(config: NodeConfiguration, network: MockNetwork, networkMapAddr: SingleMessageRecipient?, override fun create(config: NodeConfiguration, network: MockNetwork, networkMapAddr: SingleMessageRecipient?,
advertisedServices: Set<ServiceInfo>, id: Int, notaryIdentity: Pair<ServiceInfo, KeyPair>?, id: Int, notaryIdentity: Pair<ServiceInfo, KeyPair>?, entropyRoot: BigInteger): MockNetwork.MockNode {
entropyRoot: BigInteger): MockNetwork.MockNode { return object : MockNetwork.MockNode(config, network, networkMapAddr, id, notaryIdentity, entropyRoot) {
return object : MockNetwork.MockNode(config, network, networkMapAddr, advertisedServices, id, notaryIdentity, entropyRoot) {
override fun start() = super.start().apply { attachments.checkAttachmentsOnLoad = checkAttachmentsOnLoad } override fun start() = super.start().apply { attachments.checkAttachmentsOnLoad = checkAttachmentsOnLoad }
} }
} }

View File

@ -21,7 +21,6 @@ notary/network map node:
node { node {
name "O=Controller,OU=corda,L=London,C=UK" name "O=Controller,OU=corda,L=London,C=UK"
notary = [validating : true] notary = [validating : true]
advertisedServices = []
p2pPort 10002 p2pPort 10002
rpcPort 10003 rpcPort 10003
webPort 10004 webPort 10004
@ -29,7 +28,6 @@ notary/network map node:
} }
node { node {
name "CN=NodeA,O=NodeA,L=London,C=UK" name "CN=NodeA,O=NodeA,L=London,C=UK"
advertisedServices = []
p2pPort 10005 p2pPort 10005
rpcPort 10006 rpcPort 10006
webPort 10007 webPort 10007
@ -38,7 +36,6 @@ notary/network map node:
} }
node { node {
name "CN=NodeB,O=NodeB,L=New York,C=US" name "CN=NodeB,O=NodeB,L=New York,C=US"
advertisedServices = []
p2pPort 10008 p2pPort 10008
rpcPort 10009 rpcPort 10009
webPort 10010 webPort 10010
@ -47,7 +44,6 @@ notary/network map node:
} }
node { node {
name "CN=NodeC,O=NodeC,L=Paris,C=FR" name "CN=NodeC,O=NodeC,L=Paris,C=FR"
advertisedServices = []
p2pPort 10011 p2pPort 10011
rpcPort 10012 rpcPort 10012
webPort 10013 webPort 10013

View File

@ -77,7 +77,6 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) {
node { node {
name "O=Notary Service,OU=corda,L=London,C=GB" name "O=Notary Service,OU=corda,L=London,C=GB"
notary = [validating : true] notary = [validating : true]
advertisedServices = []
p2pPort 10002 p2pPort 10002
rpcPort 10003 rpcPort 10003
webPort 10004 webPort 10004
@ -85,7 +84,6 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) {
} }
node { node {
name "O=Alice Corp,L=London,C=GB" name "O=Alice Corp,L=London,C=GB"
advertisedServices = []
p2pPort 10005 p2pPort 10005
rpcPort 10006 rpcPort 10006
webPort 10007 webPort 10007

View File

@ -4,5 +4,4 @@ trustStorePassword : "trustpass"
p2pAddress : "my-network-map:10000" p2pAddress : "my-network-map:10000"
webAddress : "localhost:10001" webAddress : "localhost:10001"
sshdAddress : "localhost:10002" sshdAddress : "localhost:10002"
extraAdvertisedServiceIds : []
useHTTPS : false useHTTPS : false

View File

@ -10,7 +10,6 @@ dataSourceProperties : {
p2pAddress : "my-corda-node:10002" p2pAddress : "my-corda-node:10002"
rpcAddress : "my-corda-node:10003" rpcAddress : "my-corda-node:10003"
webAddress : "localhost:10004" webAddress : "localhost:10004"
extraAdvertisedServiceIds : [ "corda.interest_rates" ]
networkMapService : { networkMapService : {
address : "my-network-map:10000" address : "my-network-map:10000"
legalName : "O=Network Map Service,OU=corda,L=London,C=GB" legalName : "O=Network Map Service,OU=corda,L=London,C=GB"

View File

@ -29,14 +29,12 @@ Let's take a look at the nodes we're going to deploy. Open the project's ``build
node { node {
name "O=Controller,L=London,C=GB" name "O=Controller,L=London,C=GB"
notary = [validating : true] notary = [validating : true]
advertisedServices = []
p2pPort 10002 p2pPort 10002
rpcPort 10003 rpcPort 10003
cordapps = ["net.corda:corda-finance:$corda_release_version"] cordapps = ["net.corda:corda-finance:$corda_release_version"]
} }
node { node {
name "O=PartyA,L=London,C=GB" name "O=PartyA,L=London,C=GB"
advertisedServices = []
p2pPort 10005 p2pPort 10005
rpcPort 10006 rpcPort 10006
webPort 10007 webPort 10007
@ -45,7 +43,6 @@ Let's take a look at the nodes we're going to deploy. Open the project's ``build
} }
node { node {
name "O=PartyB,L=New York,C=US" name "O=PartyB,L=New York,C=US"
advertisedServices = []
p2pPort 10008 p2pPort 10008
rpcPort 10009 rpcPort 10009
webPort 10010 webPort 10010

View File

@ -295,12 +295,8 @@ NotaryService (SimpleNotaryService, ValidatingNotaryService, RaftValidatingNotar
The ``NotaryService`` is an abstract base class for the various concrete The ``NotaryService`` is an abstract base class for the various concrete
implementations of the Notary server flow. By default, a node does implementations of the Notary server flow. By default, a node does
not run any ``NotaryService`` server component. However, the appropriate not run any ``NotaryService`` server component. For that you need to specify the ``notary`` config.
implementation service is automatically started if the relevant The node may then participate in controlling state uniqueness when contacted by nodes
``ServiceType`` id is included in the node's
``extraAdvertisedServiceIds`` configuration property. The node will then
advertise itself as a Notary via the ``NetworkMapService`` and may then
participate in controlling state uniqueness when contacted by nodes
using the ``NotaryFlow.Client`` ``subFlow``. The using the ``NotaryFlow.Client`` ``subFlow``. The
``SimpleNotaryService`` only offers protection against double spend, but ``SimpleNotaryService`` only offers protection against double spend, but
does no further verification. The ``ValidatingNotaryService`` checks does no further verification. The ``ValidatingNotaryService`` checks

View File

@ -10,12 +10,25 @@ apply plugin: 'com.jfrog.artifactory'
description 'Corda finance modules' description 'Corda finance modules'
sourceSets {
integrationTest {
kotlin {
compileClasspath += main.output + test.output
runtimeClasspath += main.output + test.output
srcDir file('src/integration-test/kotlin')
}
}
}
dependencies { dependencies {
// Note the :finance module is a CorDapp in its own right // Note the :finance module is a CorDapp in its own right
// and CorDapps using :finance features should use 'cordapp' not 'compile' linkage. // and CorDapps using :finance features should use 'cordapp' not 'compile' linkage.
cordaCompile project(':core') cordaCompile project(':core')
cordaCompile project(':confidential-identities') cordaCompile project(':confidential-identities')
// TODO Remove this once we have app configs
compile "com.typesafe:config:$typesafe_config_version"
testCompile project(':test-utils') testCompile project(':test-utils')
testCompile project(path: ':core', configuration: 'testArtifacts') testCompile project(path: ':core', configuration: 'testArtifacts')
testCompile "junit:junit:$junit_version" testCompile "junit:junit:$junit_version"
@ -23,6 +36,8 @@ dependencies {
configurations { configurations {
testArtifacts.extendsFrom testRuntime testArtifacts.extendsFrom testRuntime
integrationTestCompile.extendsFrom testCompile
integrationTestRuntime.extendsFrom testRuntime
} }
task testJar(type: Jar) { task testJar(type: Jar) {
@ -30,6 +45,11 @@ task testJar(type: Jar) {
from sourceSets.test.output from sourceSets.test.output
} }
task integrationTest(type: Test, dependsOn: []) {
testClassesDirs = sourceSets.integrationTest.output.classesDirs
classpath = sourceSets.integrationTest.runtimeClasspath
}
artifacts { artifacts {
testArtifacts testJar testArtifacts testJar
} }

View File

@ -0,0 +1,20 @@
package net.corda.finance.flows
import net.corda.core.messaging.startFlow
import net.corda.core.utilities.getOrThrow
import net.corda.finance.EUR
import net.corda.finance.USD
import net.corda.testing.driver.driver
import org.assertj.core.api.Assertions.assertThat
import org.junit.Test
class CashConfigDataFlowTest {
@Test
fun `issuable currencies are read in from node config`() {
driver {
val node = startNode(customOverrides = mapOf("issuableCurrencies" to listOf("EUR", "USD"))).getOrThrow()
val config = node.rpc.startFlow(::CashConfigDataFlow).returnValue.getOrThrow()
assertThat(config.issuableCurrencies).containsExactly(EUR, USD)
}
}
}

View File

@ -1,38 +1,59 @@
package net.corda.finance.flows package net.corda.finance.flows
import co.paralleluniverse.fibers.Suspendable import co.paralleluniverse.fibers.Suspendable
import net.corda.core.flows.FlowException import com.typesafe.config.ConfigFactory
import net.corda.core.flows.FlowLogic import net.corda.core.flows.FlowLogic
import net.corda.core.flows.StartableByRPC import net.corda.core.flows.StartableByRPC
import net.corda.core.internal.declaredField
import net.corda.core.internal.div
import net.corda.core.internal.read
import net.corda.core.node.AppServiceHub
import net.corda.core.node.services.CordaService
import net.corda.core.serialization.CordaSerializable import net.corda.core.serialization.CordaSerializable
import net.corda.core.serialization.SingletonSerializeAsToken
import net.corda.finance.CHF import net.corda.finance.CHF
import net.corda.finance.EUR import net.corda.finance.EUR
import net.corda.finance.GBP import net.corda.finance.GBP
import net.corda.finance.USD import net.corda.finance.USD
import net.corda.finance.flows.ConfigHolder.Companion.supportedCurrencies
import java.nio.file.Path
import java.util.* import java.util.*
// TODO Until apps have access to their own config, we'll hack things by first getting the baseDirectory, read the node.conf
// again to get our config and store it here for access by our flow
@CordaService
class ConfigHolder(services: AppServiceHub) : SingletonSerializeAsToken() {
companion object {
val supportedCurrencies = listOf(USD, GBP, CHF, EUR)
}
val issuableCurrencies: List<Currency>
init {
// Warning!! You are about to see a major hack!
val baseDirectory = services.declaredField<Any>("serviceHub").value
.let { it.javaClass.getMethod("getConfiguration").apply { isAccessible = true }.invoke(it) }
.declaredField<Path>("baseDirectory").value
val config = (baseDirectory / "node.conf").read { ConfigFactory.parseReader(it.reader()) }
if (config.hasPath("issuableCurrencies")) {
issuableCurrencies = config.getStringList("issuableCurrencies").map { Currency.getInstance(it) }
require(supportedCurrencies.containsAll(issuableCurrencies))
} else {
issuableCurrencies = emptyList()
}
}
}
/** /**
* Flow to obtain cash cordapp app configuration. * Flow to obtain cash cordapp app configuration.
*/ */
@StartableByRPC @StartableByRPC
class CashConfigDataFlow : FlowLogic<CashConfiguration>() { class CashConfigDataFlow : FlowLogic<CashConfiguration>() {
companion object {
private val supportedCurrencies = listOf(USD, GBP, CHF, EUR)
}
@Suspendable @Suspendable
override fun call(): CashConfiguration { override fun call(): CashConfiguration {
val issuableCurrencies = supportedCurrencies.mapNotNull { val configHolder = serviceHub.cordaService(ConfigHolder::class.java)
try { return CashConfiguration(configHolder.issuableCurrencies, supportedCurrencies)
// Currently it uses checkFlowPermission to determine the list of issuable currency as a temporary hack.
// TODO: get the config from proper configuration source.
checkFlowPermission("corda.issuer.$it", emptyMap())
it
} catch (e: FlowException) {
null
}
}
return CashConfiguration(issuableCurrencies, supportedCurrencies)
} }
} }

View File

@ -1,22 +0,0 @@
package net.corda.finance.flows;
import net.corda.core.flows.AbstractStateReplacementFlow;
import net.corda.core.flows.FlowSession;
import net.corda.core.transactions.SignedTransaction;
import net.corda.core.utilities.ProgressTracker;
import org.jetbrains.annotations.NotNull;
@SuppressWarnings("unused")
public class AbstractStateReplacementFlowTest {
// Acceptor used to have a type parameter of Unit which prevented Java code from subclassing it (https://youtrack.jetbrains.com/issue/KT-15964).
private static class TestAcceptorCanBeInheritedInJava extends AbstractStateReplacementFlow.Acceptor {
public TestAcceptorCanBeInheritedInJava(@NotNull FlowSession otherSideSession, @NotNull ProgressTracker progressTracker) {
super(otherSideSession, progressTracker);
}
@Override
protected void verifyProposal(@NotNull SignedTransaction stx, @NotNull AbstractStateReplacementFlow.Proposal proposal) {
}
}
}

View File

@ -23,11 +23,6 @@ public class CordformNode implements NodeDefinition {
return name; return name;
} }
/**
* A list of advertised services ID strings.
*/
public List<String> advertisedServices = emptyList();
/** /**
* Set the RPC users for this node. This configuration block allows arbitrary configuration. * Set the RPC users for this node. This configuration block allows arbitrary configuration.
* The recommended current structure is: * The recommended current structure is:
@ -44,6 +39,8 @@ public class CordformNode implements NodeDefinition {
*/ */
public Map<String, Object> notary = null; public Map<String, Object> notary = null;
public Map<String, Object> extraConfig = null;
protected Config config = ConfigFactory.empty(); protected Config config = ConfigFactory.empty();
public Config getConfig() { public Config getConfig() {

View File

@ -123,7 +123,9 @@ class Node extends CordformNode {
if (notary) { if (notary) {
config = config.withValue("notary", ConfigValueFactory.fromMap(notary)) config = config.withValue("notary", ConfigValueFactory.fromMap(notary))
} }
config = config.withValue('extraAdvertisedServiceIds', ConfigValueFactory.fromIterable(advertisedServices*.toString())) if (extraConfig) {
config = config.withFallback(ConfigFactory.parseMap(extraConfig))
}
} }
/** /**

View File

@ -12,6 +12,7 @@ import net.corda.core.internal.uncheckedCast
import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.NetworkHostAndPort
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import java.lang.reflect.Field import java.lang.reflect.Field
import java.lang.reflect.Modifier.isStatic
import java.lang.reflect.ParameterizedType import java.lang.reflect.ParameterizedType
import java.net.Proxy import java.net.Proxy
import java.net.URL import java.net.URL
@ -142,7 +143,7 @@ fun Any.toConfig(): Config = ConfigValueFactory.fromMap(toConfigMap()).toConfig(
private fun Any.toConfigMap(): Map<String, Any> { private fun Any.toConfigMap(): Map<String, Any> {
val values = HashMap<String, Any>() val values = HashMap<String, Any>()
for (field in javaClass.declaredFields) { for (field in javaClass.declaredFields) {
if (field.isSynthetic) continue if (isStatic(field.modifiers) || field.isSynthetic) continue
field.isAccessible = true field.isAccessible = true
val value = field.get(this) ?: continue val value = field.get(this) ?: continue
val configValue = if (value is String || value is Boolean || value is Number) { val configValue = if (value is String || value is Boolean || value is Number) {

View File

@ -181,6 +181,11 @@ class ConfigParsingTest {
assertThat(OldData("old").toConfig()).isEqualTo(config("newValue" to "old")) assertThat(OldData("old").toConfig()).isEqualTo(config("newValue" to "old"))
} }
@Test
fun `static field`() {
assertThat(DataWithCompanion(3).toConfig()).isEqualTo(config("value" to 3))
}
private inline fun <reified S : SingleData<V>, reified L : ListData<V>, V : Any> testPropertyType( private inline fun <reified S : SingleData<V>, reified L : ListData<V>, V : Any> testPropertyType(
value1: V, value1: V,
value2: V, value2: V,
@ -259,6 +264,12 @@ class ConfigParsingTest {
data class OldData( data class OldData(
@OldConfig("oldValue") @OldConfig("oldValue")
val newValue: String) val newValue: String)
data class DataWithCompanion(val value: Int) {
companion object {
@Suppress("unused")
val companionValue = 2
}
}
enum class TestEnum { Value1, Value2 } enum class TestEnum { Value1, Value2 }
} }

View File

@ -1,20 +1,20 @@
package net.corda.node package net.corda.node
import co.paralleluniverse.fibers.Suspendable import co.paralleluniverse.fibers.Suspendable
import net.corda.core.internal.div
import net.corda.core.flows.FlowLogic import net.corda.core.flows.FlowLogic
import net.corda.core.flows.StartableByRPC import net.corda.core.flows.StartableByRPC
import net.corda.core.internal.div
import net.corda.core.messaging.startFlow import net.corda.core.messaging.startFlow
import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.getOrThrow
import net.corda.testing.ALICE
import net.corda.node.internal.NodeStartup import net.corda.node.internal.NodeStartup
import net.corda.node.services.FlowPermissions.Companion.startFlowPermission import net.corda.node.services.FlowPermissions.Companion.startFlowPermission
import net.corda.nodeapi.User
import net.corda.nodeapi.internal.ServiceInfo import net.corda.nodeapi.internal.ServiceInfo
import net.corda.nodeapi.internal.ServiceType import net.corda.nodeapi.internal.ServiceType
import net.corda.nodeapi.User import net.corda.testing.ALICE
import net.corda.testing.ProjectStructure.projectRootDir
import net.corda.testing.driver.ListenProcessDeathException import net.corda.testing.driver.ListenProcessDeathException
import net.corda.testing.driver.NetworkMapStartStrategy import net.corda.testing.driver.NetworkMapStartStrategy
import net.corda.testing.ProjectStructure.projectRootDir
import net.corda.testing.driver.driver import net.corda.testing.driver.driver
import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThat
import org.assertj.core.api.Assertions.assertThatThrownBy import org.assertj.core.api.Assertions.assertThatThrownBy
@ -60,7 +60,7 @@ class BootTests {
fun `node quits on failure to register with network map`() { fun `node quits on failure to register with network map`() {
val tooManyAdvertisedServices = (1..100).map { ServiceInfo(ServiceType.notary.getSubType("$it")) }.toSet() val tooManyAdvertisedServices = (1..100).map { ServiceInfo(ServiceType.notary.getSubType("$it")) }.toSet()
driver(networkMapStartStrategy = NetworkMapStartStrategy.Nominated(ALICE.name)) { driver(networkMapStartStrategy = NetworkMapStartStrategy.Nominated(ALICE.name)) {
val future = startNode(providedName = ALICE.name, advertisedServices = tooManyAdvertisedServices) val future = startNode(providedName = ALICE.name)
assertFailsWith(ListenProcessDeathException::class) { future.getOrThrow() } assertFailsWith(ListenProcessDeathException::class) { future.getOrThrow() }
} }
} }

View File

@ -70,7 +70,6 @@ import net.corda.node.services.vault.NodeVaultService
import net.corda.node.services.vault.VaultSoftLockManager import net.corda.node.services.vault.VaultSoftLockManager
import net.corda.node.utilities.* import net.corda.node.utilities.*
import net.corda.node.utilities.AddOrRemove.ADD import net.corda.node.utilities.AddOrRemove.ADD
import net.corda.nodeapi.internal.ServiceInfo
import org.apache.activemq.artemis.utils.ReusableLatch import org.apache.activemq.artemis.utils.ReusableLatch
import org.slf4j.Logger import org.slf4j.Logger
import rx.Observable import rx.Observable
@ -101,7 +100,6 @@ import net.corda.core.crypto.generateKeyPair as cryptoGenerateKeyPair
// In theory the NodeInfo for the node should be passed in, instead, however currently this is constructed by the // In theory the NodeInfo for the node should be passed in, instead, however currently this is constructed by the
// AbstractNode. It should be possible to generate the NodeInfo outside of AbstractNode, so it can be passed in. // AbstractNode. It should be possible to generate the NodeInfo outside of AbstractNode, so it can be passed in.
abstract class AbstractNode(config: NodeConfiguration, abstract class AbstractNode(config: NodeConfiguration,
val advertisedServices: Set<ServiceInfo>,
val platformClock: Clock, val platformClock: Clock,
protected val versionInfo: VersionInfo, protected val versionInfo: VersionInfo,
@VisibleForTesting val busyNodeLatch: ReusableLatch = ReusableLatch()) : SingletonSerializeAsToken() { @VisibleForTesting val busyNodeLatch: ReusableLatch = ReusableLatch()) : SingletonSerializeAsToken() {

View File

@ -21,7 +21,6 @@ import net.corda.node.services.RPCUserService
import net.corda.node.services.RPCUserServiceImpl import net.corda.node.services.RPCUserServiceImpl
import net.corda.node.services.api.NetworkMapCacheInternal import net.corda.node.services.api.NetworkMapCacheInternal
import net.corda.node.services.api.SchemaService import net.corda.node.services.api.SchemaService
import net.corda.nodeapi.internal.ServiceInfo
import net.corda.node.services.config.FullNodeConfiguration import net.corda.node.services.config.FullNodeConfiguration
import net.corda.node.services.messaging.ArtemisMessagingServer import net.corda.node.services.messaging.ArtemisMessagingServer
import net.corda.node.services.messaging.ArtemisMessagingServer.Companion.ipDetectRequestProperty import net.corda.node.services.messaging.ArtemisMessagingServer.Companion.ipDetectRequestProperty
@ -60,14 +59,11 @@ import kotlin.system.exitProcess
* loads important data off disk and starts listening for connections. * loads important data off disk and starts listening for connections.
* *
* @param configuration This is typically loaded from a TypeSafe HOCON configuration file. * @param configuration This is typically loaded from a TypeSafe HOCON configuration file.
* @param advertisedServices The services this node advertises. This must be a subset of the services it runs,
* but nodes are not required to advertise services they run (hence subset).
*/ */
open class Node(override val configuration: FullNodeConfiguration, open class Node(override val configuration: FullNodeConfiguration,
advertisedServices: Set<ServiceInfo>,
versionInfo: VersionInfo, versionInfo: VersionInfo,
val initialiseSerialization: Boolean = true val initialiseSerialization: Boolean = true
) : AbstractNode(configuration, advertisedServices, createClock(configuration), versionInfo) { ) : AbstractNode(configuration, createClock(configuration), versionInfo) {
companion object { companion object {
private val logger = loggerFor<Node>() private val logger = loggerFor<Node>()
var renderBasicInfoToConsole = true var renderBasicInfoToConsole = true

View File

@ -12,7 +12,6 @@ import net.corda.node.services.transactions.bftSMaRtSerialFilter
import net.corda.node.shell.InteractiveShell import net.corda.node.shell.InteractiveShell
import net.corda.node.utilities.registration.HTTPNetworkRegistrationService import net.corda.node.utilities.registration.HTTPNetworkRegistrationService
import net.corda.node.utilities.registration.NetworkRegistrationHelper import net.corda.node.utilities.registration.NetworkRegistrationHelper
import net.corda.nodeapi.internal.ServiceInfo
import net.corda.nodeapi.internal.addShutdownHook import net.corda.nodeapi.internal.addShutdownHook
import org.fusesource.jansi.Ansi import org.fusesource.jansi.Ansi
import org.fusesource.jansi.AnsiConsole import org.fusesource.jansi.AnsiConsole
@ -86,13 +85,10 @@ open class NodeStartup(val args: Array<String>) {
open protected fun preNetworkRegistration(conf: FullNodeConfiguration) = Unit open protected fun preNetworkRegistration(conf: FullNodeConfiguration) = Unit
open protected fun createNode(conf: FullNodeConfiguration, versionInfo: VersionInfo, services: Set<ServiceInfo>): Node { open protected fun createNode(conf: FullNodeConfiguration, versionInfo: VersionInfo): Node = Node(conf, versionInfo)
return Node(conf, services, versionInfo)
}
open protected fun startNode(conf: FullNodeConfiguration, versionInfo: VersionInfo, startTime: Long, cmdlineOptions: CmdLineOptions) { open protected fun startNode(conf: FullNodeConfiguration, versionInfo: VersionInfo, startTime: Long, cmdlineOptions: CmdLineOptions) {
val advertisedServices = conf.calculateServices() val node = createNode(conf, versionInfo)
val node = createNode(conf, versionInfo, advertisedServices)
if (cmdlineOptions.justGenerateNodeInfo) { if (cmdlineOptions.justGenerateNodeInfo) {
// Perform the minimum required start-up logic to be able to write a nodeInfo to disk // Perform the minimum required start-up logic to be able to write a nodeInfo to disk
node.generateNodeInfo() node.generateNodeInfo()

View File

@ -7,7 +7,6 @@ import net.corda.node.internal.NetworkMapInfo
import net.corda.node.services.messaging.CertificateChainCheckPolicy import net.corda.node.services.messaging.CertificateChainCheckPolicy
import net.corda.nodeapi.User import net.corda.nodeapi.User
import net.corda.nodeapi.config.NodeSSLConfiguration import net.corda.nodeapi.config.NodeSSLConfiguration
import net.corda.nodeapi.internal.ServiceInfo
import java.net.URL import java.net.URL
import java.nio.file.Path import java.nio.file.Path
import java.util.* import java.util.*
@ -82,7 +81,6 @@ data class FullNodeConfiguration(
// TODO This field is slightly redundant as p2pAddress is sufficient to hold the address of the node's MQ broker. // TODO This field is slightly redundant as p2pAddress 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 // Instead this should be a Boolean indicating whether that broker is an internal one started by the node or an external one
val messagingServerAddress: NetworkHostAndPort?, val messagingServerAddress: NetworkHostAndPort?,
val extraAdvertisedServiceIds: List<String>,
override val notary: NotaryConfig?, override val notary: NotaryConfig?,
override val certificateChainCheckPolicies: List<CertChainPolicyConfig>, override val certificateChainCheckPolicies: List<CertChainPolicyConfig>,
override val devMode: Boolean = false, override val devMode: Boolean = false,
@ -103,13 +101,6 @@ data class FullNodeConfiguration(
require(myLegalName.commonName == null) { "Common name must be null: $myLegalName" } require(myLegalName.commonName == null) { "Common name must be null: $myLegalName" }
require(minimumPlatformVersion >= 1) { "minimumPlatformVersion cannot be less than 1" } require(minimumPlatformVersion >= 1) { "minimumPlatformVersion cannot be less than 1" }
} }
fun calculateServices(): Set<ServiceInfo> {
return extraAdvertisedServiceIds
.filter(String::isNotBlank)
.map { ServiceInfo.parse(it) }
.toSet()
}
} }
enum class VerifierType { enum class VerifierType {

View File

@ -24,7 +24,6 @@ import net.corda.core.utilities.*
import net.corda.node.services.api.FlowAppAuditEvent import net.corda.node.services.api.FlowAppAuditEvent
import net.corda.node.services.api.FlowPermissionAuditEvent import net.corda.node.services.api.FlowPermissionAuditEvent
import net.corda.node.services.api.ServiceHubInternal import net.corda.node.services.api.ServiceHubInternal
import net.corda.node.services.config.FullNodeConfiguration
import net.corda.node.services.statemachine.FlowSessionState.Initiating import net.corda.node.services.statemachine.FlowSessionState.Initiating
import net.corda.node.utilities.CordaPersistence import net.corda.node.utilities.CordaPersistence
import net.corda.node.utilities.DatabaseTransaction import net.corda.node.utilities.DatabaseTransaction
@ -262,10 +261,7 @@ class FlowStateMachineImpl<R>(override val id: StateMachineRunId,
// TODO Dummy implementation of access to application specific permission controls and audit logging // TODO Dummy implementation of access to application specific permission controls and audit logging
override fun checkFlowPermission(permissionName: String, extraAuditData: Map<String, String>) { override fun checkFlowPermission(permissionName: String, extraAuditData: Map<String, String>) {
// This is a hack to allow cash app access list of permitted issuer currency. val permissionGranted = true // TODO define permission control service on ServiceHubInternal and actually check authorization.
// TODO: replace this with cordapp configuration.
val config = serviceHub.configuration as? FullNodeConfiguration
val permissionGranted = config?.extraAdvertisedServiceIds?.contains(permissionName) != false
val checkPermissionEvent = FlowPermissionAuditEvent( val checkPermissionEvent = FlowPermissionAuditEvent(
serviceHub.clock.instant(), serviceHub.clock.instant(),
flowInitiator, flowInitiator,
@ -276,6 +272,7 @@ class FlowStateMachineImpl<R>(override val id: StateMachineRunId,
permissionName, permissionName,
permissionGranted) permissionGranted)
serviceHub.auditService.recordAuditEvent(checkPermissionEvent) serviceHub.auditService.recordAuditEvent(checkPermissionEvent)
@Suppress("ConstantConditionIf")
if (!permissionGranted) { if (!permissionGranted) {
throw FlowPermissionException("User $flowInitiator not permissioned for $permissionName on flow $id") throw FlowPermissionException("User $flowInitiator not permissioned for $permissionName on flow $id")
} }
@ -398,14 +395,8 @@ class FlowStateMachineImpl<R>(override val id: StateMachineRunId,
waitForConfirmation: Boolean, waitForConfirmation: Boolean,
retryable: Boolean = false retryable: Boolean = false
): FlowSessionInternal { ): FlowSessionInternal {
val session = openSessions[Pair(sessionFlow, otherParty)] val session = openSessions[Pair(sessionFlow, otherParty)] ?: throw IllegalStateException("Expected an Uninitiated session for $otherParty")
if (session == null) { val state = session.state as? FlowSessionState.Uninitiated ?: throw IllegalStateException("Tried to initiate a session $session, but it's already initiating/initiated")
throw IllegalStateException("Expected an Uninitiated session for $otherParty")
}
val state = session.state
if (state !is FlowSessionState.Uninitiated) {
throw IllegalStateException("Tried to initiate a session $session, but it's already initiating/initiated")
}
logger.trace { "Initiating a new session with ${state.otherParty}" } logger.trace { "Initiating a new session with ${state.otherParty}" }
session.state = FlowSessionState.Initiating(state.otherParty) session.state = FlowSessionState.Initiating(state.otherParty)
session.retryable = retryable session.retryable = retryable

View File

@ -275,9 +275,8 @@ class TwoPartyTradeFlowTests(val anonymous: Boolean) {
// that Bob was waiting on before the reboot occurred. // that Bob was waiting on before the reboot occurred.
bobNode = mockNet.createNode(bobAddr.id, object : MockNetwork.Factory<MockNetwork.MockNode> { bobNode = mockNet.createNode(bobAddr.id, object : MockNetwork.Factory<MockNetwork.MockNode> {
override fun create(config: NodeConfiguration, network: MockNetwork, networkMapAddr: SingleMessageRecipient?, override fun create(config: NodeConfiguration, network: MockNetwork, networkMapAddr: SingleMessageRecipient?,
advertisedServices: Set<ServiceInfo>, id: Int, notaryIdentity: Pair<ServiceInfo, KeyPair>?, id: Int, notaryIdentity: Pair<ServiceInfo, KeyPair>?, entropyRoot: BigInteger): MockNetwork.MockNode {
entropyRoot: BigInteger): MockNetwork.MockNode { return MockNetwork.MockNode(config, network, networkMapAddr, bobAddr.id, notaryIdentity, entropyRoot)
return MockNetwork.MockNode(config, network, networkMapAddr, advertisedServices, bobAddr.id, notaryIdentity, entropyRoot)
} }
}, BOB.name) }, BOB.name)
@ -312,18 +311,15 @@ class TwoPartyTradeFlowTests(val anonymous: Boolean) {
// Creates a mock node with an overridden storage service that uses a RecordingMap, that lets us test the order // Creates a mock node with an overridden storage service that uses a RecordingMap, that lets us test the order
// of gets and puts. // of gets and puts.
private fun makeNodeWithTracking( private fun makeNodeWithTracking(name: CordaX500Name): StartedNode<MockNetwork.MockNode> {
networkMapAddress: SingleMessageRecipient?,
name: CordaX500Name): StartedNode<MockNetwork.MockNode> {
// Create a node in the mock network ... // Create a node in the mock network ...
return mockNet.createNode(nodeFactory = object : MockNetwork.Factory<MockNetwork.MockNode> { return mockNet.createNode(nodeFactory = object : MockNetwork.Factory<MockNetwork.MockNode> {
override fun create(config: NodeConfiguration, override fun create(config: NodeConfiguration,
network: MockNetwork, network: MockNetwork,
networkMapAddr: SingleMessageRecipient?, networkMapAddr: SingleMessageRecipient?,
advertisedServices: Set<ServiceInfo>, id: Int, id: Int, notaryIdentity: Pair<ServiceInfo, KeyPair>?,
notaryIdentity: Pair<ServiceInfo, KeyPair>?,
entropyRoot: BigInteger): MockNetwork.MockNode { entropyRoot: BigInteger): MockNetwork.MockNode {
return object : MockNetwork.MockNode(config, network, networkMapAddr, advertisedServices, id, notaryIdentity, entropyRoot) { return object : MockNetwork.MockNode(config, network, networkMapAddr, id, notaryIdentity, entropyRoot) {
// That constructs a recording tx storage // That constructs a recording tx storage
override fun makeTransactionStorage(): WritableTransactionStorage { override fun makeTransactionStorage(): WritableTransactionStorage {
return RecordingTransactionStorage(database, super.makeTransactionStorage()) return RecordingTransactionStorage(database, super.makeTransactionStorage())
@ -338,9 +334,9 @@ class TwoPartyTradeFlowTests(val anonymous: Boolean) {
mockNet = MockNetwork(false) mockNet = MockNetwork(false)
val notaryNode = mockNet.createNotaryNode() val notaryNode = mockNet.createNotaryNode()
val aliceNode = makeNodeWithTracking(notaryNode.network.myAddress, ALICE.name) val aliceNode = makeNodeWithTracking(ALICE.name)
val bobNode = makeNodeWithTracking(notaryNode.network.myAddress, BOB.name) val bobNode = makeNodeWithTracking(BOB.name)
val bankNode = makeNodeWithTracking(notaryNode.network.myAddress, BOC.name) val bankNode = makeNodeWithTracking(BOC.name)
val issuer = bankNode.info.chooseIdentity().ref(1, 2, 3) val issuer = bankNode.info.chooseIdentity().ref(1, 2, 3)
mockNet.runNetwork() mockNet.runNetwork()
notaryNode.internals.ensureRegistered() notaryNode.internals.ensureRegistered()
@ -443,9 +439,9 @@ class TwoPartyTradeFlowTests(val anonymous: Boolean) {
mockNet = MockNetwork(false) mockNet = MockNetwork(false)
val notaryNode = mockNet.createNotaryNode() val notaryNode = mockNet.createNotaryNode()
val aliceNode = makeNodeWithTracking(notaryNode.network.myAddress, ALICE.name) val aliceNode = makeNodeWithTracking(ALICE.name)
val bobNode = makeNodeWithTracking(notaryNode.network.myAddress, BOB.name) val bobNode = makeNodeWithTracking(BOB.name)
val bankNode = makeNodeWithTracking(notaryNode.network.myAddress, BOC.name) val bankNode = makeNodeWithTracking(BOC.name)
val issuer = bankNode.info.chooseIdentity().ref(1, 2, 3) val issuer = bankNode.info.chooseIdentity().ref(1, 2, 3)
mockNet.runNetwork() mockNet.runNetwork()

View File

@ -30,7 +30,6 @@ class FullNodeConfigurationTest {
p2pAddress = NetworkHostAndPort("localhost", 0), p2pAddress = NetworkHostAndPort("localhost", 0),
rpcAddress = NetworkHostAndPort("localhost", 1), rpcAddress = NetworkHostAndPort("localhost", 1),
messagingServerAddress = null, messagingServerAddress = null,
extraAdvertisedServiceIds = emptyList(),
notary = null, notary = null,
certificateChainCheckPolicies = emptyList(), certificateChainCheckPolicies = emptyList(),
devMode = true, devMode = true,

View File

@ -8,7 +8,6 @@ import net.corda.core.serialization.deserialize
import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.getOrThrow
import net.corda.node.internal.StartedNode import net.corda.node.internal.StartedNode
import net.corda.node.services.api.NetworkMapCacheInternal import net.corda.node.services.api.NetworkMapCacheInternal
import net.corda.nodeapi.internal.ServiceInfo
import net.corda.node.services.config.NodeConfiguration import net.corda.node.services.config.NodeConfiguration
import net.corda.node.services.messaging.MessagingService import net.corda.node.services.messaging.MessagingService
import net.corda.node.services.messaging.send import net.corda.node.services.messaging.send
@ -25,6 +24,7 @@ import net.corda.node.services.network.NetworkMapService.Companion.SUBSCRIPTION_
import net.corda.node.utilities.AddOrRemove import net.corda.node.utilities.AddOrRemove
import net.corda.node.utilities.AddOrRemove.ADD import net.corda.node.utilities.AddOrRemove.ADD
import net.corda.node.utilities.AddOrRemove.REMOVE import net.corda.node.utilities.AddOrRemove.REMOVE
import net.corda.nodeapi.internal.ServiceInfo
import net.corda.testing.* import net.corda.testing.*
import net.corda.testing.node.MockNetwork import net.corda.testing.node.MockNetwork
import net.corda.testing.node.MockNetwork.MockNode import net.corda.testing.node.MockNetwork.MockNode
@ -270,11 +270,10 @@ abstract class AbstractNetworkMapServiceTest<out S : AbstractNetworkMapService>
override fun create(config: NodeConfiguration, override fun create(config: NodeConfiguration,
network: MockNetwork, network: MockNetwork,
networkMapAddr: SingleMessageRecipient?, networkMapAddr: SingleMessageRecipient?,
advertisedServices: Set<ServiceInfo>,
id: Int, id: Int,
notaryIdentity: Pair<ServiceInfo, KeyPair>?, notaryIdentity: Pair<ServiceInfo, KeyPair>?,
entropyRoot: BigInteger): MockNode { entropyRoot: BigInteger): MockNode {
return object : MockNode(config, network, null, advertisedServices, id, notaryIdentity, entropyRoot) { return object : MockNode(config, network, null, id, notaryIdentity, entropyRoot) {
override fun makeNetworkMapService(network: MessagingService, networkMapCache: NetworkMapCacheInternal) = NullNetworkMapService override fun makeNetworkMapService(network: MessagingService, networkMapCache: NetworkMapCacheInternal) = NullNetworkMapService
} }
} }

View File

@ -2,12 +2,11 @@ package net.corda.node.services.network
import net.corda.core.messaging.SingleMessageRecipient import net.corda.core.messaging.SingleMessageRecipient
import net.corda.node.services.api.NetworkMapCacheInternal import net.corda.node.services.api.NetworkMapCacheInternal
import net.corda.nodeapi.internal.ServiceInfo
import net.corda.node.services.config.NodeConfiguration import net.corda.node.services.config.NodeConfiguration
import net.corda.node.services.messaging.MessagingService import net.corda.node.services.messaging.MessagingService
import net.corda.nodeapi.internal.ServiceInfo
import net.corda.testing.node.MockNetwork import net.corda.testing.node.MockNetwork
import net.corda.testing.node.MockNetwork.MockNode import net.corda.testing.node.MockNetwork.MockNode
import net.corda.testing.node.MockServices.Companion.MOCK_VERSION_INFO
import java.math.BigInteger import java.math.BigInteger
import java.security.KeyPair import java.security.KeyPair
@ -32,11 +31,10 @@ class PersistentNetworkMapServiceTest : AbstractNetworkMapServiceTest<Persistent
override fun create(config: NodeConfiguration, override fun create(config: NodeConfiguration,
network: MockNetwork, network: MockNetwork,
networkMapAddr: SingleMessageRecipient?, networkMapAddr: SingleMessageRecipient?,
advertisedServices: Set<ServiceInfo>,
id: Int, id: Int,
notaryIdentity: Pair<ServiceInfo, KeyPair>?, notaryIdentity: Pair<ServiceInfo, KeyPair>?,
entropyRoot: BigInteger): MockNode { entropyRoot: BigInteger): MockNode {
return object : MockNode(config, network, networkMapAddr, advertisedServices, id, notaryIdentity, entropyRoot) { return object : MockNode(config, network, networkMapAddr, id, notaryIdentity, entropyRoot) {
override fun makeNetworkMapService(network: MessagingService, networkMapCache: NetworkMapCacheInternal) = SwizzleNetworkMapService(network, networkMapCache) override fun makeNetworkMapService(network: MessagingService, networkMapCache: NetworkMapCacheInternal) = SwizzleNetworkMapService(network, networkMapCache)
} }
} }

View File

@ -135,7 +135,7 @@ class FlowFrameworkTests {
node2.internals.acceptableLiveFiberCountOnStop = 1 node2.internals.acceptableLiveFiberCountOnStop = 1
node2.dispose() node2.dispose()
mockNet.runNetwork() mockNet.runNetwork()
val restoredFlow = node2.restartAndGetRestoredFlow<InitiatedReceiveFlow>(node1) val restoredFlow = node2.restartAndGetRestoredFlow<InitiatedReceiveFlow>()
assertThat(restoredFlow.receivedPayloads[0]).isEqualTo("Hello") assertThat(restoredFlow.receivedPayloads[0]).isEqualTo("Hello")
} }
@ -184,7 +184,7 @@ class FlowFrameworkTests {
node2.smm.executor.flush() node2.smm.executor.flush()
node2.internals.disableDBCloseOnStop() node2.internals.disableDBCloseOnStop()
node2.dispose() // kill receiver node2.dispose() // kill receiver
val restoredFlow = node2.restartAndGetRestoredFlow<ReceiveFlow>(node1) val restoredFlow = node2.restartAndGetRestoredFlow<ReceiveFlow>()
assertThat(restoredFlow.receivedPayloads[0]).isEqualTo("Hello") assertThat(restoredFlow.receivedPayloads[0]).isEqualTo("Hello")
} }
@ -214,7 +214,7 @@ class FlowFrameworkTests {
assertEquals(1, node2.checkpointStorage.checkpoints().size) // confirm checkpoint assertEquals(1, node2.checkpointStorage.checkpoints().size) // confirm checkpoint
node2.services.networkMapCache.clearNetworkMapCache() node2.services.networkMapCache.clearNetworkMapCache()
} }
val node2b = mockNet.createNode(node2.internals.id, advertisedServices = *node2.internals.advertisedServices.toTypedArray()) val node2b = mockNet.createNode(node2.internals.id)
node2.internals.manuallyCloseDB() node2.internals.manuallyCloseDB()
val (firstAgain, fut1) = node2b.getSingleFlow<PingPongFlow>() val (firstAgain, fut1) = node2b.getSingleFlow<PingPongFlow>()
// Run the network which will also fire up the second flow. First message should get deduped. So message data stays in sync. // Run the network which will also fire up the second flow. First message should get deduped. So message data stays in sync.
@ -685,10 +685,10 @@ class FlowFrameworkTests {
//////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////
//region Helpers //region Helpers
private inline fun <reified P : FlowLogic<*>> StartedNode<MockNode>.restartAndGetRestoredFlow(networkMapNode: StartedNode<*>? = null) = internals.run { private inline fun <reified P : FlowLogic<*>> StartedNode<MockNode>.restartAndGetRestoredFlow() = internals.run {
disableDBCloseOnStop() // Handover DB to new node copy disableDBCloseOnStop() // Handover DB to new node copy
stop() stop()
val newNode = mockNet.createNode(id, advertisedServices = *advertisedServices.toTypedArray()) val newNode = mockNet.createNode(id)
newNode.internals.acceptableLiveFiberCountOnStop = 1 newNode.internals.acceptableLiveFiberCountOnStop = 1
manuallyCloseDB() manuallyCloseDB()
mockNet.runNetwork() // allow NetworkMapService messages to stabilise and thus start the state machine mockNet.runNetwork() // allow NetworkMapService messages to stabilise and thus start the state machine

View File

@ -42,7 +42,6 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) {
node { node {
name "O=Notary Service,L=Zurich,C=CH" name "O=Notary Service,L=Zurich,C=CH"
notary = [validating : true] notary = [validating : true]
advertisedServices = []
p2pPort 10002 p2pPort 10002
rpcPort 10003 rpcPort 10003
cordapps = [] cordapps = []
@ -50,7 +49,6 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) {
} }
node { node {
name "O=Bank A,L=London,C=GB" name "O=Bank A,L=London,C=GB"
advertisedServices = []
p2pPort 10005 p2pPort 10005
rpcPort 10006 rpcPort 10006
cordapps = [] cordapps = []
@ -58,7 +56,6 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) {
} }
node { node {
name "O=Bank B,L=New York,C=US" name "O=Bank B,L=New York,C=US"
advertisedServices = []
p2pPort 10008 p2pPort 10008
rpcPort 10009 rpcPort 10009
webPort 10010 webPort 10010

View File

@ -55,14 +55,13 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) {
node { node {
name "O=Notary Service,L=Zurich,C=CH" name "O=Notary Service,L=Zurich,C=CH"
notary = [validating : true] notary = [validating : true]
advertisedServices = []
p2pPort 10002 p2pPort 10002
rpcPort 10003 rpcPort 10003
cordapps = ["net.corda:finance:$corda_release_version"] cordapps = ["net.corda:finance:$corda_release_version"]
} }
node { node {
name "O=BankOfCorda,L=London,C=GB" name "O=BankOfCorda,L=London,C=GB"
advertisedServices = ["corda.issuer.USD"] extraConfig = [issuableCurrencies : ["USD"]]
p2pPort 10005 p2pPort 10005
rpcPort 10006 rpcPort 10006
webPort 10007 webPort 10007
@ -79,7 +78,6 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) {
} }
node { node {
name "O=BigCorporation,L=New York,C=US" name "O=BigCorporation,L=New York,C=US"
advertisedServices = []
p2pPort 10008 p2pPort 10008
rpcPort 10009 rpcPort 10009
webPort 10010 webPort 10010

View File

@ -55,7 +55,6 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) {
node { node {
name "O=Notary Service,L=Zurich,C=CH" name "O=Notary Service,L=Zurich,C=CH"
notary = [validating : true] notary = [validating : true]
advertisedServices = ["corda.interest_rates"]
p2pPort 10002 p2pPort 10002
rpcPort 10003 rpcPort 10003
webPort 10004 webPort 10004
@ -64,7 +63,6 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) {
} }
node { node {
name "O=Bank A,L=London,C=GB" name "O=Bank A,L=London,C=GB"
advertisedServices = []
p2pPort 10005 p2pPort 10005
rpcPort 10006 rpcPort 10006
webPort 10007 webPort 10007
@ -73,7 +71,6 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) {
} }
node { node {
name "O=Bank B,L=New York,C=US" name "O=Bank B,L=New York,C=US"
advertisedServices = []
p2pPort 10008 p2pPort 10008
rpcPort 10009 rpcPort 10009
webPort 10010 webPort 10010

View File

@ -49,9 +49,9 @@ abstract class Simulation(val networkSendManuallyPumped: Boolean,
// This puts together a mock network of SimulatedNodes. // This puts together a mock network of SimulatedNodes.
open class SimulatedNode(config: NodeConfiguration, mockNet: MockNetwork, networkMapAddress: SingleMessageRecipient?, open class SimulatedNode(config: NodeConfiguration, mockNet: MockNetwork, networkMapAddress: SingleMessageRecipient?,
advertisedServices: Set<ServiceInfo>, id: Int, notaryIdentity: Pair<ServiceInfo, KeyPair>?, id: Int, notaryIdentity: Pair<ServiceInfo, KeyPair>?,
entropyRoot: BigInteger) entropyRoot: BigInteger)
: MockNetwork.MockNode(config, mockNet, networkMapAddress, advertisedServices, id, notaryIdentity, entropyRoot) { : MockNetwork.MockNode(config, mockNet, networkMapAddress, id, notaryIdentity, entropyRoot) {
override val started: StartedNode<SimulatedNode>? get() = uncheckedCast(super.started) override val started: StartedNode<SimulatedNode>? get() = uncheckedCast(super.started)
override fun findMyLocation(): WorldMapLocation? { override fun findMyLocation(): WorldMapLocation? {
return configuration.myLegalName.locality.let { CityDatabase[it] } return configuration.myLegalName.locality.let { CityDatabase[it] }
@ -62,15 +62,14 @@ abstract class Simulation(val networkSendManuallyPumped: Boolean,
var counter = 0 var counter = 0
override fun create(config: NodeConfiguration, network: MockNetwork, networkMapAddr: SingleMessageRecipient?, override fun create(config: NodeConfiguration, network: MockNetwork, networkMapAddr: SingleMessageRecipient?,
advertisedServices: Set<ServiceInfo>, id: Int, notaryIdentity: Pair<ServiceInfo, KeyPair>?, id: Int, notaryIdentity: Pair<ServiceInfo, KeyPair>?, entropyRoot: BigInteger): SimulatedNode {
entropyRoot: BigInteger): SimulatedNode {
val letter = 'A' + counter val letter = 'A' + counter
val (city, country) = bankLocations[counter++ % bankLocations.size] val (city, country) = bankLocations[counter++ % bankLocations.size]
val cfg = testNodeConfiguration( val cfg = testNodeConfiguration(
baseDirectory = config.baseDirectory, baseDirectory = config.baseDirectory,
myLegalName = CordaX500Name(organisation = "Bank $letter", locality = city, country = country)) myLegalName = CordaX500Name(organisation = "Bank $letter", locality = city, country = country))
return SimulatedNode(cfg, network, networkMapAddr, advertisedServices, id, notaryIdentity, entropyRoot) return SimulatedNode(cfg, network, networkMapAddr, id, notaryIdentity, entropyRoot)
} }
fun createAll(): List<SimulatedNode> { fun createAll(): List<SimulatedNode> {
@ -85,25 +84,23 @@ abstract class Simulation(val networkSendManuallyPumped: Boolean,
object NetworkMapNodeFactory : MockNetwork.Factory<SimulatedNode> { object NetworkMapNodeFactory : MockNetwork.Factory<SimulatedNode> {
override fun create(config: NodeConfiguration, network: MockNetwork, networkMapAddr: SingleMessageRecipient?, override fun create(config: NodeConfiguration, network: MockNetwork, networkMapAddr: SingleMessageRecipient?,
advertisedServices: Set<ServiceInfo>, id: Int, notaryIdentity: Pair<ServiceInfo, KeyPair>?, id: Int, notaryIdentity: Pair<ServiceInfo, KeyPair>?, entropyRoot: BigInteger): SimulatedNode {
entropyRoot: BigInteger): SimulatedNode {
val cfg = testNodeConfiguration( val cfg = testNodeConfiguration(
baseDirectory = config.baseDirectory, baseDirectory = config.baseDirectory,
myLegalName = DUMMY_MAP.name) myLegalName = DUMMY_MAP.name)
return object : SimulatedNode(cfg, network, networkMapAddr, advertisedServices, id, notaryIdentity, entropyRoot) {} return object : SimulatedNode(cfg, network, networkMapAddr, id, notaryIdentity, entropyRoot) {}
} }
} }
object NotaryNodeFactory : MockNetwork.Factory<SimulatedNode> { object NotaryNodeFactory : MockNetwork.Factory<SimulatedNode> {
override fun create(config: NodeConfiguration, network: MockNetwork, networkMapAddr: SingleMessageRecipient?, override fun create(config: NodeConfiguration, network: MockNetwork, networkMapAddr: SingleMessageRecipient?,
advertisedServices: Set<ServiceInfo>, id: Int, notaryIdentity: Pair<ServiceInfo, KeyPair>?, id: Int, notaryIdentity: Pair<ServiceInfo, KeyPair>?, entropyRoot: BigInteger): SimulatedNode {
entropyRoot: BigInteger): SimulatedNode {
requireNotNull(config.notary) requireNotNull(config.notary)
val cfg = testNodeConfiguration( val cfg = testNodeConfiguration(
baseDirectory = config.baseDirectory, baseDirectory = config.baseDirectory,
myLegalName = DUMMY_NOTARY.name, myLegalName = DUMMY_NOTARY.name,
notaryConfig = config.notary) notaryConfig = config.notary)
return SimulatedNode(cfg, network, networkMapAddr, advertisedServices, id, notaryIdentity, entropyRoot) return SimulatedNode(cfg, network, networkMapAddr, id, notaryIdentity, entropyRoot)
} }
} }
@ -112,12 +109,11 @@ abstract class Simulation(val networkSendManuallyPumped: Boolean,
val RATES_SERVICE_NAME = CordaX500Name(organisation = "Rates Service Provider", locality = "Madrid", country = "ES") val RATES_SERVICE_NAME = CordaX500Name(organisation = "Rates Service Provider", locality = "Madrid", country = "ES")
override fun create(config: NodeConfiguration, network: MockNetwork, networkMapAddr: SingleMessageRecipient?, override fun create(config: NodeConfiguration, network: MockNetwork, networkMapAddr: SingleMessageRecipient?,
advertisedServices: Set<ServiceInfo>, id: Int, notaryIdentity: Pair<ServiceInfo, KeyPair>?, id: Int, notaryIdentity: Pair<ServiceInfo, KeyPair>?, entropyRoot: BigInteger): SimulatedNode {
entropyRoot: BigInteger): SimulatedNode {
val cfg = testNodeConfiguration( val cfg = testNodeConfiguration(
baseDirectory = config.baseDirectory, baseDirectory = config.baseDirectory,
myLegalName = RATES_SERVICE_NAME) myLegalName = RATES_SERVICE_NAME)
return object : SimulatedNode(cfg, network, networkMapAddr, advertisedServices, id, notaryIdentity, entropyRoot) { return object : SimulatedNode(cfg, network, networkMapAddr, id, notaryIdentity, entropyRoot) {
override fun start() = super.start().apply { override fun start() = super.start().apply {
registerInitiatedFlow(NodeInterestRates.FixQueryHandler::class.java) registerInitiatedFlow(NodeInterestRates.FixQueryHandler::class.java)
registerInitiatedFlow(NodeInterestRates.FixSignHandler::class.java) registerInitiatedFlow(NodeInterestRates.FixSignHandler::class.java)
@ -133,12 +129,11 @@ abstract class Simulation(val networkSendManuallyPumped: Boolean,
object RegulatorFactory : MockNetwork.Factory<SimulatedNode> { object RegulatorFactory : MockNetwork.Factory<SimulatedNode> {
override fun create(config: NodeConfiguration, network: MockNetwork, networkMapAddr: SingleMessageRecipient?, override fun create(config: NodeConfiguration, network: MockNetwork, networkMapAddr: SingleMessageRecipient?,
advertisedServices: Set<ServiceInfo>, id: Int, notaryIdentity: Pair<ServiceInfo, KeyPair>?, id: Int, notaryIdentity: Pair<ServiceInfo, KeyPair>?, entropyRoot: BigInteger): SimulatedNode {
entropyRoot: BigInteger): SimulatedNode {
val cfg = testNodeConfiguration( val cfg = testNodeConfiguration(
baseDirectory = config.baseDirectory, baseDirectory = config.baseDirectory,
myLegalName = DUMMY_REGULATOR.name) myLegalName = DUMMY_REGULATOR.name)
return object : SimulatedNode(cfg, network, networkMapAddr, advertisedServices, id, notaryIdentity, entropyRoot) { return object : SimulatedNode(cfg, network, networkMapAddr, id, notaryIdentity, entropyRoot) {
// TODO: Regulatory nodes don't actually exist properly, this is a last minute demo request. // TODO: Regulatory nodes don't actually exist properly, this is a last minute demo request.
// So we just fire a message at a node that doesn't know how to handle it, and it'll ignore it. // So we just fire a message at a node that doesn't know how to handle it, and it'll ignore it.
// But that's fine for visualisation purposes. // But that's fine for visualisation purposes.

View File

@ -68,13 +68,11 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) {
node { node {
name "O=Notary Service,L=Zurich,C=CH" name "O=Notary Service,L=Zurich,C=CH"
notary = [validating : true] notary = [validating : true]
advertisedServices = []
p2pPort 10002 p2pPort 10002
cordapps = ["net.corda:finance:$corda_release_version"] cordapps = ["net.corda:finance:$corda_release_version"]
} }
node { node {
name "O=Bank A,L=London,C=GB" name "O=Bank A,L=London,C=GB"
advertisedServices = []
p2pPort 10004 p2pPort 10004
webPort 10005 webPort 10005
rpcPort 10006 rpcPort 10006
@ -83,7 +81,6 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) {
} }
node { node {
name "O=Bank B,L=New York,C=US" name "O=Bank B,L=New York,C=US"
advertisedServices = []
p2pPort 10007 p2pPort 10007
webPort 10008 webPort 10008
rpcPort 10009 rpcPort 10009
@ -92,7 +89,6 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) {
} }
node { node {
name "O=Bank C,L=Tokyo,C=JP" name "O=Bank C,L=Tokyo,C=JP"
advertisedServices = []
p2pPort 10010 p2pPort 10010
webPort 10011 webPort 10011
rpcPort 10012 rpcPort 10012

View File

@ -56,13 +56,11 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) {
node { node {
name "O=Notary Service,L=Zurich,C=CH" name "O=Notary Service,L=Zurich,C=CH"
notary = [validating : true] notary = [validating : true]
advertisedServices = []
p2pPort 10002 p2pPort 10002
cordapps = ["net.corda:finance:$corda_release_version"] cordapps = ["net.corda:finance:$corda_release_version"]
} }
node { node {
name "O=Bank A,L=London,C=GB" name "O=Bank A,L=London,C=GB"
advertisedServices = []
p2pPort 10005 p2pPort 10005
rpcPort 10006 rpcPort 10006
cordapps = ["net.corda:finance:$corda_release_version"] cordapps = ["net.corda:finance:$corda_release_version"]
@ -70,7 +68,6 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) {
} }
node { node {
name "O=Bank B,L=New York,C=US" name "O=Bank B,L=New York,C=US"
advertisedServices = []
p2pPort 10008 p2pPort 10008
rpcPort 10009 rpcPort 10009
cordapps = ["net.corda:finance:$corda_release_version"] cordapps = ["net.corda:finance:$corda_release_version"]
@ -78,7 +75,6 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) {
} }
node { node {
name "O=BankOfCorda,L=New York,C=US" name "O=BankOfCorda,L=New York,C=US"
advertisedServices = []
p2pPort 10011 p2pPort 10011
rpcPort 10012 rpcPort 10012
cordapps = ["net.corda:finance:$corda_release_version"] cordapps = ["net.corda:finance:$corda_release_version"]

View File

@ -30,7 +30,6 @@ import net.corda.node.utilities.ServiceIdentityGenerator
import net.corda.nodeapi.User import net.corda.nodeapi.User
import net.corda.nodeapi.config.parseAs import net.corda.nodeapi.config.parseAs
import net.corda.nodeapi.config.toConfig import net.corda.nodeapi.config.toConfig
import net.corda.nodeapi.internal.ServiceInfo
import net.corda.nodeapi.internal.addShutdownHook import net.corda.nodeapi.internal.addShutdownHook
import net.corda.testing.* import net.corda.testing.*
import net.corda.testing.node.MockServices.Companion.MOCK_VERSION_INFO import net.corda.testing.node.MockServices.Companion.MOCK_VERSION_INFO
@ -78,7 +77,6 @@ interface DriverDSLExposedInterface : CordformContext {
* when called from Java code. * when called from Java code.
* @param providedName Optional name of the node, which will be its legal name in [Party]. Defaults to something * @param providedName Optional name of the node, which will be its legal name in [Party]. Defaults to something
* random. Note that this must be unique as the driver uses it as a primary key! * random. Note that this must be unique as the driver uses it as a primary key!
* @param advertisedServices The set of services to be advertised by the node. Defaults to empty set.
* @param verifierType The type of transaction verifier to use. See: [VerifierType] * @param verifierType The type of transaction verifier to use. See: [VerifierType]
* @param rpcUsers List of users who are authorised to use the RPC system. Defaults to empty list. * @param rpcUsers List of users who are authorised to use the RPC system. Defaults to empty list.
* @param startInSameProcess Determines if the node should be started inside the same process the Driver is running * @param startInSameProcess Determines if the node should be started inside the same process the Driver is running
@ -88,7 +86,6 @@ interface DriverDSLExposedInterface : CordformContext {
fun startNode( fun startNode(
defaultParameters: NodeParameters = NodeParameters(), defaultParameters: NodeParameters = NodeParameters(),
providedName: CordaX500Name? = defaultParameters.providedName, providedName: CordaX500Name? = defaultParameters.providedName,
advertisedServices: Set<ServiceInfo> = defaultParameters.advertisedServices,
rpcUsers: List<User> = defaultParameters.rpcUsers, rpcUsers: List<User> = defaultParameters.rpcUsers,
verifierType: VerifierType = defaultParameters.verifierType, verifierType: VerifierType = defaultParameters.verifierType,
customOverrides: Map<String, Any?> = defaultParameters.customOverrides, customOverrides: Map<String, Any?> = defaultParameters.customOverrides,
@ -267,7 +264,6 @@ sealed class PortAllocation {
*/ */
data class NodeParameters( data class NodeParameters(
val providedName: CordaX500Name? = null, val providedName: CordaX500Name? = null,
val advertisedServices: Set<ServiceInfo> = emptySet(),
val rpcUsers: List<User> = emptyList(), val rpcUsers: List<User> = emptyList(),
val verifierType: VerifierType = VerifierType.InMemory, val verifierType: VerifierType = VerifierType.InMemory,
val customOverrides: Map<String, Any?> = emptyMap(), val customOverrides: Map<String, Any?> = emptyMap(),
@ -275,7 +271,6 @@ data class NodeParameters(
val maximumHeapSize: String = "200m" val maximumHeapSize: String = "200m"
) { ) {
fun setProvidedName(providedName: CordaX500Name?) = copy(providedName = providedName) fun setProvidedName(providedName: CordaX500Name?) = copy(providedName = providedName)
fun setAdvertisedServices(advertisedServices: Set<ServiceInfo>) = copy(advertisedServices = advertisedServices)
fun setRpcUsers(rpcUsers: List<User>) = copy(rpcUsers = rpcUsers) fun setRpcUsers(rpcUsers: List<User>) = copy(rpcUsers = rpcUsers)
fun setVerifierType(verifierType: VerifierType) = copy(verifierType = verifierType) fun setVerifierType(verifierType: VerifierType) = copy(verifierType = verifierType)
fun setCustomerOverrides(customOverrides: Map<String, Any?>) = copy(customOverrides = customOverrides) fun setCustomerOverrides(customOverrides: Map<String, Any?>) = copy(customOverrides = customOverrides)
@ -686,7 +681,6 @@ class DriverDSL(
override fun startNode( override fun startNode(
defaultParameters: NodeParameters, defaultParameters: NodeParameters,
providedName: CordaX500Name?, providedName: CordaX500Name?,
advertisedServices: Set<ServiceInfo>,
rpcUsers: List<User>, rpcUsers: List<User>,
verifierType: VerifierType, verifierType: VerifierType,
customOverrides: Map<String, Any?>, customOverrides: Map<String, Any?>,
@ -710,7 +704,6 @@ class DriverDSL(
"p2pAddress" to p2pAddress.toString(), "p2pAddress" to p2pAddress.toString(),
"rpcAddress" to rpcAddress.toString(), "rpcAddress" to rpcAddress.toString(),
"webAddress" to webAddress.toString(), "webAddress" to webAddress.toString(),
"extraAdvertisedServiceIds" to advertisedServices.map { it.toString() },
"networkMapService" to networkMapServiceConfigLookup(name), "networkMapService" to networkMapServiceConfigLookup(name),
"useTestClock" to useTestClock, "useTestClock" to useTestClock,
"rpcUsers" to if (rpcUsers.isEmpty()) defaultRpcUserList else rpcUsers.map { it.toMap() }, "rpcUsers" to if (rpcUsers.isEmpty()) defaultRpcUserList else rpcUsers.map { it.toMap() },
@ -741,7 +734,6 @@ class DriverDSL(
baseDirectory = baseDirectory(name), baseDirectory = baseDirectory(name),
allowMissingConfig = true, allowMissingConfig = true,
configOverrides = node.config + notary + mapOf( configOverrides = node.config + notary + mapOf(
"extraAdvertisedServiceIds" to node.advertisedServices,
"networkMapService" to networkMapServiceConfigLookup(name), "networkMapService" to networkMapServiceConfigLookup(name),
"rpcUsers" to if (rpcUsers.isEmpty()) defaultRpcUserList else rpcUsers "rpcUsers" to if (rpcUsers.isEmpty()) defaultRpcUserList else rpcUsers
) )
@ -935,7 +927,7 @@ class DriverDSL(
// Write node.conf // Write node.conf
writeConfig(nodeConf.baseDirectory, "node.conf", config) writeConfig(nodeConf.baseDirectory, "node.conf", config)
// TODO pass the version in? // TODO pass the version in?
val node = Node(nodeConf, nodeConf.calculateServices(), MOCK_VERSION_INFO, initialiseSerialization = false).start() val node = Node(nodeConf, MOCK_VERSION_INFO, initialiseSerialization = false).start()
val nodeThread = thread(name = nodeConf.myLegalName.organisation) { val nodeThread = thread(name = nodeConf.myLegalName.organisation) {
node.internals.run() node.internals.run()
} }

View File

@ -8,7 +8,6 @@ import net.corda.core.identity.CordaX500Name
import net.corda.node.services.config.NotaryConfig import net.corda.node.services.config.NotaryConfig
import net.corda.nodeapi.User import net.corda.nodeapi.User
import net.corda.nodeapi.config.toConfig import net.corda.nodeapi.config.toConfig
import net.corda.nodeapi.internal.ServiceInfo
fun CordformDefinition.node(configure: CordformNode.() -> Unit) { fun CordformDefinition.node(configure: CordformNode.() -> Unit) {
addNode { cordformNode -> cordformNode.configure() } addNode { cordformNode -> cordformNode.configure() }
@ -23,7 +22,3 @@ fun CordformNode.rpcUsers(vararg users: User) {
fun CordformNode.notary(notaryConfig: NotaryConfig) { fun CordformNode.notary(notaryConfig: NotaryConfig) {
notary = notaryConfig.toConfig().root().unwrapped() notary = notaryConfig.toConfig().root().unwrapped()
} }
fun CordformNode.advertisedServices(vararg services: ServiceInfo) {
advertisedServices = services.map { it.toString() }
}

View File

@ -115,15 +115,13 @@ class MockNetwork(private val networkSendManuallyPumped: Boolean = false,
* but can be overriden to cause nodes to have stable or colliding identity/service keys. * but can be overriden to cause nodes to have stable or colliding identity/service keys.
*/ */
fun create(config: NodeConfiguration, network: MockNetwork, networkMapAddr: SingleMessageRecipient?, fun create(config: NodeConfiguration, network: MockNetwork, networkMapAddr: SingleMessageRecipient?,
advertisedServices: Set<ServiceInfo>, id: Int, notaryIdentity: Pair<ServiceInfo, KeyPair>?, id: Int, notaryIdentity: Pair<ServiceInfo, KeyPair>?, entropyRoot: BigInteger): N
entropyRoot: BigInteger): N
} }
object DefaultFactory : Factory<MockNode> { object DefaultFactory : Factory<MockNode> {
override fun create(config: NodeConfiguration, network: MockNetwork, networkMapAddr: SingleMessageRecipient?, override fun create(config: NodeConfiguration, network: MockNetwork, networkMapAddr: SingleMessageRecipient?,
advertisedServices: Set<ServiceInfo>, id: Int, notaryIdentity: Pair<ServiceInfo, KeyPair>?, id: Int, notaryIdentity: Pair<ServiceInfo, KeyPair>?, entropyRoot: BigInteger): MockNode {
entropyRoot: BigInteger): MockNode { return MockNode(config, network, networkMapAddr, id, notaryIdentity, entropyRoot)
return MockNode(config, network, networkMapAddr, advertisedServices, id, notaryIdentity, entropyRoot)
} }
} }
@ -159,11 +157,10 @@ class MockNetwork(private val networkSendManuallyPumped: Boolean = false,
open class MockNode(config: NodeConfiguration, open class MockNode(config: NodeConfiguration,
val mockNet: MockNetwork, val mockNet: MockNetwork,
override val networkMapAddress: SingleMessageRecipient?, override val networkMapAddress: SingleMessageRecipient?,
advertisedServices: Set<ServiceInfo>,
val id: Int, val id: Int,
internal val notaryIdentity: Pair<ServiceInfo, KeyPair>?, internal val notaryIdentity: Pair<ServiceInfo, KeyPair>?,
val entropyRoot: BigInteger = BigInteger.valueOf(random63BitValue())) : val entropyRoot: BigInteger = BigInteger.valueOf(random63BitValue())) :
AbstractNode(config, advertisedServices, TestClock(), MOCK_VERSION_INFO, mockNet.busyLatch) { AbstractNode(config, TestClock(), MOCK_VERSION_INFO, mockNet.busyLatch) {
var counter = entropyRoot var counter = entropyRoot
override val log: Logger = loggerFor<MockNode>() override val log: Logger = loggerFor<MockNode>()
override val serverThread: AffinityExecutor = override val serverThread: AffinityExecutor =
@ -303,7 +300,6 @@ class MockNetwork(private val networkSendManuallyPumped: Boolean = false,
nodeFactory = nodeFactory ?: defaultFactory, nodeFactory = nodeFactory ?: defaultFactory,
legalName = MOCK_NET_MAP.name, legalName = MOCK_NET_MAP.name,
notaryIdentity = null, notaryIdentity = null,
advertisedServices = arrayOf(),
entropyRoot = BigInteger.valueOf(random63BitValue()), entropyRoot = BigInteger.valueOf(random63BitValue()),
configOverrides = {}, configOverrides = {},
start = true start = true
@ -315,18 +311,16 @@ class MockNetwork(private val networkSendManuallyPumped: Boolean = false,
fun createUnstartedNode(forcedID: Int? = null, fun createUnstartedNode(forcedID: Int? = null,
legalName: CordaX500Name? = null, notaryIdentity: Pair<ServiceInfo, KeyPair>? = null, legalName: CordaX500Name? = null, notaryIdentity: Pair<ServiceInfo, KeyPair>? = null,
entropyRoot: BigInteger = BigInteger.valueOf(random63BitValue()), entropyRoot: BigInteger = BigInteger.valueOf(random63BitValue()),
vararg advertisedServices: ServiceInfo,
configOverrides: (NodeConfiguration) -> Any? = {}): MockNode { configOverrides: (NodeConfiguration) -> Any? = {}): MockNode {
return createUnstartedNode(forcedID, defaultFactory, legalName, notaryIdentity, entropyRoot, *advertisedServices, configOverrides = configOverrides) return createUnstartedNode(forcedID, defaultFactory, legalName, notaryIdentity, entropyRoot, configOverrides = configOverrides)
} }
fun <N : MockNode> createUnstartedNode(forcedID: Int? = null, nodeFactory: Factory<N>, fun <N : MockNode> createUnstartedNode(forcedID: Int? = null, nodeFactory: Factory<N>,
legalName: CordaX500Name? = null, notaryIdentity: Pair<ServiceInfo, KeyPair>? = null, legalName: CordaX500Name? = null, notaryIdentity: Pair<ServiceInfo, KeyPair>? = null,
entropyRoot: BigInteger = BigInteger.valueOf(random63BitValue()), entropyRoot: BigInteger = BigInteger.valueOf(random63BitValue()),
vararg advertisedServices: ServiceInfo,
configOverrides: (NodeConfiguration) -> Any? = {}): N { configOverrides: (NodeConfiguration) -> Any? = {}): N {
val networkMapAddress = networkMapNode.network.myAddress val networkMapAddress = networkMapNode.network.myAddress
return createNodeImpl(networkMapAddress, forcedID, nodeFactory, false, legalName, notaryIdentity, entropyRoot, advertisedServices, configOverrides) return createNodeImpl(networkMapAddress, forcedID, nodeFactory, false, legalName, notaryIdentity, entropyRoot, configOverrides)
} }
/** /**
@ -340,25 +334,22 @@ class MockNetwork(private val networkSendManuallyPumped: Boolean = false,
fun createNode(forcedID: Int? = null, fun createNode(forcedID: Int? = null,
legalName: CordaX500Name? = null, notaryIdentity: Pair<ServiceInfo, KeyPair>? = null, legalName: CordaX500Name? = null, notaryIdentity: Pair<ServiceInfo, KeyPair>? = null,
entropyRoot: BigInteger = BigInteger.valueOf(random63BitValue()), entropyRoot: BigInteger = BigInteger.valueOf(random63BitValue()),
vararg advertisedServices: ServiceInfo,
configOverrides: (NodeConfiguration) -> Any? = {}): StartedNode<MockNode> { configOverrides: (NodeConfiguration) -> Any? = {}): StartedNode<MockNode> {
return createNode(forcedID, defaultFactory, legalName, notaryIdentity, entropyRoot, *advertisedServices, configOverrides = configOverrides) return createNode(forcedID, defaultFactory, legalName, notaryIdentity, entropyRoot, configOverrides = configOverrides)
} }
/** Like the other [createNode] but takes a [Factory] and propagates its [MockNode] subtype. */ /** Like the other [createNode] but takes a [Factory] and propagates its [MockNode] subtype. */
fun <N : MockNode> createNode(forcedID: Int? = null, nodeFactory: Factory<N>, fun <N : MockNode> createNode(forcedID: Int? = null, nodeFactory: Factory<N>,
legalName: CordaX500Name? = null, notaryIdentity: Pair<ServiceInfo, KeyPair>? = null, legalName: CordaX500Name? = null, notaryIdentity: Pair<ServiceInfo, KeyPair>? = null,
entropyRoot: BigInteger = BigInteger.valueOf(random63BitValue()), entropyRoot: BigInteger = BigInteger.valueOf(random63BitValue()),
vararg advertisedServices: ServiceInfo,
configOverrides: (NodeConfiguration) -> Any? = {}): StartedNode<N> { configOverrides: (NodeConfiguration) -> Any? = {}): StartedNode<N> {
val networkMapAddress = networkMapNode.network.myAddress val networkMapAddress = networkMapNode.network.myAddress
return uncheckedCast(createNodeImpl(networkMapAddress, forcedID, nodeFactory, true, legalName, notaryIdentity, entropyRoot, advertisedServices, configOverrides).started)!! return uncheckedCast(createNodeImpl(networkMapAddress, forcedID, nodeFactory, true, legalName, notaryIdentity, entropyRoot, configOverrides).started)!!
} }
private fun <N : MockNode> createNodeImpl(networkMapAddress: SingleMessageRecipient?, forcedID: Int?, nodeFactory: Factory<N>, private fun <N : MockNode> createNodeImpl(networkMapAddress: SingleMessageRecipient?, forcedID: Int?, nodeFactory: Factory<N>,
start: Boolean, legalName: CordaX500Name?, notaryIdentity: Pair<ServiceInfo, KeyPair>?, start: Boolean, legalName: CordaX500Name?, notaryIdentity: Pair<ServiceInfo, KeyPair>?,
entropyRoot: BigInteger, entropyRoot: BigInteger,
advertisedServices: Array<out ServiceInfo>,
configOverrides: (NodeConfiguration) -> Any?): N { configOverrides: (NodeConfiguration) -> Any?): N {
val id = forcedID ?: nextNodeId++ val id = forcedID ?: nextNodeId++
val config = testNodeConfiguration( val config = testNodeConfiguration(
@ -367,7 +358,7 @@ class MockNetwork(private val networkSendManuallyPumped: Boolean = false,
whenever(it.dataSourceProperties).thenReturn(makeTestDataSourceProperties("node_${id}_net_$networkId")) whenever(it.dataSourceProperties).thenReturn(makeTestDataSourceProperties("node_${id}_net_$networkId"))
configOverrides(it) configOverrides(it)
} }
return nodeFactory.create(config, this, networkMapAddress, advertisedServices.toSet(), id, notaryIdentity, entropyRoot).apply { return nodeFactory.create(config, this, networkMapAddress, id, notaryIdentity, entropyRoot).apply {
if (start) { if (start) {
start() start()
if (threadPerNode && networkMapAddress != null) nodeReadyFuture.getOrThrow() // XXX: What about manually-started nodes? if (threadPerNode && networkMapAddress != null) nodeReadyFuture.getOrThrow() // XXX: What about manually-started nodes?
@ -425,7 +416,7 @@ class MockNetwork(private val networkSendManuallyPumped: Boolean = false,
return when (msgRecipient) { return when (msgRecipient) {
is SingleMessageRecipient -> nodes.single { it.started!!.network.myAddress == msgRecipient } is SingleMessageRecipient -> nodes.single { it.started!!.network.myAddress == msgRecipient }
is InMemoryMessagingNetwork.ServiceHandle -> { is InMemoryMessagingNetwork.ServiceHandle -> {
nodes.firstOrNull { it.advertisedServices.any { it.name == msgRecipient.party.name } } nodes.firstOrNull { it.started!!.info.isLegalIdentity(msgRecipient.party) }
?: throw IllegalArgumentException("Couldn't find node advertising service with owning party name: ${msgRecipient.party.name} ") ?: throw IllegalArgumentException("Couldn't find node advertising service with owning party name: ${msgRecipient.party.name} ")
} }
else -> throw IllegalArgumentException("Method not implemented for different type of message recipients") else -> throw IllegalArgumentException("Method not implemented for different type of message recipients")

View File

@ -14,7 +14,6 @@ import net.corda.node.utilities.ServiceIdentityGenerator
import net.corda.nodeapi.User import net.corda.nodeapi.User
import net.corda.nodeapi.config.parseAs import net.corda.nodeapi.config.parseAs
import net.corda.nodeapi.config.toConfig import net.corda.nodeapi.config.toConfig
import net.corda.nodeapi.internal.ServiceInfo
import net.corda.testing.DUMMY_MAP import net.corda.testing.DUMMY_MAP
import net.corda.testing.TestDependencyInjectionBase import net.corda.testing.TestDependencyInjectionBase
import net.corda.testing.driver.addressMustNotBeBoundFuture import net.corda.testing.driver.addressMustNotBeBoundFuture
@ -83,11 +82,10 @@ abstract class NodeBasedTest : TestDependencyInjectionBase() {
*/ */
fun startNetworkMapNode(legalName: CordaX500Name = DUMMY_MAP.name, fun startNetworkMapNode(legalName: CordaX500Name = DUMMY_MAP.name,
platformVersion: Int = 1, platformVersion: Int = 1,
advertisedServices: Set<ServiceInfo> = emptySet(),
rpcUsers: List<User> = emptyList(), rpcUsers: List<User> = emptyList(),
configOverrides: Map<String, Any> = emptyMap()): StartedNode<Node> { configOverrides: Map<String, Any> = emptyMap()): StartedNode<Node> {
check(_networkMapNode == null || _networkMapNode!!.info.legalIdentitiesAndCerts.first().name == legalName) check(_networkMapNode == null || _networkMapNode!!.info.legalIdentitiesAndCerts.first().name == legalName)
return startNodeInternal(legalName, platformVersion, advertisedServices, rpcUsers, configOverrides).apply { return startNodeInternal(legalName, platformVersion, rpcUsers, configOverrides).apply {
_networkMapNode = this _networkMapNode = this
} }
} }
@ -95,7 +93,6 @@ abstract class NodeBasedTest : TestDependencyInjectionBase() {
@JvmOverloads @JvmOverloads
fun startNode(legalName: CordaX500Name, fun startNode(legalName: CordaX500Name,
platformVersion: Int = 1, platformVersion: Int = 1,
advertisedServices: Set<ServiceInfo> = emptySet(),
rpcUsers: List<User> = emptyList(), rpcUsers: List<User> = emptyList(),
configOverrides: Map<String, Any> = emptyMap(), configOverrides: Map<String, Any> = emptyMap(),
noNetworkMap: Boolean = false, noNetworkMap: Boolean = false,
@ -119,7 +116,6 @@ abstract class NodeBasedTest : TestDependencyInjectionBase() {
val node = startNodeInternal( val node = startNodeInternal(
legalName, legalName,
platformVersion, platformVersion,
advertisedServices,
rpcUsers, rpcUsers,
networkMapConf + configOverrides, networkMapConf + configOverrides,
noNetworkMap) noNetworkMap)
@ -175,7 +171,6 @@ abstract class NodeBasedTest : TestDependencyInjectionBase() {
private fun startNodeInternal(legalName: CordaX500Name, private fun startNodeInternal(legalName: CordaX500Name,
platformVersion: Int, platformVersion: Int,
advertisedServices: Set<ServiceInfo>,
rpcUsers: List<User>, rpcUsers: List<User>,
configOverrides: Map<String, Any>, configOverrides: Map<String, Any>,
noNetworkMap: Boolean = false): StartedNode<Node> { noNetworkMap: Boolean = false): StartedNode<Node> {
@ -189,14 +184,15 @@ abstract class NodeBasedTest : TestDependencyInjectionBase() {
"myLegalName" to legalName.toString(), "myLegalName" to legalName.toString(),
"p2pAddress" to p2pAddress, "p2pAddress" to p2pAddress,
"rpcAddress" to localPort[1].toString(), "rpcAddress" to localPort[1].toString(),
"extraAdvertisedServiceIds" to advertisedServices.map { it.toString() },
"rpcUsers" to rpcUsers.map { it.toMap() }, "rpcUsers" to rpcUsers.map { it.toMap() },
"noNetworkMap" to noNetworkMap "noNetworkMap" to noNetworkMap
) + configOverrides ) + configOverrides
) )
val parsedConfig = config.parseAs<FullNodeConfiguration>() val parsedConfig = config.parseAs<FullNodeConfiguration>()
val node = Node(parsedConfig, parsedConfig.calculateServices(), MOCK_VERSION_INFO.copy(platformVersion = platformVersion), val node = Node(
parsedConfig,
MOCK_VERSION_INFO.copy(platformVersion = platformVersion),
initialiseSerialization = false).start() initialiseSerialization = false).start()
nodes += node nodes += node
thread(name = legalName.organisation) { thread(name = legalName.organisation) {

View File

@ -70,9 +70,6 @@ node in a new tab.
![Configure Bank Node](demobench-configure-bank.png) ![Configure Bank Node](demobench-configure-bank.png)
This time, there will be additional services available. Select `corda.cash` and
`corda.issuer.GBP`, and then press the `Start node` button.
When you press the `Launch Web Server` this time, your browser should open to a When you press the `Launch Web Server` this time, your browser should open to a
page saying: page saying:
> ### Installed CorDapps > ### Installed CorDapps

View File

@ -71,6 +71,7 @@ dependencies {
testCompile "junit:junit:$junit_version" testCompile "junit:junit:$junit_version"
testCompile "org.jetbrains.kotlin:kotlin-test:$kotlin_version" testCompile "org.jetbrains.kotlin:kotlin-test:$kotlin_version"
testCompile "org.assertj:assertj-core:${assertj_version}"
testCompile "org.mockito:mockito-core:$mockito_version" testCompile "org.mockito:mockito-core:$mockito_version"
} }

View File

@ -5,13 +5,12 @@ import net.corda.core.internal.div
import net.corda.core.internal.list import net.corda.core.internal.list
import net.corda.core.utilities.loggerFor import net.corda.core.utilities.loggerFor
import net.corda.demobench.model.JVMConfig import net.corda.demobench.model.JVMConfig
import net.corda.demobench.model.NodeConfig import net.corda.demobench.model.NodeConfigWrapper
import net.corda.demobench.model.forceDirectory
import net.corda.demobench.readErrorLines import net.corda.demobench.readErrorLines
import tornadofx.* import tornadofx.*
import java.io.IOException import java.io.IOException
import java.nio.file.Files import java.nio.file.Files
import java.nio.file.StandardCopyOption.* import java.nio.file.StandardCopyOption.REPLACE_EXISTING
import java.util.concurrent.Executors import java.util.concurrent.Executors
class Explorer internal constructor(private val explorerController: ExplorerController) : AutoCloseable { class Explorer internal constructor(private val explorerController: ExplorerController) : AutoCloseable {
@ -23,29 +22,32 @@ class Explorer internal constructor(private val explorerController: ExplorerCont
private var process: Process? = null private var process: Process? = null
@Throws(IOException::class) @Throws(IOException::class)
fun open(config: NodeConfig, onExit: (NodeConfig) -> Unit) { fun open(config: NodeConfigWrapper, onExit: (NodeConfigWrapper) -> Unit) {
val explorerDir = config.explorerDir.toFile() val explorerDir = config.explorerDir
if (!explorerDir.forceDirectory()) { try {
log.warn("Failed to create working directory '{}'", explorerDir.absolutePath) explorerDir.createDirectories()
} catch (e: IOException) {
log.warn("Failed to create working directory '{}'", explorerDir.toAbsolutePath())
onExit(config) onExit(config)
return return
} }
val legalName = config.nodeConfig.myLegalName
try { try {
installApps(config) installApps(config)
val user = config.users.elementAt(0) val user = config.nodeConfig.rpcUsers[0]
val p = explorerController.process( val p = explorerController.process(
"--host=localhost", "--host=localhost",
"--port=${config.rpcPort}", "--port=${config.nodeConfig.rpcAddress.port}",
"--username=${user.username}", "--username=${user.username}",
"--password=${user.password}") "--password=${user.password}")
.directory(explorerDir) .directory(explorerDir.toFile())
.start() .start()
process = p process = p
log.info("Launched Node Explorer for '{}'", config.legalName) log.info("Launched Node Explorer for '{}'", legalName)
// Close these streams because no-one is using them. // Close these streams because no-one is using them.
safeClose(p.outputStream) safeClose(p.outputStream)
@ -57,21 +59,21 @@ class Explorer internal constructor(private val explorerController: ExplorerCont
process = null process = null
if (errors.isEmpty()) { if (errors.isEmpty()) {
log.info("Node Explorer for '{}' has exited (value={})", config.legalName, exitValue) log.info("Node Explorer for '{}' has exited (value={})", legalName, exitValue)
} else { } else {
log.error("Node Explorer for '{}' has exited (value={}, {})", config.legalName, exitValue, errors) log.error("Node Explorer for '{}' has exited (value={}, {})", legalName, exitValue, errors)
} }
onExit(config) onExit(config)
} }
} catch (e: IOException) { } catch (e: IOException) {
log.error("Failed to launch Node Explorer for '{}': {}", config.legalName, e.message) log.error("Failed to launch Node Explorer for '{}': {}", legalName, e.message)
onExit(config) onExit(config)
throw e throw e
} }
} }
private fun installApps(config: NodeConfig) { private fun installApps(config: NodeConfigWrapper) {
// Make sure that the explorer has cordapps on its class path. This is only necessary because currently apps // Make sure that the explorer has cordapps on its class path. This is only necessary because currently apps
// require the original class files to deserialise states: Kryo serialisation doesn't let us write generic // require the original class files to deserialise states: Kryo serialisation doesn't let us write generic
// tools that work with serialised data structures. But the AMQP serialisation revamp will fix this by // tools that work with serialised data structures. But the AMQP serialisation revamp will fix this by

View File

@ -1,76 +1,46 @@
package net.corda.demobench.model package net.corda.demobench.model
import com.typesafe.config.Config import com.typesafe.config.Config
import net.corda.core.identity.CordaX500Name import net.corda.core.internal.div
import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.NetworkHostAndPort
import net.corda.nodeapi.config.parseAs
import tornadofx.* import tornadofx.*
import java.io.IOException import java.io.IOException
import java.nio.file.Files import java.nio.file.Files
import java.nio.file.Path import java.nio.file.Path
class InstallFactory : Controller() { class InstallFactory : Controller() {
private val nodeController by inject<NodeController>() private val nodeController by inject<NodeController>()
private val serviceController by inject<ServiceController>()
@Throws(IOException::class) @Throws(IOException::class)
fun toInstallConfig(config: Config, baseDir: Path): InstallConfig { fun toInstallConfig(config: Config, baseDir: Path): InstallConfig {
val p2pPort = config.parsePort("p2pAddress") fun NetworkHostAndPort.checkPort() {
val rpcPort = config.parsePort("rpcAddress") require(nodeController.isPortValid(port)) { "Invalid port $port" }
val webPort = config.parsePort("webAddress") }
val h2Port = config.getInt("h2port")
val x500name = CordaX500Name.parse(config.getString("myLegalName")) val nodeConfig = config.parseAs<NodeConfig>()
val extraServices = config.parseExtraServices("extraAdvertisedServiceIds") nodeConfig.p2pAddress.checkPort()
nodeConfig.rpcAddress.checkPort()
nodeConfig.webAddress.checkPort()
val tempDir = Files.createTempDirectory(baseDir, ".node") val tempDir = Files.createTempDirectory(baseDir, ".node")
val nodeConfig = NodeConfig( if (nodeConfig.isNetworkMap) {
tempDir, log.info("Node '${nodeConfig.myLegalName}' is the network map")
x500name,
p2pPort,
rpcPort,
webPort,
h2Port,
extraServices,
config.getObjectList("rpcUsers").map { toUser(it.unwrapped()) }.toList()
)
if (config.hasPath("networkMapService")) {
val nmap = config.getConfig("networkMapService")
nodeConfig.networkMap = NetworkMapConfig(CordaX500Name.parse(nmap.getString("legalName")), nmap.parsePort("address"))
} else {
log.info("Node '${nodeConfig.legalName}' is the network map")
} }
return InstallConfig(tempDir, nodeConfig) return InstallConfig(tempDir, NodeConfigWrapper(tempDir, nodeConfig))
} }
private fun Config.parsePort(path: String): Int {
val address = this.getString(path)
val port = NetworkHostAndPort.parse(address).port
require(nodeController.isPortValid(port), { "Invalid port $port from '$path'." })
return port
}
private fun Config.parseExtraServices(path: String): MutableList<String> {
val services = serviceController.services.values.toSortedSet()
return this.getStringList(path)
.filter { !it.isNullOrEmpty() }
.map { svc ->
require(svc in services, { "Unknown service '$svc'." })
svc
}.toMutableList()
}
} }
/** /**
* Wraps the configuration information for a Node * Wraps the configuration information for a Node
* which isn't ready to be instantiated yet. * which isn't ready to be instantiated yet.
*/ */
class InstallConfig internal constructor(val baseDir: Path, private val config: NodeConfig) : HasPlugins { class InstallConfig internal constructor(val baseDir: Path, private val config: NodeConfigWrapper) : HasPlugins {
val key = config.key val key = config.key
override val pluginDir: Path = baseDir.resolve("plugins") override val pluginDir: Path = baseDir / "plugins"
fun deleteBaseDir(): Boolean = baseDir.toFile().deleteRecursively() fun deleteBaseDir(): Boolean = baseDir.toFile().deleteRecursively()
fun installTo(installDir: Path) = config.moveTo(installDir) fun installTo(installDir: Path) = config.copy(baseDir = installDir)
} }

View File

@ -1,10 +0,0 @@
package net.corda.demobench.model
import net.corda.core.identity.CordaX500Name
open class NetworkMapConfig(val legalName: CordaX500Name, val p2pPort: Int) {
val key: String = legalName.organisation.toKey()
}
fun String.stripWhitespace() = String(this.filter { !it.isWhitespace() }.toCharArray())
fun String.toKey() = stripWhitespace().toLowerCase()

View File

@ -1,87 +1,77 @@
package net.corda.demobench.model package net.corda.demobench.model
import com.typesafe.config.* import com.typesafe.config.ConfigRenderOptions
import net.corda.core.identity.CordaX500Name import net.corda.core.identity.CordaX500Name
import net.corda.core.internal.copyToDirectory
import net.corda.core.internal.createDirectories
import net.corda.core.internal.div
import net.corda.core.utilities.NetworkHostAndPort
import net.corda.nodeapi.User import net.corda.nodeapi.User
import java.io.File import net.corda.nodeapi.config.toConfig
import java.nio.file.Files
import java.nio.file.Path import java.nio.file.Path
import java.nio.file.StandardCopyOption import java.nio.file.StandardCopyOption
class NodeConfig constructor( /**
baseDir: Path, * This is a subset of FullNodeConfiguration, containing only those configs which we need. The node uses reference.conf
legalName: CordaX500Name, * to fill in the defaults so we're not required to specify them here.
p2pPort: Int, */
val rpcPort: Int, data class NodeConfig(
val webPort: Int, val myLegalName: CordaX500Name,
val h2Port: Int, val p2pAddress: NetworkHostAndPort,
val extraServices: MutableList<String> = mutableListOf(), val rpcAddress: NetworkHostAndPort,
val users: List<User> = listOf(defaultUser), /** This is not used by the node but by the webserver which looks at node.conf. */
var networkMap: NetworkMapConfig? = null val webAddress: NetworkHostAndPort,
) : NetworkMapConfig(legalName, p2pPort), HasPlugins { val notary: NotaryService?,
val networkMapService: NetworkMapConfig?,
val h2port: Int,
val rpcUsers: List<User> = listOf(defaultUser),
/** This is an extra config used by the Cash app. */
val issuableCurrencies: List<String> = emptyList()
) {
companion object { companion object {
val renderOptions: ConfigRenderOptions = ConfigRenderOptions.defaults().setOriginComments(false) val renderOptions: ConfigRenderOptions = ConfigRenderOptions.defaults().setOriginComments(false)
val defaultUser = user("guest") val defaultUser = user("guest")
} }
val nearestCity: String = legalName.locality @Suppress("unused")
val nodeDir: Path = baseDir.resolve(key) private val detectPublicIp = false
override val pluginDir: Path = nodeDir.resolve("plugins") @Suppress("unused")
val explorerDir: Path = baseDir.resolve("$key-explorer") private val useTestClock = true
val isNetworkMap: Boolean get() = networkMapService == null
fun toText(): String = toConfig().root().render(renderOptions)
}
/**
* This is a mirror of NetworkMapInfo.
*/
data class NetworkMapConfig(val legalName: CordaX500Name, val address: NetworkHostAndPort)
/**
* This is a subset of NotaryConfig. It implements [ExtraService] to avoid unnecessary copying.
*/
data class NotaryService(val validating: Boolean) : ExtraService {
override fun toString(): String = "${if (validating) "V" else "Non-v"}alidating Notary"
}
// TODO Think of a better name
data class NodeConfigWrapper(val baseDir: Path, val nodeConfig: NodeConfig) : HasPlugins {
val key: String = nodeConfig.myLegalName.organisation.toKey()
val nodeDir: Path = baseDir / key
val explorerDir: Path = baseDir / "$key-explorer"
override val pluginDir: Path = nodeDir / "plugins"
var state: NodeState = NodeState.STARTING var state: NodeState = NodeState.STARTING
val isCashIssuer: Boolean = extraServices.any { fun install(cordapps: Collection<Path>) {
it.startsWith("corda.issuer.") if (cordapps.isEmpty()) return
} pluginDir.createDirectories()
for (cordapp in cordapps) {
fun isNetworkMap(): Boolean = networkMap == null cordapp.copyToDirectory(pluginDir, StandardCopyOption.REPLACE_EXISTING)
/*
* The configuration object depends upon the networkMap,
* which is mutable.
*/
fun toFileConfig(): Config {
return ConfigFactory.empty()
.withValue("myLegalName", valueFor(legalName.toString()))
.withValue("p2pAddress", addressValueFor(p2pPort))
.withValue("extraAdvertisedServiceIds", valueFor(extraServices))
.withFallback(optional("networkMapService", networkMap, { c, n ->
c.withValue("address", addressValueFor(n.p2pPort))
.withValue("legalName", valueFor(n.legalName.toString()))
}))
.withValue("webAddress", addressValueFor(webPort))
.withValue("rpcAddress", addressValueFor(rpcPort))
.withValue("rpcUsers", valueFor(users.map(User::toMap).toList()))
.withValue("h2port", valueFor(h2Port))
.withValue("useTestClock", valueFor(true))
.withValue("detectPublicIp", valueFor(false))
}
fun toText(): String = toFileConfig().root().render(renderOptions)
fun moveTo(baseDir: Path) = NodeConfig(
baseDir, legalName, p2pPort, rpcPort, webPort, h2Port, extraServices, users, networkMap
)
fun install(plugins: Collection<Path>) {
if (plugins.isNotEmpty() && pluginDir.toFile().forceDirectory()) {
plugins.forEach {
Files.copy(it, pluginDir.resolve(it.fileName.toString()), StandardCopyOption.REPLACE_EXISTING)
} }
} }
} }
} fun user(name: String) = User(name, "letmein", setOf("ALL"))
private fun <T> valueFor(any: T): ConfigValue? = ConfigValueFactory.fromAnyRef(any) fun String.toKey() = filter { !it.isWhitespace() }.toLowerCase()
private fun addressValueFor(port: Int) = valueFor("localhost:$port")
private inline fun <T> optional(path: String, obj: T?, body: (Config, T) -> Config): Config {
val config = ConfigFactory.empty()
return if (obj == null) config else body(config, obj).atPath(path)
}
fun File.forceDirectory(): Boolean = this.isDirectory || this.mkdirs()

View File

@ -1,6 +1,12 @@
package net.corda.demobench.model package net.corda.demobench.model
import javafx.beans.binding.IntegerExpression
import net.corda.core.identity.CordaX500Name import net.corda.core.identity.CordaX500Name
import net.corda.core.internal.copyToDirectory
import net.corda.core.internal.createDirectories
import net.corda.core.internal.div
import net.corda.core.internal.noneOrSingle
import net.corda.core.utilities.NetworkHostAndPort
import net.corda.demobench.plugin.PluginController import net.corda.demobench.plugin.PluginController
import net.corda.demobench.pty.R3Pty import net.corda.demobench.pty.R3Pty
import tornadofx.* import tornadofx.*
@ -22,18 +28,17 @@ class NodeController(check: atRuntime = ::checkExists) : Controller() {
private val jvm by inject<JVMConfig>() private val jvm by inject<JVMConfig>()
private val pluginController by inject<PluginController>() private val pluginController by inject<PluginController>()
private val serviceController by inject<ServiceController>()
private var baseDir: Path = baseDirFor(ManagementFactory.getRuntimeMXBean().startTime) private var baseDir: Path = baseDirFor(ManagementFactory.getRuntimeMXBean().startTime)
private val cordaPath: Path = jvm.applicationDir.resolve("corda").resolve("corda.jar") private val cordaPath: Path = jvm.applicationDir.resolve("corda").resolve("corda.jar")
private val command = jvm.commandFor(cordaPath).toTypedArray() private val command = jvm.commandFor(cordaPath).toTypedArray()
private val nodes = LinkedHashMap<String, NodeConfig>() private val nodes = LinkedHashMap<String, NodeConfigWrapper>()
private val port = AtomicInteger(firstPort) private val port = AtomicInteger(firstPort)
private var networkMapConfig: NetworkMapConfig? = null private var networkMapConfig: NetworkMapConfig? = null
val activeNodes: List<NodeConfig> val activeNodes: List<NodeConfigWrapper>
get() = nodes.values.filter { get() = nodes.values.filter {
(it.state == NodeState.RUNNING) || (it.state == NodeState.STARTING) (it.state == NodeState.RUNNING) || (it.state == NodeState.STARTING)
} }
@ -50,38 +55,45 @@ class NodeController(check: atRuntime = ::checkExists) : Controller() {
/** /**
* Validate a Node configuration provided by [net.corda.demobench.views.NodeTabView]. * Validate a Node configuration provided by [net.corda.demobench.views.NodeTabView].
*/ */
fun validate(nodeData: NodeData): NodeConfig? { fun validate(nodeData: NodeData): NodeConfigWrapper? {
fun IntegerExpression.toLocalAddress() = NetworkHostAndPort("localhost", value)
val location = nodeData.nearestCity.value val location = nodeData.nearestCity.value
val config = NodeConfig( val nodeConfig = NodeConfig(
baseDir, myLegalName = CordaX500Name(
CordaX500Name(
organisation = nodeData.legalName.value.trim(), organisation = nodeData.legalName.value.trim(),
locality = location.description, locality = location.description,
country = location.countryCode country = location.countryCode
), ),
nodeData.p2pPort.value, p2pAddress = nodeData.p2pPort.toLocalAddress(),
nodeData.rpcPort.value, rpcAddress = nodeData.rpcPort.toLocalAddress(),
nodeData.webPort.value, webAddress = nodeData.webPort.toLocalAddress(),
nodeData.h2Port.value, notary = nodeData.extraServices.filterIsInstance<NotaryService>().noneOrSingle(),
nodeData.extraServices.map { serviceController.services[it]!! }.toMutableList() networkMapService = networkMapConfig, // The first node becomes the network map
h2port = nodeData.h2Port.value,
issuableCurrencies = nodeData.extraServices.filterIsInstance<CurrencyIssuer>().map { it.currency.toString() }
) )
if (nodes.putIfAbsent(config.key, config) != null) { val wrapper = NodeConfigWrapper(baseDir, nodeConfig)
log.warning("Node with key '${config.key}' already exists.")
if (nodes.putIfAbsent(wrapper.key, wrapper) != null) {
log.warning("Node with key '${wrapper.key}' already exists.")
return null return null
} }
// The first node becomes our network map if (nodeConfig.isNetworkMap) {
chooseNetworkMap(config) networkMapConfig = nodeConfig.let { NetworkMapConfig(it.myLegalName, it.p2pAddress) }
log.info("Network map provided by: ${nodeConfig.myLegalName}")
return config
} }
fun dispose(config: NodeConfig) { return wrapper
}
fun dispose(config: NodeConfigWrapper) {
config.state = NodeState.DEAD config.state = NodeState.DEAD
if (config.networkMap == null) { if (config.nodeConfig.isNetworkMap) {
log.warning("Network map service (Node '${config.legalName}') has exited.") log.warning("Network map service (Node '${config.nodeConfig.myLegalName}') has exited.")
} }
} }
@ -95,41 +107,28 @@ class NodeController(check: atRuntime = ::checkExists) : Controller() {
fun hasNetworkMap(): Boolean = networkMapConfig != null fun hasNetworkMap(): Boolean = networkMapConfig != null
private fun chooseNetworkMap(config: NodeConfig) { fun runCorda(pty: R3Pty, config: NodeConfigWrapper): Boolean {
if (hasNetworkMap()) {
config.networkMap = networkMapConfig
} else {
networkMapConfig = config
log.info("Network map provided by: ${config.legalName}")
}
}
fun runCorda(pty: R3Pty, config: NodeConfig): Boolean {
val nodeDir = config.nodeDir.toFile()
if (nodeDir.forceDirectory()) {
try { try {
config.nodeDir.createDirectories()
// Install any built-in plugins into the working directory. // Install any built-in plugins into the working directory.
pluginController.populate(config) pluginController.populate(config)
// Write this node's configuration file into its working directory. // Write this node's configuration file into its working directory.
val confFile = nodeDir.resolve("node.conf") val confFile = config.nodeDir / "node.conf"
confFile.writeText(config.toText()) Files.write(confFile, config.nodeConfig.toText().toByteArray())
// Execute the Corda node // Execute the Corda node
val cordaEnv = System.getenv().toMutableMap().apply { val cordaEnv = System.getenv().toMutableMap().apply {
jvm.setCapsuleCacheDir(this) jvm.setCapsuleCacheDir(this)
} }
pty.run(command, cordaEnv, nodeDir.toString()) pty.run(command, cordaEnv, config.nodeDir.toString())
log.info("Launched node: ${config.legalName}") log.info("Launched node: ${config.nodeConfig.myLegalName}")
return true return true
} catch (e: Exception) { } catch (e: Exception) {
log.log(Level.SEVERE, "Failed to launch Corda: ${e.message}", e) log.log(Level.SEVERE, "Failed to launch Corda: ${e.message}", e)
return false return false
} }
} else {
return false
}
} }
fun reset() { fun reset() {
@ -144,15 +143,15 @@ class NodeController(check: atRuntime = ::checkExists) : Controller() {
/** /**
* Add a [NodeConfig] object that has been loaded from a profile. * Add a [NodeConfig] object that has been loaded from a profile.
*/ */
fun register(config: NodeConfig): Boolean { fun register(config: NodeConfigWrapper): Boolean {
if (nodes.putIfAbsent(config.key, config) != null) { if (nodes.putIfAbsent(config.key, config) != null) {
return false return false
} }
updatePort(config) updatePort(config.nodeConfig)
if ((networkMapConfig == null) && config.isNetworkMap()) { if (networkMapConfig == null && config.nodeConfig.isNetworkMap) {
networkMapConfig = config networkMapConfig = config.nodeConfig.let { NetworkMapConfig(it.myLegalName, it.p2pAddress) }
} }
return true return true
@ -162,12 +161,12 @@ class NodeController(check: atRuntime = ::checkExists) : Controller() {
* Creates a node directory that can host a running instance of Corda. * Creates a node directory that can host a running instance of Corda.
*/ */
@Throws(IOException::class) @Throws(IOException::class)
fun install(config: InstallConfig): NodeConfig { fun install(config: InstallConfig): NodeConfigWrapper {
val installed = config.installTo(baseDir) val installed = config.installTo(baseDir)
pluginController.userPluginsFor(config).forEach { pluginController.userPluginsFor(config).forEach {
val pluginDir = Files.createDirectories(installed.pluginDir) installed.pluginDir.createDirectories()
val plugin = Files.copy(it, pluginDir.resolve(it.fileName.toString())) val plugin = it.copyToDirectory(installed.pluginDir)
log.info("Installed: $plugin") log.info("Installed: $plugin")
} }
@ -179,7 +178,7 @@ class NodeController(check: atRuntime = ::checkExists) : Controller() {
} }
private fun updatePort(config: NodeConfig) { private fun updatePort(config: NodeConfig) {
val nextPort = 1 + arrayOf(config.p2pPort, config.rpcPort, config.webPort, config.h2Port).max() as Int val nextPort = 1 + arrayOf(config.p2pAddress.port, config.rpcAddress.port, config.webAddress.port, config.h2port).max() as Int
port.getAndUpdate { Math.max(nextPort, it) } port.getAndUpdate { Math.max(nextPort, it) }
} }

View File

@ -4,8 +4,10 @@ import javafx.beans.property.SimpleIntegerProperty
import javafx.beans.property.SimpleListProperty import javafx.beans.property.SimpleListProperty
import javafx.beans.property.SimpleObjectProperty import javafx.beans.property.SimpleObjectProperty
import javafx.beans.property.SimpleStringProperty import javafx.beans.property.SimpleStringProperty
import javafx.collections.FXCollections.observableArrayList
import net.corda.finance.utils.CityDatabase import net.corda.finance.utils.CityDatabase
import tornadofx.* import tornadofx.*
import java.util.*
object SuggestedDetails { object SuggestedDetails {
val banks = listOf( val banks = listOf(
@ -35,7 +37,7 @@ class NodeData {
val rpcPort = SimpleIntegerProperty() val rpcPort = SimpleIntegerProperty()
val webPort = SimpleIntegerProperty() val webPort = SimpleIntegerProperty()
val h2Port = SimpleIntegerProperty() val h2Port = SimpleIntegerProperty()
val extraServices = SimpleListProperty(mutableListOf<String>().observable()) val extraServices = SimpleListProperty(observableArrayList<ExtraService>())
} }
class NodeDataModel : ItemViewModel<NodeData>(NodeData()) { class NodeDataModel : ItemViewModel<NodeData>(NodeData()) {
@ -46,3 +48,9 @@ class NodeDataModel : ItemViewModel<NodeData>(NodeData()) {
val webPort = bind { item?.webPort } val webPort = bind { item?.webPort }
val h2Port = bind { item?.h2Port } val h2Port = bind { item?.h2Port }
} }
interface ExtraService
data class CurrencyIssuer(val currency: Currency) : ExtraService {
override fun toString(): String = "Issuer $currency"
}

View File

@ -1,44 +0,0 @@
package net.corda.demobench.model
import tornadofx.*
import java.io.IOException
import java.io.InputStreamReader
import java.net.URL
import java.util.logging.Level
class ServiceController(resourceName: String = "/services.conf") : Controller() {
val services: Map<String, String> = loadConf(resources.url(resourceName))
val notaries: Map<String, String> = services.filter { it.value.startsWith("corda.notary.") }
val issuers: Map<String, String> = services.filter { it.value.startsWith("corda.issuer.") }
/*
* Load our list of known extra Corda services.
*/
private fun loadConf(url: URL?): Map<String, String> {
return if (url == null) {
emptyMap()
} else {
try {
val map = linkedMapOf<String, String>()
InputStreamReader(url.openStream()).useLines { sq ->
sq.forEach { line ->
val service = line.split(":").map { it.trim() }
if (service.size != 2) {
log.warning("Encountered corrupted line '$line' while reading services from config: $url")
} else {
map[service[1]] = service[0]
log.info("Supports: $service")
}
}
map
}
} catch (e: IOException) {
log.log(Level.SEVERE, "Failed to load $url: ${e.message}", e)
emptyMap<String, String>()
}
}
}
}

View File

@ -1,15 +0,0 @@
@file:JvmName("User")
package net.corda.demobench.model
import net.corda.core.internal.uncheckedCast
import net.corda.nodeapi.User
import java.util.*
fun toUser(map: Map<String, Any>) = User(
map.getOrElse("username", { "none" }) as String,
map.getOrElse("password", { "none" }) as String,
LinkedHashSet(uncheckedCast<Any, Collection<String>>(map.getOrElse("permissions", { emptyList<String>() })))
)
fun user(name: String) = User(name, "letmein", setOf("ALL"))

View File

@ -5,7 +5,7 @@ import net.corda.core.internal.createDirectories
import net.corda.core.internal.exists import net.corda.core.internal.exists
import net.corda.demobench.model.HasPlugins import net.corda.demobench.model.HasPlugins
import net.corda.demobench.model.JVMConfig import net.corda.demobench.model.JVMConfig
import net.corda.demobench.model.NodeConfig import net.corda.demobench.model.NodeConfigWrapper
import tornadofx.* import tornadofx.*
import java.io.IOException import java.io.IOException
import java.nio.file.Files import java.nio.file.Files
@ -24,16 +24,14 @@ class PluginController : Controller() {
* Install any built-in plugins that this node requires. * Install any built-in plugins that this node requires.
*/ */
@Throws(IOException::class) @Throws(IOException::class)
fun populate(config: NodeConfig) { fun populate(config: NodeConfigWrapper) {
if (!config.pluginDir.exists()) {
config.pluginDir.createDirectories() config.pluginDir.createDirectories()
}
if (finance.exists()) { if (finance.exists()) {
finance.copyToDirectory(config.pluginDir, StandardCopyOption.REPLACE_EXISTING) finance.copyToDirectory(config.pluginDir, StandardCopyOption.REPLACE_EXISTING)
log.info("Installed 'Finance' plugin") log.info("Installed 'Finance' plugin")
} }
// Nodes cannot issue cash unless they contain the "Bank of Corda" plugin. // Nodes cannot issue cash unless they contain the "Bank of Corda" plugin.
if (config.isCashIssuer && bankOfCorda.exists()) { if (config.nodeConfig.issuableCurrencies.isNotEmpty() && bankOfCorda.exists()) {
bankOfCorda.copyToDirectory(config.pluginDir, StandardCopyOption.REPLACE_EXISTING) bankOfCorda.copyToDirectory(config.pluginDir, StandardCopyOption.REPLACE_EXISTING)
log.info("Installed 'Bank of Corda' plugin") log.info("Installed 'Bank of Corda' plugin")
} }

View File

@ -4,6 +4,8 @@ import com.typesafe.config.Config
import com.typesafe.config.ConfigFactory import com.typesafe.config.ConfigFactory
import javafx.stage.FileChooser import javafx.stage.FileChooser
import javafx.stage.FileChooser.ExtensionFilter import javafx.stage.FileChooser.ExtensionFilter
import net.corda.core.internal.createDirectories
import net.corda.core.internal.div
import net.corda.demobench.model.InstallConfig import net.corda.demobench.model.InstallConfig
import net.corda.demobench.model.InstallFactory import net.corda.demobench.model.InstallFactory
import net.corda.demobench.model.JVMConfig import net.corda.demobench.model.JVMConfig
@ -58,8 +60,8 @@ class ProfileController : Controller() {
FileSystems.newFileSystem(URI.create("jar:" + target.toURI()), mapOf("create" to "true")).use { fs -> FileSystems.newFileSystem(URI.create("jar:" + target.toURI()), mapOf("create" to "true")).use { fs ->
configs.forEach { config -> configs.forEach { config ->
// Write the configuration file. // Write the configuration file.
val nodeDir = Files.createDirectories(fs.getPath(config.key)) val nodeDir = fs.getPath(config.key).createDirectories()
val file = Files.write(nodeDir.resolve("node.conf"), config.toText().toByteArray(UTF_8)) val file = Files.write(nodeDir / "node.conf", config.nodeConfig.toText().toByteArray(UTF_8))
log.info("Wrote: $file") log.info("Wrote: $file")
// Write all of the non-built-in plugins. // Write all of the non-built-in plugins.

View File

@ -5,18 +5,17 @@ import net.corda.client.rpc.CordaRPCConnection
import net.corda.core.messaging.CordaRPCOps import net.corda.core.messaging.CordaRPCOps
import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.NetworkHostAndPort
import net.corda.core.utilities.loggerFor import net.corda.core.utilities.loggerFor
import net.corda.demobench.model.NodeConfig import net.corda.demobench.model.NodeConfigWrapper
import java.util.* import java.util.*
import java.util.concurrent.TimeUnit.SECONDS import java.util.concurrent.TimeUnit.SECONDS
class NodeRPC(config: NodeConfig, start: (NodeConfig, CordaRPCOps) -> Unit, invoke: (CordaRPCOps) -> Unit) : AutoCloseable { class NodeRPC(config: NodeConfigWrapper, start: (NodeConfigWrapper, CordaRPCOps) -> Unit, invoke: (CordaRPCOps) -> Unit) : AutoCloseable {
private companion object { private companion object {
val log = loggerFor<NodeRPC>() val log = loggerFor<NodeRPC>()
val oneSecond = SECONDS.toMillis(1) val oneSecond = SECONDS.toMillis(1)
} }
private val rpcClient = CordaRPCClient(NetworkHostAndPort("localhost", config.rpcPort)) private val rpcClient = CordaRPCClient(NetworkHostAndPort("localhost", config.nodeConfig.rpcAddress.port))
private var rpcConnection: CordaRPCConnection? = null private var rpcConnection: CordaRPCConnection? = null
private val timer = Timer() private val timer = Timer()
@ -24,7 +23,7 @@ class NodeRPC(config: NodeConfig, start: (NodeConfig, CordaRPCOps) -> Unit, invo
val setupTask = object : TimerTask() { val setupTask = object : TimerTask() {
override fun run() { override fun run() {
try { try {
val user = config.users.elementAt(0) val user = config.nodeConfig.rpcUsers[0]
val connection = rpcClient.start(user.username, user.password) val connection = rpcClient.start(user.username, user.password)
rpcConnection = connection rpcConnection = connection
val ops = connection.proxy val ops = connection.proxy
@ -42,7 +41,7 @@ class NodeRPC(config: NodeConfig, start: (NodeConfig, CordaRPCOps) -> Unit, invo
} }
}, 0, oneSecond) }, 0, oneSecond)
} catch (e: Exception) { } catch (e: Exception) {
log.warn("Node '{}' not ready yet (Error: {})", config.legalName, e.message) log.warn("Node '{}' not ready yet (Error: {})", config.nodeConfig.myLegalName, e.message)
} }
} }
} }

View File

@ -16,10 +16,14 @@ import javafx.scene.layout.Priority
import javafx.stage.FileChooser import javafx.stage.FileChooser
import javafx.util.StringConverter import javafx.util.StringConverter
import net.corda.core.internal.* import net.corda.core.internal.*
import net.corda.finance.utils.CityDatabase
import net.corda.finance.utils.WorldMapLocation
import net.corda.demobench.model.* import net.corda.demobench.model.*
import net.corda.demobench.ui.CloseableTab import net.corda.demobench.ui.CloseableTab
import net.corda.finance.CHF
import net.corda.finance.EUR
import net.corda.finance.GBP
import net.corda.finance.USD
import net.corda.finance.utils.CityDatabase
import net.corda.finance.utils.WorldMapLocation
import org.controlsfx.control.CheckListView import org.controlsfx.control.CheckListView
import tornadofx.* import tornadofx.*
import java.nio.file.Path import java.nio.file.Path
@ -39,10 +43,10 @@ class NodeTabView : Fragment() {
val cordappPathsFile: Path = jvm.dataHome / "cordapp-paths.txt" val cordappPathsFile: Path = jvm.dataHome / "cordapp-paths.txt"
fun loadDefaultCordappPaths(): MutableList<Path> { fun loadDefaultCordappPaths(): MutableList<Path> {
if (cordappPathsFile.exists()) return if (cordappPathsFile.exists())
return cordappPathsFile.readAllLines().map { Paths.get(it) }.filter { it.exists() }.toMutableList() cordappPathsFile.readAllLines().map { Paths.get(it) }.filter { it.exists() }.toMutableList()
else else
return ArrayList() ArrayList()
} }
// This is shared between tabs. // This is shared between tabs.
@ -58,11 +62,9 @@ class NodeTabView : Fragment() {
} }
private val nodeController by inject<NodeController>() private val nodeController by inject<NodeController>()
private val serviceController by inject<ServiceController>()
private val chooser = FileChooser() private val chooser = FileChooser()
private val model = NodeDataModel() private val model = NodeDataModel()
private val availableServices: List<String> = if (nodeController.hasNetworkMap()) serviceController.issuers.keys.toList() else serviceController.notaries.keys.toList()
private val nodeTerminalView = find<NodeTerminalView>() private val nodeTerminalView = find<NodeTerminalView>()
private val nodeConfigView = stackpane { private val nodeConfigView = stackpane {
@ -112,8 +114,13 @@ class NodeTabView : Fragment() {
fieldset("Additional configuration") { fieldset("Additional configuration") {
styleClass.addAll("services-panel") styleClass.addAll("services-panel")
val extraServices = if (nodeController.hasNetworkMap()) {
listOf(USD, GBP, CHF, EUR).map { CurrencyIssuer(it) }
} else {
listOf(NotaryService(true), NotaryService(false))
}
val servicesList = CheckListView(availableServices.observable()).apply { val servicesList = CheckListView(extraServices.observable()).apply {
vboxConstraints { vGrow = Priority.ALWAYS } vboxConstraints { vGrow = Priority.ALWAYS }
model.item.extraServices.set(checkModel.checkedItems) model.item.extraServices.set(checkModel.checkedItems)
if (!nodeController.hasNetworkMap()) { if (!nodeController.hasNetworkMap()) {
@ -263,17 +270,17 @@ class NodeTabView : Fragment() {
/** /**
* Launches a preconfigured Corda node, e.g. from a saved profile. * Launches a preconfigured Corda node, e.g. from a saved profile.
*/ */
fun launch(config: NodeConfig) { fun launch(config: NodeConfigWrapper) {
nodeController.register(config) nodeController.register(config)
launchNode(config) launchNode(config)
} }
private fun launchNode(config: NodeConfig) { private fun launchNode(config: NodeConfigWrapper) {
val countryCode = CityDatabase.cityMap[config.nearestCity]?.countryCode val countryCode = CityDatabase.cityMap[config.nodeConfig.myLegalName.locality]?.countryCode
if (countryCode != null) { if (countryCode != null) {
nodeTab.graphic = ImageView(flags.get()[countryCode]).apply { fitWidth = 24.0; isPreserveRatio = true } nodeTab.graphic = ImageView(flags.get()[countryCode]).apply { fitWidth = 24.0; isPreserveRatio = true }
} }
nodeTab.text = config.legalName.organisation nodeTab.text = config.nodeConfig.myLegalName.organisation
nodeTerminalView.open(config) { exitCode -> nodeTerminalView.open(config) { exitCode ->
Platform.runLater { Platform.runLater {
if (exitCode == 0) { if (exitCode == 0) {

View File

@ -20,6 +20,7 @@ import net.corda.core.messaging.vaultTrackBy
import net.corda.core.node.services.vault.PageSpecification import net.corda.core.node.services.vault.PageSpecification
import net.corda.demobench.explorer.ExplorerController import net.corda.demobench.explorer.ExplorerController
import net.corda.demobench.model.NodeConfig import net.corda.demobench.model.NodeConfig
import net.corda.demobench.model.NodeConfigWrapper
import net.corda.demobench.model.NodeController import net.corda.demobench.model.NodeController
import net.corda.demobench.model.NodeState import net.corda.demobench.model.NodeState
import net.corda.demobench.pty.R3Pty import net.corda.demobench.pty.R3Pty
@ -69,8 +70,8 @@ class NodeTerminalView : Fragment() {
private lateinit var logo: ImageView private lateinit var logo: ImageView
private lateinit var swingTerminal: SwingNode private lateinit var swingTerminal: SwingNode
fun open(config: NodeConfig, onExit: (Int) -> Unit) { fun open(config: NodeConfigWrapper, onExit: (Int) -> Unit) {
nodeName.text = config.legalName.organisation nodeName.text = config.nodeConfig.myLegalName.organisation
swingTerminal = SwingNode() swingTerminal = SwingNode()
swingTerminal.setOnMouseClicked { swingTerminal.setOnMouseClicked {
@ -85,13 +86,13 @@ class NodeTerminalView : Fragment() {
root.isVisible = true root.isVisible = true
SwingUtilities.invokeLater({ SwingUtilities.invokeLater({
val r3pty = R3Pty(config.legalName, TerminalSettingsProvider(), Dimension(160, 80), onExit) val r3pty = R3Pty(config.nodeConfig.myLegalName, TerminalSettingsProvider(), Dimension(160, 80), onExit)
pty = r3pty pty = r3pty
if (nodeController.runCorda(r3pty, config)) { if (nodeController.runCorda(r3pty, config)) {
swingTerminal.content = r3pty.terminal swingTerminal.content = r3pty.terminal
configureDatabaseButton(config) configureDatabaseButton(config.nodeConfig)
configureExplorerButton(config) configureExplorerButton(config)
configureWebButton(config) configureWebButton(config)
@ -105,7 +106,7 @@ class NodeTerminalView : Fragment() {
* and close the RPC client if it has. * and close the RPC client if it has.
*/ */
if (!r3pty.isConnected) { if (!r3pty.isConnected) {
log.severe("Node '${config.legalName}' has failed to start.") log.severe("Node '${config.nodeConfig.myLegalName}' has failed to start.")
swingTerminal.content = null swingTerminal.content = null
rpc?.close() rpc?.close()
} }
@ -119,7 +120,7 @@ class NodeTerminalView : Fragment() {
* launched the explorer and only reenable it when * launched the explorer and only reenable it when
* the explorer has exited. * the explorer has exited.
*/ */
private fun configureExplorerButton(config: NodeConfig) { private fun configureExplorerButton(config: NodeConfigWrapper) {
launchExplorerButton.setOnAction { launchExplorerButton.setOnAction {
launchExplorerButton.isDisable = true launchExplorerButton.isDisable = true
@ -131,7 +132,7 @@ class NodeTerminalView : Fragment() {
private fun configureDatabaseButton(config: NodeConfig) { private fun configureDatabaseButton(config: NodeConfig) {
viewDatabaseButton.setOnAction { viewDatabaseButton.setOnAction {
viewer.openBrowser(config.h2Port) viewer.openBrowser(config.h2port)
} }
} }
@ -144,7 +145,7 @@ class NodeTerminalView : Fragment() {
* launched the web server and only reenable it when * launched the web server and only reenable it when
* the web server has exited. * the web server has exited.
*/ */
private fun configureWebButton(config: NodeConfig) { private fun configureWebButton(config: NodeConfigWrapper) {
launchWebButton.setOnAction { launchWebButton.setOnAction {
if (webURL != null) { if (webURL != null) {
app.hostServices.showDocument(webURL.toString()) app.hostServices.showDocument(webURL.toString())
@ -161,12 +162,12 @@ class NodeTerminalView : Fragment() {
launchWebButton.text = "" launchWebButton.text = ""
launchWebButton.graphic = ProgressIndicator() launchWebButton.graphic = ProgressIndicator()
log.info("Starting web server for ${config.legalName}") log.info("Starting web server for ${config.nodeConfig.myLegalName}")
webServer.open(config).then { webServer.open(config).then {
Platform.runLater { Platform.runLater {
launchWebButton.graphic = null launchWebButton.graphic = null
it.match(success = { it.match(success = {
log.info("Web server for ${config.legalName} started on $it") log.info("Web server for ${config.nodeConfig.myLegalName} started on $it")
webURL = it webURL = it
launchWebButton.text = "Reopen\nweb site" launchWebButton.text = "Reopen\nweb site"
app.hostServices.showDocument(it.toString()) app.hostServices.showDocument(it.toString())
@ -178,13 +179,13 @@ class NodeTerminalView : Fragment() {
} }
} }
private fun launchRPC(config: NodeConfig) = NodeRPC( private fun launchRPC(config: NodeConfigWrapper) = NodeRPC(
config = config, config = config,
start = this::initialise, start = this::initialise,
invoke = this::pollCashBalances invoke = this::pollCashBalances
) )
private fun initialise(config: NodeConfig, ops: CordaRPCOps) { private fun initialise(config: NodeConfigWrapper, ops: CordaRPCOps) {
try { try {
val (txInit, txNext) = ops.internalVerifiedTransactionsFeed() val (txInit, txNext) = ops.internalVerifiedTransactionsFeed()
val (stateInit, stateNext) = ops.vaultTrackBy<ContractState>(paging = pageSpecification) val (stateInit, stateNext) = ops.vaultTrackBy<ContractState>(paging = pageSpecification)
@ -212,7 +213,7 @@ class NodeTerminalView : Fragment() {
} }
config.state = NodeState.RUNNING config.state = NodeState.RUNNING
log.info("Node '${config.legalName}' is now ready.") log.info("Node '${config.nodeConfig.myLegalName}' is now ready.")
header.isDisable = false header.isDisable = false
} }
@ -225,7 +226,7 @@ class NodeTerminalView : Fragment() {
) )
Platform.runLater { Platform.runLater {
balance.value = if (cashBalances.isNullOrEmpty()) "0" else cashBalances balance.value = if (cashBalances.isEmpty()) "0" else cashBalances
} }
} catch (e: ClassNotFoundException) { } catch (e: ClassNotFoundException) {
// TODO: Remove this special case once Rick's serialisation work means we can deserialise states that weren't on our own classpath. // TODO: Remove this special case once Rick's serialisation work means we can deserialise states that weren't on our own classpath.

View File

@ -2,11 +2,11 @@ package net.corda.demobench.web
import com.google.common.util.concurrent.RateLimiter import com.google.common.util.concurrent.RateLimiter
import net.corda.core.concurrent.CordaFuture import net.corda.core.concurrent.CordaFuture
import net.corda.core.utilities.minutes
import net.corda.core.internal.concurrent.openFuture import net.corda.core.internal.concurrent.openFuture
import net.corda.core.internal.until import net.corda.core.internal.until
import net.corda.core.utilities.loggerFor import net.corda.core.utilities.loggerFor
import net.corda.demobench.model.NodeConfig import net.corda.core.utilities.minutes
import net.corda.demobench.model.NodeConfigWrapper
import net.corda.demobench.readErrorLines import net.corda.demobench.readErrorLines
import java.io.IOException import java.io.IOException
import java.net.HttpURLConnection import java.net.HttpURLConnection
@ -25,7 +25,7 @@ class WebServer internal constructor(private val webServerController: WebServerC
private var process: Process? = null private var process: Process? = null
@Throws(IOException::class) @Throws(IOException::class)
fun open(config: NodeConfig): CordaFuture<URI> { fun open(config: NodeConfigWrapper): CordaFuture<URI> {
val nodeDir = config.nodeDir.toFile() val nodeDir = config.nodeDir.toFile()
if (!nodeDir.isDirectory) { if (!nodeDir.isDirectory) {
@ -33,13 +33,14 @@ class WebServer internal constructor(private val webServerController: WebServerC
return openFuture() return openFuture()
} }
val legalName = config.nodeConfig.myLegalName
try { try {
val p = webServerController.process() val p = webServerController.process()
.directory(nodeDir) .directory(nodeDir)
.start() .start()
process = p process = p
log.info("Launched Web Server for '{}'", config.legalName) log.info("Launched Web Server for '{}'", legalName)
// Close these streams because no-one is using them. // Close these streams because no-one is using them.
safeClose(p.outputStream) safeClose(p.outputStream)
@ -51,22 +52,22 @@ class WebServer internal constructor(private val webServerController: WebServerC
process = null process = null
if (errors.isEmpty()) { if (errors.isEmpty()) {
log.info("Web Server for '{}' has exited (value={})", config.legalName, exitValue) log.info("Web Server for '{}' has exited (value={})", legalName, exitValue)
} else { } else {
log.error("Web Server for '{}' has exited (value={}, {})", config.legalName, exitValue, errors) log.error("Web Server for '{}' has exited (value={}, {})", legalName, exitValue, errors)
} }
} }
val future = openFuture<URI>() val future = openFuture<URI>()
thread { thread {
future.capture { future.capture {
log.info("Waiting for web server for ${config.legalName} to start ...") log.info("Waiting for web server for $legalName to start ...")
waitForStart(config.webPort) waitForStart(config.nodeConfig.webAddress.port)
} }
} }
return future return future
} catch (e: IOException) { } catch (e: IOException) {
log.error("Failed to launch Web Server for '{}': {}", config.legalName, e.message) log.error("Failed to launch Web Server for '{}': {}", legalName, e.message)
throw e throw e
} }
} }

View File

@ -1,6 +0,0 @@
corda.notary.validating : Validating Notary
corda.notary.simple : Non-validating Notary
corda.issuer.USD : Issuer USD
corda.issuer.GBP : Issuer GBP
corda.issuer.CHF : Issuer CHF
corda.issuer.EUR : Issuer EUR

View File

@ -3,7 +3,6 @@ package net.corda.demobench
import net.corda.demobench.config.LoggingConfig import net.corda.demobench.config.LoggingConfig
import net.corda.demobench.model.JVMConfigTest import net.corda.demobench.model.JVMConfigTest
import net.corda.demobench.model.NodeControllerTest import net.corda.demobench.model.NodeControllerTest
import net.corda.demobench.model.ServiceControllerTest
import org.junit.BeforeClass import org.junit.BeforeClass
import org.junit.runner.RunWith import org.junit.runner.RunWith
import org.junit.runners.Suite import org.junit.runners.Suite
@ -13,7 +12,6 @@ import org.junit.runners.Suite
*/ */
@RunWith(Suite::class) @RunWith(Suite::class)
@Suite.SuiteClasses( @Suite.SuiteClasses(
ServiceControllerTest::class,
NodeControllerTest::class, NodeControllerTest::class,
JVMConfigTest::class JVMConfigTest::class
) )

View File

@ -1,22 +0,0 @@
package net.corda.demobench.model
import net.corda.core.identity.CordaX500Name
import org.bouncycastle.asn1.x500.X500Name
import org.junit.Ignore
import org.junit.Test
import kotlin.test.assertEquals
class NetworkMapConfigTest {
@Ignore("This has been superseded by validation logic in CordaX500Name")
@Test
fun keyValue() {
val config = NetworkMapConfig(CordaX500Name.parse("O=My\tNasty Little\rLabel\n,L=London,C=GB"), 10000)
assertEquals("mynastylittlelabel", config.key)
}
@Test
fun removeWhitespace() {
assertEquals("OneTwoThreeFour!", "One\tTwo \rThree\r\nFour!".stripWhitespace())
}
}

View File

@ -1,190 +1,30 @@
package net.corda.demobench.model package net.corda.demobench.model
import com.fasterxml.jackson.annotation.JsonInclude
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.databind.SerializationFeature
import com.typesafe.config.ConfigFactory import com.typesafe.config.ConfigFactory
import com.typesafe.config.ConfigValueFactory import com.typesafe.config.ConfigValueFactory
import net.corda.core.identity.CordaX500Name import net.corda.core.identity.CordaX500Name
import net.corda.core.internal.div
import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.NetworkHostAndPort
import net.corda.node.internal.NetworkMapInfo import net.corda.node.internal.NetworkMapInfo
import net.corda.node.services.config.FullNodeConfiguration import net.corda.node.services.config.FullNodeConfiguration
import net.corda.nodeapi.User import net.corda.nodeapi.User
import net.corda.nodeapi.config.parseAs import net.corda.nodeapi.config.parseAs
import net.corda.nodeapi.config.toConfig
import net.corda.testing.DUMMY_NOTARY import net.corda.testing.DUMMY_NOTARY
import net.corda.webserver.WebServerConfig import net.corda.webserver.WebServerConfig
import org.assertj.core.api.Assertions.assertThat
import org.junit.Test import org.junit.Test
import java.io.StringWriter
import java.nio.file.Path import java.nio.file.Path
import java.nio.file.Paths import java.nio.file.Paths
import kotlin.test.assertEquals import kotlin.test.assertEquals
import kotlin.test.assertFalse import kotlin.test.assertFalse
import kotlin.test.assertNull
import kotlin.test.assertTrue import kotlin.test.assertTrue
class NodeConfigTest { class NodeConfigTest {
companion object { companion object {
private val baseDir: Path = Paths.get(".").toAbsolutePath() private val baseDir: Path = Paths.get(".").toAbsolutePath()
private val myLegalName = CordaX500Name(organisation = "My Name", locality = "New York", country = "US") private val myLegalName = CordaX500Name(organisation = "My Name", locality = "New York", country = "US")
} }
@Test
fun `test name`() {
val config = createConfig(legalName = myLegalName)
assertEquals(myLegalName, config.legalName)
assertEquals("myname", config.key)
}
@Test
fun `test node directory`() {
val config = createConfig(legalName = myLegalName)
assertEquals(baseDir / "myname", config.nodeDir)
}
@Test
fun `test explorer directory`() {
val config = createConfig(legalName = myLegalName)
assertEquals(baseDir / "myname-explorer", config.explorerDir)
}
@Test
fun `test plugin directory`() {
val config = createConfig(legalName = myLegalName)
assertEquals(baseDir / "myname" / "plugins", config.pluginDir)
}
@Test
fun `test P2P port`() {
val config = createConfig(p2pPort = 10001)
assertEquals(10001, config.p2pPort)
}
@Test
fun `test rpc port`() {
val config = createConfig(rpcPort = 40002)
assertEquals(40002, config.rpcPort)
}
@Test
fun `test web port`() {
val config = createConfig(webPort = 20001)
assertEquals(20001, config.webPort)
}
@Test
fun `test H2 port`() {
val config = createConfig(h2Port = 30001)
assertEquals(30001, config.h2Port)
}
@Test
fun `test services`() {
val config = createConfig(services = mutableListOf("my.service"))
assertEquals(listOf("my.service"), config.extraServices)
}
@Test
fun `test users`() {
val config = createConfig(users = listOf(user("myuser")))
assertEquals(listOf(user("myuser")), config.users)
}
@Test
fun `test default state`() {
val config = createConfig()
assertEquals(NodeState.STARTING, config.state)
}
@Test
fun `test network map`() {
val config = createConfig()
assertNull(config.networkMap)
assertTrue(config.isNetworkMap())
}
@Test
fun `test cash issuer`() {
val config = createConfig(services = mutableListOf("corda.issuer.GBP"))
assertTrue(config.isCashIssuer)
}
@Test
fun `test not cash issuer`() {
val config = createConfig(services = mutableListOf("corda.issuerubbish"))
assertFalse(config.isCashIssuer)
}
/**
* Reformat JSON via Jackson to ensure a consistent format for comparison purposes.
*/
private fun prettyPrint(content: String): String {
val mapper = ObjectMapper()
mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL)
mapper.enable(SerializationFeature.INDENT_OUTPUT)
val sw = StringWriter()
val parsed = mapper.readTree(content)
mapper.writeValue(sw, parsed)
return sw.toString()
}
@Test
fun `test config text`() {
val config = createConfig(
legalName = myLegalName,
p2pPort = 10001,
rpcPort = 40002,
webPort = 20001,
h2Port = 30001,
services = mutableListOf("my.service"),
users = listOf(user("jenny"))
)
assertEquals(prettyPrint("{"
+ "\"detectPublicIp\":false,"
+ "\"extraAdvertisedServiceIds\":[\"my.service\"],"
+ "\"h2port\":30001,"
+ "\"myLegalName\":\"C=US,L=New York,O=My Name\","
+ "\"p2pAddress\":\"localhost:10001\","
+ "\"rpcAddress\":\"localhost:40002\","
+ "\"rpcUsers\":["
+ "{\"password\":\"letmein\",\"permissions\":[\"ALL\"],\"username\":\"jenny\"}"
+ "],"
+ "\"useTestClock\":true,"
+ "\"webAddress\":\"localhost:20001\""
+ "}"), prettyPrint(config.toText()))
}
@Test
fun `test config text with network map`() {
val config = createConfig(
legalName = myLegalName,
p2pPort = 10001,
rpcPort = 40002,
webPort = 20001,
h2Port = 30001,
services = mutableListOf("my.service"),
users = listOf(user("jenny"))
)
config.networkMap = NetworkMapConfig(DUMMY_NOTARY.name, 12345)
assertEquals(prettyPrint("{"
+ "\"detectPublicIp\":false,"
+ "\"extraAdvertisedServiceIds\":[\"my.service\"],"
+ "\"h2port\":30001,"
+ "\"myLegalName\":\"C=US,L=New York,O=My Name\","
+ "\"networkMapService\":{\"address\":\"localhost:12345\",\"legalName\":\"C=CH,L=Zurich,O=Notary Service\"},"
+ "\"p2pAddress\":\"localhost:10001\","
+ "\"rpcAddress\":\"localhost:40002\","
+ "\"rpcUsers\":["
+ "{\"password\":\"letmein\",\"permissions\":[\"ALL\"],\"username\":\"jenny\"}"
+ "],"
+ "\"useTestClock\":true,"
+ "\"webAddress\":\"localhost:20001\""
+ "}"), prettyPrint(config.toText()))
}
@Test @Test
fun `reading node configuration`() { fun `reading node configuration`() {
val config = createConfig( val config = createConfig(
@ -192,13 +32,13 @@ class NodeConfigTest {
p2pPort = 10001, p2pPort = 10001,
rpcPort = 40002, rpcPort = 40002,
webPort = 20001, webPort = 20001,
h2Port = 30001, h2port = 30001,
services = mutableListOf("my.service"), notary = NotaryService(validating = false),
networkMap = NetworkMapConfig(DUMMY_NOTARY.name, localPort(12345)),
users = listOf(user("jenny")) users = listOf(user("jenny"))
) )
config.networkMap = NetworkMapConfig(DUMMY_NOTARY.name, 12345)
val nodeConfig = config.toFileConfig() val nodeConfig = config.toConfig()
.withValue("baseDirectory", ConfigValueFactory.fromAnyRef(baseDir.toString())) .withValue("baseDirectory", ConfigValueFactory.fromAnyRef(baseDir.toString()))
.withFallback(ConfigFactory.parseResources("reference.conf")) .withFallback(ConfigFactory.parseResources("reference.conf"))
.resolve() .resolve()
@ -207,10 +47,9 @@ class NodeConfigTest {
assertEquals(myLegalName, fullConfig.myLegalName) assertEquals(myLegalName, fullConfig.myLegalName)
assertEquals(localPort(40002), fullConfig.rpcAddress) assertEquals(localPort(40002), fullConfig.rpcAddress)
assertEquals(localPort(10001), fullConfig.p2pAddress) assertEquals(localPort(10001), fullConfig.p2pAddress)
assertEquals(listOf("my.service"), fullConfig.extraAdvertisedServiceIds)
assertEquals(listOf(user("jenny")), fullConfig.rpcUsers) assertEquals(listOf(user("jenny")), fullConfig.rpcUsers)
assertEquals(NetworkMapInfo(localPort(12345), DUMMY_NOTARY.name), fullConfig.networkMapService) assertEquals(NetworkMapInfo(localPort(12345), DUMMY_NOTARY.name), fullConfig.networkMapService)
assertTrue((fullConfig.dataSourceProperties["dataSource.url"] as String).contains("AUTO_SERVER_PORT=30001")) assertThat(fullConfig.dataSourceProperties["dataSource.url"] as String).contains("AUTO_SERVER_PORT=30001")
assertTrue(fullConfig.useTestClock) assertTrue(fullConfig.useTestClock)
assertFalse(fullConfig.detectPublicIp) assertFalse(fullConfig.detectPublicIp)
} }
@ -222,13 +61,13 @@ class NodeConfigTest {
p2pPort = 10001, p2pPort = 10001,
rpcPort = 40002, rpcPort = 40002,
webPort = 20001, webPort = 20001,
h2Port = 30001, h2port = 30001,
services = mutableListOf("my.service"), notary = NotaryService(validating = false),
networkMap = NetworkMapConfig(DUMMY_NOTARY.name, localPort(12345)),
users = listOf(user("jenny")) users = listOf(user("jenny"))
) )
config.networkMap = NetworkMapConfig(DUMMY_NOTARY.name, 12345)
val nodeConfig = config.toFileConfig() val nodeConfig = config.toConfig()
.withValue("baseDirectory", ConfigValueFactory.fromAnyRef(baseDir.toString())) .withValue("baseDirectory", ConfigValueFactory.fromAnyRef(baseDir.toString()))
.withFallback(ConfigFactory.parseResources("web-reference.conf")) .withFallback(ConfigFactory.parseResources("web-reference.conf"))
.resolve() .resolve()
@ -240,35 +79,27 @@ class NodeConfigTest {
assertEquals("cordacadevpass", webConfig.keyStorePassword) assertEquals("cordacadevpass", webConfig.keyStorePassword)
} }
@Test
fun `test moving`() {
val config = createConfig(legalName = myLegalName)
val elsewhere = baseDir / "elsewhere"
val moved = config.moveTo(elsewhere)
assertEquals(elsewhere / "myname", moved.nodeDir)
assertEquals(elsewhere / "myname-explorer", moved.explorerDir)
assertEquals(elsewhere / "myname" / "plugins", moved.pluginDir)
}
private fun createConfig( private fun createConfig(
legalName: CordaX500Name = CordaX500Name(organisation = "Unknown", locality = "Nowhere", country = "GB"), legalName: CordaX500Name = CordaX500Name(organisation = "Unknown", locality = "Nowhere", country = "GB"),
p2pPort: Int = -1, p2pPort: Int = -1,
rpcPort: Int = -1, rpcPort: Int = -1,
webPort: Int = -1, webPort: Int = -1,
h2Port: Int = -1, h2port: Int = -1,
services: MutableList<String> = mutableListOf("extra.service"), notary: NotaryService?,
networkMap: NetworkMapConfig?,
users: List<User> = listOf(user("guest")) users: List<User> = listOf(user("guest"))
) = NodeConfig( ): NodeConfig {
baseDir, return NodeConfig(
legalName = legalName, myLegalName = legalName,
p2pPort = p2pPort, p2pAddress = localPort(p2pPort),
rpcPort = rpcPort, rpcAddress = localPort(rpcPort),
webPort = webPort, webAddress = localPort(webPort),
h2Port = h2Port, h2port = h2port,
extraServices = services, notary = notary,
users = users networkMapService = networkMap,
rpcUsers = users
) )
}
private fun localPort(port: Int) = NetworkHostAndPort("localhost", port) private fun localPort(port: Int) = NetworkHostAndPort("localhost", port)
} }

View File

@ -1,6 +1,7 @@
package net.corda.demobench.model package net.corda.demobench.model
import net.corda.core.identity.CordaX500Name import net.corda.core.identity.CordaX500Name
import net.corda.core.utilities.NetworkHostAndPort
import net.corda.nodeapi.User import net.corda.nodeapi.User
import net.corda.testing.DUMMY_NOTARY import net.corda.testing.DUMMY_NOTARY
import org.junit.Test import org.junit.Test
@ -51,7 +52,7 @@ class NodeControllerTest {
fun `test first validated node becomes network map`() { fun `test first validated node becomes network map`() {
val data = NodeData() val data = NodeData()
data.legalName.value = node1Name data.legalName.value = node1Name
data.p2pPort.value = 100000 data.p2pPort.value = 10000
assertFalse(controller.hasNetworkMap()) assertFalse(controller.hasNetworkMap())
controller.validate(data) controller.validate(data)
@ -90,7 +91,7 @@ class NodeControllerTest {
@Test @Test
fun `test register network map node`() { fun `test register network map node`() {
val config = createConfig(commonName = "Organisation is Network Map") val config = createConfig(commonName = "Organisation is Network Map")
assertTrue(config.isNetworkMap()) assertTrue(config.nodeConfig.isNetworkMap)
assertFalse(controller.hasNetworkMap()) assertFalse(controller.hasNetworkMap())
controller.register(config) controller.register(config)
@ -99,9 +100,10 @@ class NodeControllerTest {
@Test @Test
fun `test register non-network-map node`() { fun `test register non-network-map node`() {
val config = createConfig(commonName = "Organisation is not Network Map") val config = createConfig(
config.networkMap = NetworkMapConfig(DUMMY_NOTARY.name, 10000) commonName = "Organisation is not Network Map",
assertFalse(config.isNetworkMap()) networkMap = NetworkMapConfig(DUMMY_NOTARY.name, localPort(10000)))
assertFalse(config.nodeConfig.isNetworkMap)
assertFalse(controller.hasNetworkMap()) assertFalse(controller.hasNetworkMap())
controller.register(config) controller.register(config)
@ -146,7 +148,7 @@ class NodeControllerTest {
@Test @Test
fun `test H2 port is max`() { fun `test H2 port is max`() {
val portNumber = NodeController.firstPort + 3478 val portNumber = NodeController.firstPort + 3478
val config = createConfig(h2Port = portNumber) val config = createConfig(h2port = portNumber)
assertEquals(NodeController.firstPort, controller.nextPort) assertEquals(NodeController.firstPort, controller.nextPort)
controller.register(config) controller.register(config)
assertEquals(portNumber + 1, controller.nextPort) assertEquals(portNumber + 1, controller.nextPort)
@ -166,25 +168,30 @@ class NodeControllerTest {
private fun createConfig( private fun createConfig(
commonName: String = "Unknown", commonName: String = "Unknown",
p2pPort: Int = -1, p2pPort: Int = 0,
rpcPort: Int = -1, rpcPort: Int = 0,
webPort: Int = -1, webPort: Int = 0,
h2Port: Int = -1, h2port: Int = 0,
services: MutableList<String> = mutableListOf("extra.service"), notary: NotaryService? = null,
networkMap: NetworkMapConfig? = null,
users: List<User> = listOf(user("guest")) users: List<User> = listOf(user("guest"))
) = NodeConfig( ): NodeConfigWrapper {
baseDir, val nodeConfig = NodeConfig(
legalName = CordaX500Name( myLegalName = CordaX500Name(
organisation = commonName, organisation = commonName,
locality = "New York", locality = "New York",
country = "US" country = "US"
), ),
p2pPort = p2pPort, p2pAddress = localPort(p2pPort),
rpcPort = rpcPort, rpcAddress = localPort(rpcPort),
webPort = webPort, webAddress = localPort(webPort),
h2Port = h2Port, h2port = h2port,
extraServices = services, notary = notary,
users = users networkMapService = networkMap,
rpcUsers = users
) )
return NodeConfigWrapper(baseDir, nodeConfig)
}
private fun localPort(port: Int) = NetworkHostAndPort("localhost", port)
} }

View File

@ -1,44 +0,0 @@
package net.corda.demobench.model
import org.junit.Test
import kotlin.test.assertEquals
import kotlin.test.assertNotNull
import kotlin.test.assertTrue
class ServiceControllerTest {
@Test
fun `test empty`() {
val controller = ServiceController("/empty-services.conf")
assertNotNull(controller.services)
assertTrue(controller.services.isEmpty())
assertNotNull(controller.notaries)
assertTrue(controller.notaries.isEmpty())
}
@Test
fun `test duplicates`() {
val controller = ServiceController("/duplicate-services.conf")
assertNotNull(controller.services)
assertEquals(listOf("corda.example"), controller.services.map { it.value })
}
@Test
fun `test notaries`() {
val controller = ServiceController("/notary-services.conf")
assertNotNull(controller.notaries)
assertEquals(listOf("corda.notary.simple"), controller.notaries.map { it.value })
}
@Test
fun `test services`() {
val controller = ServiceController()
assertNotNull(controller.services)
assertTrue(controller.services.isNotEmpty())
assertNotNull(controller.notaries)
assertTrue(controller.notaries.isNotEmpty())
}
}

View File

@ -1,47 +0,0 @@
package net.corda.demobench.model
import net.corda.nodeapi.User
import org.junit.Test
import kotlin.test.assertEquals
class UserTest {
@Test
fun createFromEmptyMap() {
val user = toUser(emptyMap())
assertEquals("none", user.username)
assertEquals("none", user.password)
assertEquals(emptySet(), user.permissions)
}
@Test
fun createFromMap() {
val map = mapOf(
"username" to "MyName",
"password" to "MyPassword",
"permissions" to listOf("Flow.MyFlow")
)
val user = toUser(map)
assertEquals("MyName", user.username)
assertEquals("MyPassword", user.password)
assertEquals(setOf("Flow.MyFlow"), user.permissions)
}
@Test
fun userToMap() {
val user = User("MyName", "MyPassword", setOf("Flow.MyFlow"))
val map = user.toMap()
assertEquals("MyName", map["username"])
assertEquals("MyPassword", map["password"])
assertEquals(listOf("Flow.MyFlow"), map["permissions"])
}
@Test
fun `default user`() {
val user = user("guest")
assertEquals("guest", user.username)
assertEquals("letmein", user.password)
assertEquals(setOf("ALL"), user.permissions)
}
}

View File

@ -1,3 +0,0 @@
corda.example : Example
corda.example : Example
corda.example : Example

View File

@ -1,2 +0,0 @@
corda.notary.simple : Notary Simple
corda.example : Example

View File

@ -22,8 +22,6 @@ import net.corda.finance.flows.CashExitFlow.ExitRequest
import net.corda.finance.flows.CashIssueAndPaymentFlow.IssueAndPaymentRequest import net.corda.finance.flows.CashIssueAndPaymentFlow.IssueAndPaymentRequest
import net.corda.node.services.FlowPermissions.Companion.startFlowPermission import net.corda.node.services.FlowPermissions.Companion.startFlowPermission
import net.corda.nodeapi.User import net.corda.nodeapi.User
import net.corda.nodeapi.internal.ServiceInfo
import net.corda.nodeapi.internal.ServiceType
import net.corda.testing.ALICE import net.corda.testing.ALICE
import net.corda.testing.BOB import net.corda.testing.BOB
import net.corda.testing.DUMMY_NOTARY import net.corda.testing.DUMMY_NOTARY
@ -71,19 +69,19 @@ class ExplorerSimulation(val options: OptionSet) {
// TODO : Supported flow should be exposed somehow from the node instead of set of ServiceInfo. // TODO : Supported flow should be exposed somehow from the node instead of set of ServiceInfo.
val notary = startNotaryNode(DUMMY_NOTARY.name, customOverrides = mapOf("nearestCity" to "Zurich"), validating = false) val notary = startNotaryNode(DUMMY_NOTARY.name, customOverrides = mapOf("nearestCity" to "Zurich"), validating = false)
val alice = startNode(providedName = ALICE.name, rpcUsers = arrayListOf(user), val alice = startNode(providedName = ALICE.name, rpcUsers = arrayListOf(user),
advertisedServices = setOf(ServiceInfo(ServiceType.corda.getSubType("cash"))),
customOverrides = mapOf("nearestCity" to "Milan")) customOverrides = mapOf("nearestCity" to "Milan"))
val bob = startNode(providedName = BOB.name, rpcUsers = arrayListOf(user), val bob = startNode(providedName = BOB.name, rpcUsers = arrayListOf(user),
advertisedServices = setOf(ServiceInfo(ServiceType.corda.getSubType("cash"))),
customOverrides = mapOf("nearestCity" to "Madrid")) customOverrides = mapOf("nearestCity" to "Madrid"))
val ukBankName = CordaX500Name(organisation = "UK Bank Plc", locality = "London", country = "GB") val ukBankName = CordaX500Name(organisation = "UK Bank Plc", locality = "London", country = "GB")
val usaBankName = CordaX500Name(organisation = "USA Bank Corp", locality = "New York", country = "US") val usaBankName = CordaX500Name(organisation = "USA Bank Corp", locality = "New York", country = "US")
val issuerGBP = startNode(providedName = ukBankName, rpcUsers = arrayListOf(manager), val issuerGBP = startNode(providedName = ukBankName, rpcUsers = arrayListOf(manager),
advertisedServices = setOf(ServiceInfo(ServiceType.corda.getSubType("issuer.GBP"))), customOverrides = mapOf(
customOverrides = mapOf("nearestCity" to "London")) "issuableCurrencies" to listOf("GBP"),
"nearestCity" to "London"))
val issuerUSD = startNode(providedName = usaBankName, rpcUsers = arrayListOf(manager), val issuerUSD = startNode(providedName = usaBankName, rpcUsers = arrayListOf(manager),
advertisedServices = setOf(ServiceInfo(ServiceType.corda.getSubType("issuer.USD"))), customOverrides = mapOf(
customOverrides = mapOf("nearestCity" to "New York")) "issuableCurrencies" to listOf("USD"),
"nearestCity" to "New York"))
notaryNode = notary.get() notaryNode = notary.get()
aliceNode = alice.get() aliceNode = alice.get()

View File

@ -1,27 +0,0 @@
package net.corda.explorer.model
import net.corda.finance.USD
import org.junit.Test
import java.util.*
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith
import kotlin.test.assertFalse
import kotlin.test.assertTrue
class IssuerModelTest {
@Test
fun `test issuer regex`() {
val regex = Regex("corda.issuer.(USD|GBP|CHF)")
assertTrue("corda.issuer.USD".matches(regex))
assertTrue("corda.issuer.GBP".matches(regex))
assertFalse("corda.issuer.USD.GBP".matches(regex))
assertFalse("corda.issuer.EUR".matches(regex))
assertFalse("corda.issuer".matches(regex))
assertEquals(USD, Currency.getInstance("corda.issuer.USD".substringAfterLast(".")))
assertFailsWith(IllegalArgumentException::class) {
Currency.getInstance("corda.issuer.DOLLAR".substringBeforeLast("."))
}
}
}