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

View File

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

View File

@ -4,7 +4,6 @@ trustStorePassword : "trustpass"
p2pAddress : "localhost:10005"
rpcAddress : "localhost:10006"
webAddress : "localhost:10007"
extraAdvertisedServiceIds : [ "corda.interest_rates" ]
networkMapService : {
address : "localhost:10000"
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
guavaVersion=21.0
bouncycastleVersion=1.57

View File

@ -118,10 +118,9 @@ class AttachmentTests {
// 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> {
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 {
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 }
}
}

View File

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

View File

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

View File

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

View File

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

View File

@ -10,7 +10,6 @@ dataSourceProperties : {
p2pAddress : "my-corda-node:10002"
rpcAddress : "my-corda-node:10003"
webAddress : "localhost:10004"
extraAdvertisedServiceIds : [ "corda.interest_rates" ]
networkMapService : {
address : "my-network-map:10000"
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 {
name "O=Controller,L=London,C=GB"
notary = [validating : true]
advertisedServices = []
p2pPort 10002
rpcPort 10003
cordapps = ["net.corda:corda-finance:$corda_release_version"]
}
node {
name "O=PartyA,L=London,C=GB"
advertisedServices = []
p2pPort 10005
rpcPort 10006
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 {
name "O=PartyB,L=New York,C=US"
advertisedServices = []
p2pPort 10008
rpcPort 10009
webPort 10010

View File

@ -295,12 +295,8 @@ NotaryService (SimpleNotaryService, ValidatingNotaryService, RaftValidatingNotar
The ``NotaryService`` is an abstract base class for the various concrete
implementations of the Notary server flow. By default, a node does
not run any ``NotaryService`` server component. However, the appropriate
implementation service is automatically started if the relevant
``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
not run any ``NotaryService`` server component. For that you need to specify the ``notary`` config.
The node may then participate in controlling state uniqueness when contacted by nodes
using the ``NotaryFlow.Client`` ``subFlow``. The
``SimpleNotaryService`` only offers protection against double spend, but
does no further verification. The ``ValidatingNotaryService`` checks

View File

@ -10,12 +10,25 @@ apply plugin: 'com.jfrog.artifactory'
description 'Corda finance modules'
sourceSets {
integrationTest {
kotlin {
compileClasspath += main.output + test.output
runtimeClasspath += main.output + test.output
srcDir file('src/integration-test/kotlin')
}
}
}
dependencies {
// Note the :finance module is a CorDapp in its own right
// and CorDapps using :finance features should use 'cordapp' not 'compile' linkage.
cordaCompile project(':core')
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(path: ':core', configuration: 'testArtifacts')
testCompile "junit:junit:$junit_version"
@ -23,6 +36,8 @@ dependencies {
configurations {
testArtifacts.extendsFrom testRuntime
integrationTestCompile.extendsFrom testCompile
integrationTestRuntime.extendsFrom testRuntime
}
task testJar(type: Jar) {
@ -30,6 +45,11 @@ task testJar(type: Jar) {
from sourceSets.test.output
}
task integrationTest(type: Test, dependsOn: []) {
testClassesDirs = sourceSets.integrationTest.output.classesDirs
classpath = sourceSets.integrationTest.runtimeClasspath
}
artifacts {
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
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.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.SingletonSerializeAsToken
import net.corda.finance.CHF
import net.corda.finance.EUR
import net.corda.finance.GBP
import net.corda.finance.USD
import net.corda.finance.flows.ConfigHolder.Companion.supportedCurrencies
import java.nio.file.Path
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.
*/
@StartableByRPC
class CashConfigDataFlow : FlowLogic<CashConfiguration>() {
companion object {
private val supportedCurrencies = listOf(USD, GBP, CHF, EUR)
}
@Suspendable
override fun call(): CashConfiguration {
val issuableCurrencies = supportedCurrencies.mapNotNull {
try {
// 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)
val configHolder = serviceHub.cordaService(ConfigHolder::class.java)
return CashConfiguration(configHolder.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;
}
/**
* 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.
* The recommended current structure is:
@ -44,6 +39,8 @@ public class CordformNode implements NodeDefinition {
*/
public Map<String, Object> notary = null;
public Map<String, Object> extraConfig = null;
protected Config config = ConfigFactory.empty();
public Config getConfig() {

View File

@ -123,7 +123,9 @@ class Node extends CordformNode {
if (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 org.slf4j.LoggerFactory
import java.lang.reflect.Field
import java.lang.reflect.Modifier.isStatic
import java.lang.reflect.ParameterizedType
import java.net.Proxy
import java.net.URL
@ -142,7 +143,7 @@ fun Any.toConfig(): Config = ConfigValueFactory.fromMap(toConfigMap()).toConfig(
private fun Any.toConfigMap(): Map<String, Any> {
val values = HashMap<String, Any>()
for (field in javaClass.declaredFields) {
if (field.isSynthetic) continue
if (isStatic(field.modifiers) || field.isSynthetic) continue
field.isAccessible = true
val value = field.get(this) ?: continue
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"))
}
@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(
value1: V,
value2: V,
@ -259,6 +264,12 @@ class ConfigParsingTest {
data class OldData(
@OldConfig("oldValue")
val newValue: String)
data class DataWithCompanion(val value: Int) {
companion object {
@Suppress("unused")
val companionValue = 2
}
}
enum class TestEnum { Value1, Value2 }
}

View File

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

View File

@ -70,7 +70,6 @@ import net.corda.node.services.vault.NodeVaultService
import net.corda.node.services.vault.VaultSoftLockManager
import net.corda.node.utilities.*
import net.corda.node.utilities.AddOrRemove.ADD
import net.corda.nodeapi.internal.ServiceInfo
import org.apache.activemq.artemis.utils.ReusableLatch
import org.slf4j.Logger
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
// AbstractNode. It should be possible to generate the NodeInfo outside of AbstractNode, so it can be passed in.
abstract class AbstractNode(config: NodeConfiguration,
val advertisedServices: Set<ServiceInfo>,
val platformClock: Clock,
protected val versionInfo: VersionInfo,
@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.api.NetworkMapCacheInternal
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.messaging.ArtemisMessagingServer
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.
*
* @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,
advertisedServices: Set<ServiceInfo>,
versionInfo: VersionInfo,
val initialiseSerialization: Boolean = true
) : AbstractNode(configuration, advertisedServices, createClock(configuration), versionInfo) {
) : AbstractNode(configuration, createClock(configuration), versionInfo) {
companion object {
private val logger = loggerFor<Node>()
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.utilities.registration.HTTPNetworkRegistrationService
import net.corda.node.utilities.registration.NetworkRegistrationHelper
import net.corda.nodeapi.internal.ServiceInfo
import net.corda.nodeapi.internal.addShutdownHook
import org.fusesource.jansi.Ansi
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 createNode(conf: FullNodeConfiguration, versionInfo: VersionInfo, services: Set<ServiceInfo>): Node {
return Node(conf, services, versionInfo)
}
open protected fun createNode(conf: FullNodeConfiguration, versionInfo: VersionInfo): Node = Node(conf, versionInfo)
open protected fun startNode(conf: FullNodeConfiguration, versionInfo: VersionInfo, startTime: Long, cmdlineOptions: CmdLineOptions) {
val advertisedServices = conf.calculateServices()
val node = createNode(conf, versionInfo, advertisedServices)
val node = createNode(conf, versionInfo)
if (cmdlineOptions.justGenerateNodeInfo) {
// Perform the minimum required start-up logic to be able to write a nodeInfo to disk
node.generateNodeInfo()

View File

@ -7,7 +7,6 @@ import net.corda.node.internal.NetworkMapInfo
import net.corda.node.services.messaging.CertificateChainCheckPolicy
import net.corda.nodeapi.User
import net.corda.nodeapi.config.NodeSSLConfiguration
import net.corda.nodeapi.internal.ServiceInfo
import java.net.URL
import java.nio.file.Path
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.
// 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 extraAdvertisedServiceIds: List<String>,
override val notary: NotaryConfig?,
override val certificateChainCheckPolicies: List<CertChainPolicyConfig>,
override val devMode: Boolean = false,
@ -103,13 +101,6 @@ data class FullNodeConfiguration(
require(myLegalName.commonName == null) { "Common name must be null: $myLegalName" }
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 {

View File

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

View File

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

View File

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

View File

@ -8,7 +8,6 @@ import net.corda.core.serialization.deserialize
import net.corda.core.utilities.getOrThrow
import net.corda.node.internal.StartedNode
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.messaging.MessagingService
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.ADD
import net.corda.node.utilities.AddOrRemove.REMOVE
import net.corda.nodeapi.internal.ServiceInfo
import net.corda.testing.*
import net.corda.testing.node.MockNetwork
import net.corda.testing.node.MockNetwork.MockNode
@ -270,11 +270,10 @@ abstract class AbstractNetworkMapServiceTest<out S : AbstractNetworkMapService>
override fun create(config: NodeConfiguration,
network: MockNetwork,
networkMapAddr: SingleMessageRecipient?,
advertisedServices: Set<ServiceInfo>,
id: Int,
notaryIdentity: Pair<ServiceInfo, KeyPair>?,
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
}
}

View File

@ -2,12 +2,11 @@ package net.corda.node.services.network
import net.corda.core.messaging.SingleMessageRecipient
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.messaging.MessagingService
import net.corda.nodeapi.internal.ServiceInfo
import net.corda.testing.node.MockNetwork
import net.corda.testing.node.MockNetwork.MockNode
import net.corda.testing.node.MockServices.Companion.MOCK_VERSION_INFO
import java.math.BigInteger
import java.security.KeyPair
@ -32,11 +31,10 @@ class PersistentNetworkMapServiceTest : AbstractNetworkMapServiceTest<Persistent
override fun create(config: NodeConfiguration,
network: MockNetwork,
networkMapAddr: SingleMessageRecipient?,
advertisedServices: Set<ServiceInfo>,
id: Int,
notaryIdentity: Pair<ServiceInfo, KeyPair>?,
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)
}
}

View File

@ -135,7 +135,7 @@ class FlowFrameworkTests {
node2.internals.acceptableLiveFiberCountOnStop = 1
node2.dispose()
mockNet.runNetwork()
val restoredFlow = node2.restartAndGetRestoredFlow<InitiatedReceiveFlow>(node1)
val restoredFlow = node2.restartAndGetRestoredFlow<InitiatedReceiveFlow>()
assertThat(restoredFlow.receivedPayloads[0]).isEqualTo("Hello")
}
@ -184,7 +184,7 @@ class FlowFrameworkTests {
node2.smm.executor.flush()
node2.internals.disableDBCloseOnStop()
node2.dispose() // kill receiver
val restoredFlow = node2.restartAndGetRestoredFlow<ReceiveFlow>(node1)
val restoredFlow = node2.restartAndGetRestoredFlow<ReceiveFlow>()
assertThat(restoredFlow.receivedPayloads[0]).isEqualTo("Hello")
}
@ -214,7 +214,7 @@ class FlowFrameworkTests {
assertEquals(1, node2.checkpointStorage.checkpoints().size) // confirm checkpoint
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()
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.
@ -685,10 +685,10 @@ class FlowFrameworkTests {
////////////////////////////////////////////////////////////////////////////////////////////////////////////
//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
stop()
val newNode = mockNet.createNode(id, advertisedServices = *advertisedServices.toTypedArray())
val newNode = mockNet.createNode(id)
newNode.internals.acceptableLiveFiberCountOnStop = 1
manuallyCloseDB()
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 {
name "O=Notary Service,L=Zurich,C=CH"
notary = [validating : true]
advertisedServices = []
p2pPort 10002
rpcPort 10003
cordapps = []
@ -50,7 +49,6 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) {
}
node {
name "O=Bank A,L=London,C=GB"
advertisedServices = []
p2pPort 10005
rpcPort 10006
cordapps = []
@ -58,7 +56,6 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) {
}
node {
name "O=Bank B,L=New York,C=US"
advertisedServices = []
p2pPort 10008
rpcPort 10009
webPort 10010

View File

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

View File

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

View File

@ -49,9 +49,9 @@ abstract class Simulation(val networkSendManuallyPumped: Boolean,
// This puts together a mock network of SimulatedNodes.
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)
: 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 fun findMyLocation(): WorldMapLocation? {
return configuration.myLegalName.locality.let { CityDatabase[it] }
@ -62,15 +62,14 @@ abstract class Simulation(val networkSendManuallyPumped: Boolean,
var counter = 0
override fun create(config: NodeConfiguration, network: MockNetwork, networkMapAddr: SingleMessageRecipient?,
advertisedServices: Set<ServiceInfo>, id: Int, notaryIdentity: Pair<ServiceInfo, KeyPair>?,
entropyRoot: BigInteger): SimulatedNode {
id: Int, notaryIdentity: Pair<ServiceInfo, KeyPair>?, entropyRoot: BigInteger): SimulatedNode {
val letter = 'A' + counter
val (city, country) = bankLocations[counter++ % bankLocations.size]
val cfg = testNodeConfiguration(
baseDirectory = config.baseDirectory,
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> {
@ -85,25 +84,23 @@ abstract class Simulation(val networkSendManuallyPumped: Boolean,
object NetworkMapNodeFactory : MockNetwork.Factory<SimulatedNode> {
override fun create(config: NodeConfiguration, network: MockNetwork, networkMapAddr: SingleMessageRecipient?,
advertisedServices: Set<ServiceInfo>, id: Int, notaryIdentity: Pair<ServiceInfo, KeyPair>?,
entropyRoot: BigInteger): SimulatedNode {
id: Int, notaryIdentity: Pair<ServiceInfo, KeyPair>?, entropyRoot: BigInteger): SimulatedNode {
val cfg = testNodeConfiguration(
baseDirectory = config.baseDirectory,
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> {
override fun create(config: NodeConfiguration, network: MockNetwork, networkMapAddr: SingleMessageRecipient?,
advertisedServices: Set<ServiceInfo>, id: Int, notaryIdentity: Pair<ServiceInfo, KeyPair>?,
entropyRoot: BigInteger): SimulatedNode {
id: Int, notaryIdentity: Pair<ServiceInfo, KeyPair>?, entropyRoot: BigInteger): SimulatedNode {
requireNotNull(config.notary)
val cfg = testNodeConfiguration(
baseDirectory = config.baseDirectory,
myLegalName = DUMMY_NOTARY.name,
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")
override fun create(config: NodeConfiguration, network: MockNetwork, networkMapAddr: SingleMessageRecipient?,
advertisedServices: Set<ServiceInfo>, id: Int, notaryIdentity: Pair<ServiceInfo, KeyPair>?,
entropyRoot: BigInteger): SimulatedNode {
id: Int, notaryIdentity: Pair<ServiceInfo, KeyPair>?, entropyRoot: BigInteger): SimulatedNode {
val cfg = testNodeConfiguration(
baseDirectory = config.baseDirectory,
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 {
registerInitiatedFlow(NodeInterestRates.FixQueryHandler::class.java)
registerInitiatedFlow(NodeInterestRates.FixSignHandler::class.java)
@ -133,12 +129,11 @@ abstract class Simulation(val networkSendManuallyPumped: Boolean,
object RegulatorFactory : MockNetwork.Factory<SimulatedNode> {
override fun create(config: NodeConfiguration, network: MockNetwork, networkMapAddr: SingleMessageRecipient?,
advertisedServices: Set<ServiceInfo>, id: Int, notaryIdentity: Pair<ServiceInfo, KeyPair>?,
entropyRoot: BigInteger): SimulatedNode {
id: Int, notaryIdentity: Pair<ServiceInfo, KeyPair>?, entropyRoot: BigInteger): SimulatedNode {
val cfg = testNodeConfiguration(
baseDirectory = config.baseDirectory,
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.
// 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.

View File

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

View File

@ -56,13 +56,11 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) {
node {
name "O=Notary Service,L=Zurich,C=CH"
notary = [validating : true]
advertisedServices = []
p2pPort 10002
cordapps = ["net.corda:finance:$corda_release_version"]
}
node {
name "O=Bank A,L=London,C=GB"
advertisedServices = []
p2pPort 10005
rpcPort 10006
cordapps = ["net.corda:finance:$corda_release_version"]
@ -70,7 +68,6 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) {
}
node {
name "O=Bank B,L=New York,C=US"
advertisedServices = []
p2pPort 10008
rpcPort 10009
cordapps = ["net.corda:finance:$corda_release_version"]
@ -78,7 +75,6 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) {
}
node {
name "O=BankOfCorda,L=New York,C=US"
advertisedServices = []
p2pPort 10011
rpcPort 10012
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.config.parseAs
import net.corda.nodeapi.config.toConfig
import net.corda.nodeapi.internal.ServiceInfo
import net.corda.nodeapi.internal.addShutdownHook
import net.corda.testing.*
import net.corda.testing.node.MockServices.Companion.MOCK_VERSION_INFO
@ -78,7 +77,6 @@ interface DriverDSLExposedInterface : CordformContext {
* when called from Java code.
* @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!
* @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 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
@ -88,7 +86,6 @@ interface DriverDSLExposedInterface : CordformContext {
fun startNode(
defaultParameters: NodeParameters = NodeParameters(),
providedName: CordaX500Name? = defaultParameters.providedName,
advertisedServices: Set<ServiceInfo> = defaultParameters.advertisedServices,
rpcUsers: List<User> = defaultParameters.rpcUsers,
verifierType: VerifierType = defaultParameters.verifierType,
customOverrides: Map<String, Any?> = defaultParameters.customOverrides,
@ -267,7 +264,6 @@ sealed class PortAllocation {
*/
data class NodeParameters(
val providedName: CordaX500Name? = null,
val advertisedServices: Set<ServiceInfo> = emptySet(),
val rpcUsers: List<User> = emptyList(),
val verifierType: VerifierType = VerifierType.InMemory,
val customOverrides: Map<String, Any?> = emptyMap(),
@ -275,7 +271,6 @@ data class NodeParameters(
val maximumHeapSize: String = "200m"
) {
fun setProvidedName(providedName: CordaX500Name?) = copy(providedName = providedName)
fun setAdvertisedServices(advertisedServices: Set<ServiceInfo>) = copy(advertisedServices = advertisedServices)
fun setRpcUsers(rpcUsers: List<User>) = copy(rpcUsers = rpcUsers)
fun setVerifierType(verifierType: VerifierType) = copy(verifierType = verifierType)
fun setCustomerOverrides(customOverrides: Map<String, Any?>) = copy(customOverrides = customOverrides)
@ -686,7 +681,6 @@ class DriverDSL(
override fun startNode(
defaultParameters: NodeParameters,
providedName: CordaX500Name?,
advertisedServices: Set<ServiceInfo>,
rpcUsers: List<User>,
verifierType: VerifierType,
customOverrides: Map<String, Any?>,
@ -710,7 +704,6 @@ class DriverDSL(
"p2pAddress" to p2pAddress.toString(),
"rpcAddress" to rpcAddress.toString(),
"webAddress" to webAddress.toString(),
"extraAdvertisedServiceIds" to advertisedServices.map { it.toString() },
"networkMapService" to networkMapServiceConfigLookup(name),
"useTestClock" to useTestClock,
"rpcUsers" to if (rpcUsers.isEmpty()) defaultRpcUserList else rpcUsers.map { it.toMap() },
@ -741,7 +734,6 @@ class DriverDSL(
baseDirectory = baseDirectory(name),
allowMissingConfig = true,
configOverrides = node.config + notary + mapOf(
"extraAdvertisedServiceIds" to node.advertisedServices,
"networkMapService" to networkMapServiceConfigLookup(name),
"rpcUsers" to if (rpcUsers.isEmpty()) defaultRpcUserList else rpcUsers
)
@ -935,7 +927,7 @@ class DriverDSL(
// Write node.conf
writeConfig(nodeConf.baseDirectory, "node.conf", config)
// 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) {
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.nodeapi.User
import net.corda.nodeapi.config.toConfig
import net.corda.nodeapi.internal.ServiceInfo
fun CordformDefinition.node(configure: CordformNode.() -> Unit) {
addNode { cordformNode -> cordformNode.configure() }
@ -23,7 +22,3 @@ fun CordformNode.rpcUsers(vararg users: User) {
fun CordformNode.notary(notaryConfig: NotaryConfig) {
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.
*/
fun create(config: NodeConfiguration, network: MockNetwork, networkMapAddr: SingleMessageRecipient?,
advertisedServices: Set<ServiceInfo>, id: Int, notaryIdentity: Pair<ServiceInfo, KeyPair>?,
entropyRoot: BigInteger): N
id: Int, notaryIdentity: Pair<ServiceInfo, KeyPair>?, entropyRoot: BigInteger): N
}
object DefaultFactory : Factory<MockNode> {
override fun create(config: NodeConfiguration, network: MockNetwork, networkMapAddr: SingleMessageRecipient?,
advertisedServices: Set<ServiceInfo>, id: Int, notaryIdentity: Pair<ServiceInfo, KeyPair>?,
entropyRoot: BigInteger): MockNode {
return MockNode(config, network, networkMapAddr, advertisedServices, id, notaryIdentity, entropyRoot)
id: Int, notaryIdentity: Pair<ServiceInfo, KeyPair>?, entropyRoot: BigInteger): MockNode {
return MockNode(config, network, networkMapAddr, id, notaryIdentity, entropyRoot)
}
}
@ -159,11 +157,10 @@ class MockNetwork(private val networkSendManuallyPumped: Boolean = false,
open class MockNode(config: NodeConfiguration,
val mockNet: MockNetwork,
override val networkMapAddress: SingleMessageRecipient?,
advertisedServices: Set<ServiceInfo>,
val id: Int,
internal val notaryIdentity: Pair<ServiceInfo, KeyPair>?,
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
override val log: Logger = loggerFor<MockNode>()
override val serverThread: AffinityExecutor =
@ -303,7 +300,6 @@ class MockNetwork(private val networkSendManuallyPumped: Boolean = false,
nodeFactory = nodeFactory ?: defaultFactory,
legalName = MOCK_NET_MAP.name,
notaryIdentity = null,
advertisedServices = arrayOf(),
entropyRoot = BigInteger.valueOf(random63BitValue()),
configOverrides = {},
start = true
@ -315,18 +311,16 @@ class MockNetwork(private val networkSendManuallyPumped: Boolean = false,
fun createUnstartedNode(forcedID: Int? = null,
legalName: CordaX500Name? = null, notaryIdentity: Pair<ServiceInfo, KeyPair>? = null,
entropyRoot: BigInteger = BigInteger.valueOf(random63BitValue()),
vararg advertisedServices: ServiceInfo,
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>,
legalName: CordaX500Name? = null, notaryIdentity: Pair<ServiceInfo, KeyPair>? = null,
entropyRoot: BigInteger = BigInteger.valueOf(random63BitValue()),
vararg advertisedServices: ServiceInfo,
configOverrides: (NodeConfiguration) -> Any? = {}): N {
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,
legalName: CordaX500Name? = null, notaryIdentity: Pair<ServiceInfo, KeyPair>? = null,
entropyRoot: BigInteger = BigInteger.valueOf(random63BitValue()),
vararg advertisedServices: ServiceInfo,
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. */
fun <N : MockNode> createNode(forcedID: Int? = null, nodeFactory: Factory<N>,
legalName: CordaX500Name? = null, notaryIdentity: Pair<ServiceInfo, KeyPair>? = null,
entropyRoot: BigInteger = BigInteger.valueOf(random63BitValue()),
vararg advertisedServices: ServiceInfo,
configOverrides: (NodeConfiguration) -> Any? = {}): StartedNode<N> {
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>,
start: Boolean, legalName: CordaX500Name?, notaryIdentity: Pair<ServiceInfo, KeyPair>?,
entropyRoot: BigInteger,
advertisedServices: Array<out ServiceInfo>,
configOverrides: (NodeConfiguration) -> Any?): N {
val id = forcedID ?: nextNodeId++
val config = testNodeConfiguration(
@ -367,7 +358,7 @@ class MockNetwork(private val networkSendManuallyPumped: Boolean = false,
whenever(it.dataSourceProperties).thenReturn(makeTestDataSourceProperties("node_${id}_net_$networkId"))
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) {
start()
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) {
is SingleMessageRecipient -> nodes.single { it.started!!.network.myAddress == msgRecipient }
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} ")
}
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.config.parseAs
import net.corda.nodeapi.config.toConfig
import net.corda.nodeapi.internal.ServiceInfo
import net.corda.testing.DUMMY_MAP
import net.corda.testing.TestDependencyInjectionBase
import net.corda.testing.driver.addressMustNotBeBoundFuture
@ -83,11 +82,10 @@ abstract class NodeBasedTest : TestDependencyInjectionBase() {
*/
fun startNetworkMapNode(legalName: CordaX500Name = DUMMY_MAP.name,
platformVersion: Int = 1,
advertisedServices: Set<ServiceInfo> = emptySet(),
rpcUsers: List<User> = emptyList(),
configOverrides: Map<String, Any> = emptyMap()): StartedNode<Node> {
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
}
}
@ -95,7 +93,6 @@ abstract class NodeBasedTest : TestDependencyInjectionBase() {
@JvmOverloads
fun startNode(legalName: CordaX500Name,
platformVersion: Int = 1,
advertisedServices: Set<ServiceInfo> = emptySet(),
rpcUsers: List<User> = emptyList(),
configOverrides: Map<String, Any> = emptyMap(),
noNetworkMap: Boolean = false,
@ -119,7 +116,6 @@ abstract class NodeBasedTest : TestDependencyInjectionBase() {
val node = startNodeInternal(
legalName,
platformVersion,
advertisedServices,
rpcUsers,
networkMapConf + configOverrides,
noNetworkMap)
@ -175,7 +171,6 @@ abstract class NodeBasedTest : TestDependencyInjectionBase() {
private fun startNodeInternal(legalName: CordaX500Name,
platformVersion: Int,
advertisedServices: Set<ServiceInfo>,
rpcUsers: List<User>,
configOverrides: Map<String, Any>,
noNetworkMap: Boolean = false): StartedNode<Node> {
@ -189,14 +184,15 @@ abstract class NodeBasedTest : TestDependencyInjectionBase() {
"myLegalName" to legalName.toString(),
"p2pAddress" to p2pAddress,
"rpcAddress" to localPort[1].toString(),
"extraAdvertisedServiceIds" to advertisedServices.map { it.toString() },
"rpcUsers" to rpcUsers.map { it.toMap() },
"noNetworkMap" to noNetworkMap
) + configOverrides
)
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()
nodes += node
thread(name = legalName.organisation) {

View File

@ -70,9 +70,6 @@ node in a new tab.
![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
page saying:
> ### Installed CorDapps

View File

@ -71,6 +71,7 @@ dependencies {
testCompile "junit:junit:$junit_version"
testCompile "org.jetbrains.kotlin:kotlin-test:$kotlin_version"
testCompile "org.assertj:assertj-core:${assertj_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.utilities.loggerFor
import net.corda.demobench.model.JVMConfig
import net.corda.demobench.model.NodeConfig
import net.corda.demobench.model.forceDirectory
import net.corda.demobench.model.NodeConfigWrapper
import net.corda.demobench.readErrorLines
import tornadofx.*
import java.io.IOException
import java.nio.file.Files
import java.nio.file.StandardCopyOption.*
import java.nio.file.StandardCopyOption.REPLACE_EXISTING
import java.util.concurrent.Executors
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
@Throws(IOException::class)
fun open(config: NodeConfig, onExit: (NodeConfig) -> Unit) {
val explorerDir = config.explorerDir.toFile()
fun open(config: NodeConfigWrapper, onExit: (NodeConfigWrapper) -> Unit) {
val explorerDir = config.explorerDir
if (!explorerDir.forceDirectory()) {
log.warn("Failed to create working directory '{}'", explorerDir.absolutePath)
try {
explorerDir.createDirectories()
} catch (e: IOException) {
log.warn("Failed to create working directory '{}'", explorerDir.toAbsolutePath())
onExit(config)
return
}
val legalName = config.nodeConfig.myLegalName
try {
installApps(config)
val user = config.users.elementAt(0)
val user = config.nodeConfig.rpcUsers[0]
val p = explorerController.process(
"--host=localhost",
"--port=${config.rpcPort}",
"--port=${config.nodeConfig.rpcAddress.port}",
"--username=${user.username}",
"--password=${user.password}")
.directory(explorerDir)
.directory(explorerDir.toFile())
.start()
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.
safeClose(p.outputStream)
@ -57,21 +59,21 @@ class Explorer internal constructor(private val explorerController: ExplorerCont
process = null
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 {
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)
}
} 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)
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
// 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

View File

@ -1,76 +1,46 @@
package net.corda.demobench.model
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.nodeapi.config.parseAs
import tornadofx.*
import java.io.IOException
import java.nio.file.Files
import java.nio.file.Path
class InstallFactory : Controller() {
private val nodeController by inject<NodeController>()
private val serviceController by inject<ServiceController>()
@Throws(IOException::class)
fun toInstallConfig(config: Config, baseDir: Path): InstallConfig {
val p2pPort = config.parsePort("p2pAddress")
val rpcPort = config.parsePort("rpcAddress")
val webPort = config.parsePort("webAddress")
val h2Port = config.getInt("h2port")
val x500name = CordaX500Name.parse(config.getString("myLegalName"))
val extraServices = config.parseExtraServices("extraAdvertisedServiceIds")
val tempDir = Files.createTempDirectory(baseDir, ".node")
val nodeConfig = NodeConfig(
tempDir,
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")
fun NetworkHostAndPort.checkPort() {
require(nodeController.isPortValid(port)) { "Invalid port $port" }
}
return InstallConfig(tempDir, nodeConfig)
}
val nodeConfig = config.parseAs<NodeConfig>()
nodeConfig.p2pAddress.checkPort()
nodeConfig.rpcAddress.checkPort()
nodeConfig.webAddress.checkPort()
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
}
val tempDir = Files.createTempDirectory(baseDir, ".node")
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()
}
if (nodeConfig.isNetworkMap) {
log.info("Node '${nodeConfig.myLegalName}' is the network map")
}
return InstallConfig(tempDir, NodeConfigWrapper(tempDir, nodeConfig))
}
}
/**
* Wraps the configuration information for a Node
* 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
override val pluginDir: Path = baseDir.resolve("plugins")
override val pluginDir: Path = baseDir / "plugins"
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
import com.typesafe.config.*
import com.typesafe.config.ConfigRenderOptions
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 java.io.File
import java.nio.file.Files
import net.corda.nodeapi.config.toConfig
import java.nio.file.Path
import java.nio.file.StandardCopyOption
class NodeConfig constructor(
baseDir: Path,
legalName: CordaX500Name,
p2pPort: Int,
val rpcPort: Int,
val webPort: Int,
val h2Port: Int,
val extraServices: MutableList<String> = mutableListOf(),
val users: List<User> = listOf(defaultUser),
var networkMap: NetworkMapConfig? = null
) : NetworkMapConfig(legalName, p2pPort), HasPlugins {
/**
* This is a subset of FullNodeConfiguration, containing only those configs which we need. The node uses reference.conf
* to fill in the defaults so we're not required to specify them here.
*/
data class NodeConfig(
val myLegalName: CordaX500Name,
val p2pAddress: NetworkHostAndPort,
val rpcAddress: NetworkHostAndPort,
/** This is not used by the node but by the webserver which looks at node.conf. */
val webAddress: NetworkHostAndPort,
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 {
val renderOptions: ConfigRenderOptions = ConfigRenderOptions.defaults().setOriginComments(false)
val defaultUser = user("guest")
}
val nearestCity: String = legalName.locality
val nodeDir: Path = baseDir.resolve(key)
override val pluginDir: Path = nodeDir.resolve("plugins")
val explorerDir: Path = baseDir.resolve("$key-explorer")
@Suppress("unused")
private val detectPublicIp = false
@Suppress("unused")
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
val isCashIssuer: Boolean = extraServices.any {
it.startsWith("corda.issuer.")
}
fun isNetworkMap(): Boolean = networkMap == null
/*
* 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 install(cordapps: Collection<Path>) {
if (cordapps.isEmpty()) return
pluginDir.createDirectories()
for (cordapp in cordapps) {
cordapp.copyToDirectory(pluginDir, StandardCopyOption.REPLACE_EXISTING)
}
}
}
private fun <T> valueFor(any: T): ConfigValue? = ConfigValueFactory.fromAnyRef(any)
fun user(name: String) = User(name, "letmein", setOf("ALL"))
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()
fun String.toKey() = filter { !it.isWhitespace() }.toLowerCase()

View File

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

View File

@ -4,8 +4,10 @@ import javafx.beans.property.SimpleIntegerProperty
import javafx.beans.property.SimpleListProperty
import javafx.beans.property.SimpleObjectProperty
import javafx.beans.property.SimpleStringProperty
import javafx.collections.FXCollections.observableArrayList
import net.corda.finance.utils.CityDatabase
import tornadofx.*
import java.util.*
object SuggestedDetails {
val banks = listOf(
@ -35,7 +37,7 @@ class NodeData {
val rpcPort = SimpleIntegerProperty()
val webPort = SimpleIntegerProperty()
val h2Port = SimpleIntegerProperty()
val extraServices = SimpleListProperty(mutableListOf<String>().observable())
val extraServices = SimpleListProperty(observableArrayList<ExtraService>())
}
class NodeDataModel : ItemViewModel<NodeData>(NodeData()) {
@ -46,3 +48,9 @@ class NodeDataModel : ItemViewModel<NodeData>(NodeData()) {
val webPort = bind { item?.webPort }
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.demobench.model.HasPlugins
import net.corda.demobench.model.JVMConfig
import net.corda.demobench.model.NodeConfig
import net.corda.demobench.model.NodeConfigWrapper
import tornadofx.*
import java.io.IOException
import java.nio.file.Files
@ -24,16 +24,14 @@ class PluginController : Controller() {
* Install any built-in plugins that this node requires.
*/
@Throws(IOException::class)
fun populate(config: NodeConfig) {
if (!config.pluginDir.exists()) {
config.pluginDir.createDirectories()
}
fun populate(config: NodeConfigWrapper) {
config.pluginDir.createDirectories()
if (finance.exists()) {
finance.copyToDirectory(config.pluginDir, StandardCopyOption.REPLACE_EXISTING)
log.info("Installed 'Finance' 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)
log.info("Installed 'Bank of Corda' plugin")
}

View File

@ -4,6 +4,8 @@ import com.typesafe.config.Config
import com.typesafe.config.ConfigFactory
import javafx.stage.FileChooser
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.InstallFactory
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 ->
configs.forEach { config ->
// Write the configuration file.
val nodeDir = Files.createDirectories(fs.getPath(config.key))
val file = Files.write(nodeDir.resolve("node.conf"), config.toText().toByteArray(UTF_8))
val nodeDir = fs.getPath(config.key).createDirectories()
val file = Files.write(nodeDir / "node.conf", config.nodeConfig.toText().toByteArray(UTF_8))
log.info("Wrote: $file")
// 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.utilities.NetworkHostAndPort
import net.corda.core.utilities.loggerFor
import net.corda.demobench.model.NodeConfig
import net.corda.demobench.model.NodeConfigWrapper
import java.util.*
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 {
val log = loggerFor<NodeRPC>()
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 val timer = Timer()
@ -24,7 +23,7 @@ class NodeRPC(config: NodeConfig, start: (NodeConfig, CordaRPCOps) -> Unit, invo
val setupTask = object : TimerTask() {
override fun run() {
try {
val user = config.users.elementAt(0)
val user = config.nodeConfig.rpcUsers[0]
val connection = rpcClient.start(user.username, user.password)
rpcConnection = connection
val ops = connection.proxy
@ -42,7 +41,7 @@ class NodeRPC(config: NodeConfig, start: (NodeConfig, CordaRPCOps) -> Unit, invo
}
}, 0, oneSecond)
} 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.util.StringConverter
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.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 tornadofx.*
import java.nio.file.Path
@ -39,10 +43,10 @@ class NodeTabView : Fragment() {
val cordappPathsFile: Path = jvm.dataHome / "cordapp-paths.txt"
fun loadDefaultCordappPaths(): MutableList<Path> {
if (cordappPathsFile.exists())
return cordappPathsFile.readAllLines().map { Paths.get(it) }.filter { it.exists() }.toMutableList()
return if (cordappPathsFile.exists())
cordappPathsFile.readAllLines().map { Paths.get(it) }.filter { it.exists() }.toMutableList()
else
return ArrayList()
ArrayList()
}
// This is shared between tabs.
@ -58,11 +62,9 @@ class NodeTabView : Fragment() {
}
private val nodeController by inject<NodeController>()
private val serviceController by inject<ServiceController>()
private val chooser = FileChooser()
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 nodeConfigView = stackpane {
@ -112,8 +114,13 @@ class NodeTabView : Fragment() {
fieldset("Additional configuration") {
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 }
model.item.extraServices.set(checkModel.checkedItems)
if (!nodeController.hasNetworkMap()) {
@ -263,17 +270,17 @@ class NodeTabView : Fragment() {
/**
* Launches a preconfigured Corda node, e.g. from a saved profile.
*/
fun launch(config: NodeConfig) {
fun launch(config: NodeConfigWrapper) {
nodeController.register(config)
launchNode(config)
}
private fun launchNode(config: NodeConfig) {
val countryCode = CityDatabase.cityMap[config.nearestCity]?.countryCode
private fun launchNode(config: NodeConfigWrapper) {
val countryCode = CityDatabase.cityMap[config.nodeConfig.myLegalName.locality]?.countryCode
if (countryCode != null) {
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 ->
Platform.runLater {
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.demobench.explorer.ExplorerController
import net.corda.demobench.model.NodeConfig
import net.corda.demobench.model.NodeConfigWrapper
import net.corda.demobench.model.NodeController
import net.corda.demobench.model.NodeState
import net.corda.demobench.pty.R3Pty
@ -69,8 +70,8 @@ class NodeTerminalView : Fragment() {
private lateinit var logo: ImageView
private lateinit var swingTerminal: SwingNode
fun open(config: NodeConfig, onExit: (Int) -> Unit) {
nodeName.text = config.legalName.organisation
fun open(config: NodeConfigWrapper, onExit: (Int) -> Unit) {
nodeName.text = config.nodeConfig.myLegalName.organisation
swingTerminal = SwingNode()
swingTerminal.setOnMouseClicked {
@ -85,13 +86,13 @@ class NodeTerminalView : Fragment() {
root.isVisible = true
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
if (nodeController.runCorda(r3pty, config)) {
swingTerminal.content = r3pty.terminal
configureDatabaseButton(config)
configureDatabaseButton(config.nodeConfig)
configureExplorerButton(config)
configureWebButton(config)
@ -105,7 +106,7 @@ class NodeTerminalView : Fragment() {
* and close the RPC client if it has.
*/
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
rpc?.close()
}
@ -119,7 +120,7 @@ class NodeTerminalView : Fragment() {
* launched the explorer and only reenable it when
* the explorer has exited.
*/
private fun configureExplorerButton(config: NodeConfig) {
private fun configureExplorerButton(config: NodeConfigWrapper) {
launchExplorerButton.setOnAction {
launchExplorerButton.isDisable = true
@ -131,7 +132,7 @@ class NodeTerminalView : Fragment() {
private fun configureDatabaseButton(config: NodeConfig) {
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
* the web server has exited.
*/
private fun configureWebButton(config: NodeConfig) {
private fun configureWebButton(config: NodeConfigWrapper) {
launchWebButton.setOnAction {
if (webURL != null) {
app.hostServices.showDocument(webURL.toString())
@ -161,12 +162,12 @@ class NodeTerminalView : Fragment() {
launchWebButton.text = ""
launchWebButton.graphic = ProgressIndicator()
log.info("Starting web server for ${config.legalName}")
log.info("Starting web server for ${config.nodeConfig.myLegalName}")
webServer.open(config).then {
Platform.runLater {
launchWebButton.graphic = null
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
launchWebButton.text = "Reopen\nweb site"
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,
start = this::initialise,
invoke = this::pollCashBalances
)
private fun initialise(config: NodeConfig, ops: CordaRPCOps) {
private fun initialise(config: NodeConfigWrapper, ops: CordaRPCOps) {
try {
val (txInit, txNext) = ops.internalVerifiedTransactionsFeed()
val (stateInit, stateNext) = ops.vaultTrackBy<ContractState>(paging = pageSpecification)
@ -212,7 +213,7 @@ class NodeTerminalView : Fragment() {
}
config.state = NodeState.RUNNING
log.info("Node '${config.legalName}' is now ready.")
log.info("Node '${config.nodeConfig.myLegalName}' is now ready.")
header.isDisable = false
}
@ -225,7 +226,7 @@ class NodeTerminalView : Fragment() {
)
Platform.runLater {
balance.value = if (cashBalances.isNullOrEmpty()) "0" else cashBalances
balance.value = if (cashBalances.isEmpty()) "0" else cashBalances
}
} 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.

View File

@ -2,11 +2,11 @@ package net.corda.demobench.web
import com.google.common.util.concurrent.RateLimiter
import net.corda.core.concurrent.CordaFuture
import net.corda.core.utilities.minutes
import net.corda.core.internal.concurrent.openFuture
import net.corda.core.internal.until
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 java.io.IOException
import java.net.HttpURLConnection
@ -25,7 +25,7 @@ class WebServer internal constructor(private val webServerController: WebServerC
private var process: Process? = null
@Throws(IOException::class)
fun open(config: NodeConfig): CordaFuture<URI> {
fun open(config: NodeConfigWrapper): CordaFuture<URI> {
val nodeDir = config.nodeDir.toFile()
if (!nodeDir.isDirectory) {
@ -33,13 +33,14 @@ class WebServer internal constructor(private val webServerController: WebServerC
return openFuture()
}
val legalName = config.nodeConfig.myLegalName
try {
val p = webServerController.process()
.directory(nodeDir)
.start()
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.
safeClose(p.outputStream)
@ -51,22 +52,22 @@ class WebServer internal constructor(private val webServerController: WebServerC
process = null
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 {
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>()
thread {
future.capture {
log.info("Waiting for web server for ${config.legalName} to start ...")
waitForStart(config.webPort)
log.info("Waiting for web server for $legalName to start ...")
waitForStart(config.nodeConfig.webAddress.port)
}
}
return future
} 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
}
}

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.model.JVMConfigTest
import net.corda.demobench.model.NodeControllerTest
import net.corda.demobench.model.ServiceControllerTest
import org.junit.BeforeClass
import org.junit.runner.RunWith
import org.junit.runners.Suite
@ -13,7 +12,6 @@ import org.junit.runners.Suite
*/
@RunWith(Suite::class)
@Suite.SuiteClasses(
ServiceControllerTest::class,
NodeControllerTest::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
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.ConfigValueFactory
import net.corda.core.identity.CordaX500Name
import net.corda.core.internal.div
import net.corda.core.utilities.NetworkHostAndPort
import net.corda.node.internal.NetworkMapInfo
import net.corda.node.services.config.FullNodeConfiguration
import net.corda.nodeapi.User
import net.corda.nodeapi.config.parseAs
import net.corda.nodeapi.config.toConfig
import net.corda.testing.DUMMY_NOTARY
import net.corda.webserver.WebServerConfig
import org.assertj.core.api.Assertions.assertThat
import org.junit.Test
import java.io.StringWriter
import java.nio.file.Path
import java.nio.file.Paths
import kotlin.test.assertEquals
import kotlin.test.assertFalse
import kotlin.test.assertNull
import kotlin.test.assertTrue
class NodeConfigTest {
companion object {
private val baseDir: Path = Paths.get(".").toAbsolutePath()
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
fun `reading node configuration`() {
val config = createConfig(
@ -192,13 +32,13 @@ class NodeConfigTest {
p2pPort = 10001,
rpcPort = 40002,
webPort = 20001,
h2Port = 30001,
services = mutableListOf("my.service"),
h2port = 30001,
notary = NotaryService(validating = false),
networkMap = NetworkMapConfig(DUMMY_NOTARY.name, localPort(12345)),
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()))
.withFallback(ConfigFactory.parseResources("reference.conf"))
.resolve()
@ -207,10 +47,9 @@ class NodeConfigTest {
assertEquals(myLegalName, fullConfig.myLegalName)
assertEquals(localPort(40002), fullConfig.rpcAddress)
assertEquals(localPort(10001), fullConfig.p2pAddress)
assertEquals(listOf("my.service"), fullConfig.extraAdvertisedServiceIds)
assertEquals(listOf(user("jenny")), fullConfig.rpcUsers)
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)
assertFalse(fullConfig.detectPublicIp)
}
@ -222,13 +61,13 @@ class NodeConfigTest {
p2pPort = 10001,
rpcPort = 40002,
webPort = 20001,
h2Port = 30001,
services = mutableListOf("my.service"),
h2port = 30001,
notary = NotaryService(validating = false),
networkMap = NetworkMapConfig(DUMMY_NOTARY.name, localPort(12345)),
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()))
.withFallback(ConfigFactory.parseResources("web-reference.conf"))
.resolve()
@ -240,35 +79,27 @@ class NodeConfigTest {
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(
legalName: CordaX500Name = CordaX500Name(organisation = "Unknown", locality = "Nowhere", country = "GB"),
p2pPort: Int = -1,
rpcPort: Int = -1,
webPort: Int = -1,
h2Port: Int = -1,
services: MutableList<String> = mutableListOf("extra.service"),
h2port: Int = -1,
notary: NotaryService?,
networkMap: NetworkMapConfig?,
users: List<User> = listOf(user("guest"))
) = NodeConfig(
baseDir,
legalName = legalName,
p2pPort = p2pPort,
rpcPort = rpcPort,
webPort = webPort,
h2Port = h2Port,
extraServices = services,
users = users
)
): NodeConfig {
return NodeConfig(
myLegalName = legalName,
p2pAddress = localPort(p2pPort),
rpcAddress = localPort(rpcPort),
webAddress = localPort(webPort),
h2port = h2port,
notary = notary,
networkMapService = networkMap,
rpcUsers = users
)
}
private fun localPort(port: Int) = NetworkHostAndPort("localhost", port)
}

View File

@ -1,6 +1,7 @@
package net.corda.demobench.model
import net.corda.core.identity.CordaX500Name
import net.corda.core.utilities.NetworkHostAndPort
import net.corda.nodeapi.User
import net.corda.testing.DUMMY_NOTARY
import org.junit.Test
@ -51,7 +52,7 @@ class NodeControllerTest {
fun `test first validated node becomes network map`() {
val data = NodeData()
data.legalName.value = node1Name
data.p2pPort.value = 100000
data.p2pPort.value = 10000
assertFalse(controller.hasNetworkMap())
controller.validate(data)
@ -90,7 +91,7 @@ class NodeControllerTest {
@Test
fun `test register network map node`() {
val config = createConfig(commonName = "Organisation is Network Map")
assertTrue(config.isNetworkMap())
assertTrue(config.nodeConfig.isNetworkMap)
assertFalse(controller.hasNetworkMap())
controller.register(config)
@ -99,9 +100,10 @@ class NodeControllerTest {
@Test
fun `test register non-network-map node`() {
val config = createConfig(commonName = "Organisation is not Network Map")
config.networkMap = NetworkMapConfig(DUMMY_NOTARY.name, 10000)
assertFalse(config.isNetworkMap())
val config = createConfig(
commonName = "Organisation is not Network Map",
networkMap = NetworkMapConfig(DUMMY_NOTARY.name, localPort(10000)))
assertFalse(config.nodeConfig.isNetworkMap)
assertFalse(controller.hasNetworkMap())
controller.register(config)
@ -146,7 +148,7 @@ class NodeControllerTest {
@Test
fun `test H2 port is max`() {
val portNumber = NodeController.firstPort + 3478
val config = createConfig(h2Port = portNumber)
val config = createConfig(h2port = portNumber)
assertEquals(NodeController.firstPort, controller.nextPort)
controller.register(config)
assertEquals(portNumber + 1, controller.nextPort)
@ -166,25 +168,30 @@ class NodeControllerTest {
private fun createConfig(
commonName: String = "Unknown",
p2pPort: Int = -1,
rpcPort: Int = -1,
webPort: Int = -1,
h2Port: Int = -1,
services: MutableList<String> = mutableListOf("extra.service"),
p2pPort: Int = 0,
rpcPort: Int = 0,
webPort: Int = 0,
h2port: Int = 0,
notary: NotaryService? = null,
networkMap: NetworkMapConfig? = null,
users: List<User> = listOf(user("guest"))
) = NodeConfig(
baseDir,
legalName = CordaX500Name(
organisation = commonName,
locality = "New York",
country = "US"
),
p2pPort = p2pPort,
rpcPort = rpcPort,
webPort = webPort,
h2Port = h2Port,
extraServices = services,
users = users
)
): NodeConfigWrapper {
val nodeConfig = NodeConfig(
myLegalName = CordaX500Name(
organisation = commonName,
locality = "New York",
country = "US"
),
p2pAddress = localPort(p2pPort),
rpcAddress = localPort(rpcPort),
webAddress = localPort(webPort),
h2port = h2port,
notary = notary,
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.node.services.FlowPermissions.Companion.startFlowPermission
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.BOB
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.
val notary = startNotaryNode(DUMMY_NOTARY.name, customOverrides = mapOf("nearestCity" to "Zurich"), validating = false)
val alice = startNode(providedName = ALICE.name, rpcUsers = arrayListOf(user),
advertisedServices = setOf(ServiceInfo(ServiceType.corda.getSubType("cash"))),
customOverrides = mapOf("nearestCity" to "Milan"))
val bob = startNode(providedName = BOB.name, rpcUsers = arrayListOf(user),
advertisedServices = setOf(ServiceInfo(ServiceType.corda.getSubType("cash"))),
customOverrides = mapOf("nearestCity" to "Madrid"))
val ukBankName = CordaX500Name(organisation = "UK Bank Plc", locality = "London", country = "GB")
val usaBankName = CordaX500Name(organisation = "USA Bank Corp", locality = "New York", country = "US")
val issuerGBP = startNode(providedName = ukBankName, rpcUsers = arrayListOf(manager),
advertisedServices = setOf(ServiceInfo(ServiceType.corda.getSubType("issuer.GBP"))),
customOverrides = mapOf("nearestCity" to "London"))
customOverrides = mapOf(
"issuableCurrencies" to listOf("GBP"),
"nearestCity" to "London"))
val issuerUSD = startNode(providedName = usaBankName, rpcUsers = arrayListOf(manager),
advertisedServices = setOf(ServiceInfo(ServiceType.corda.getSubType("issuer.USD"))),
customOverrides = mapOf("nearestCity" to "New York"))
customOverrides = mapOf(
"issuableCurrencies" to listOf("USD"),
"nearestCity" to "New York"))
notaryNode = notary.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("."))
}
}
}