mirror of
https://github.com/corda/corda.git
synced 2024-12-18 20:47:57 +00:00
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:
parent
34dbbe626b
commit
bd53a22efa
1
.idea/compiler.xml
generated
1
.idea/compiler.xml
generated
@ -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" />
|
||||
|
@ -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
|
||||
|
@ -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"
|
||||
|
@ -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"
|
||||
|
@ -1,4 +1,4 @@
|
||||
gradlePluginsVersion=2.0.0
|
||||
gradlePluginsVersion=2.0.1
|
||||
kotlinVersion=1.1.50
|
||||
guavaVersion=21.0
|
||||
bouncycastleVersion=1.57
|
||||
|
@ -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 }
|
||||
}
|
||||
}
|
||||
|
@ -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 }
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -4,5 +4,4 @@ trustStorePassword : "trustpass"
|
||||
p2pAddress : "my-network-map:10000"
|
||||
webAddress : "localhost:10001"
|
||||
sshdAddress : "localhost:10002"
|
||||
extraAdvertisedServiceIds : []
|
||||
useHTTPS : false
|
||||
|
@ -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"
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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) {
|
||||
}
|
||||
}
|
||||
}
|
@ -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() {
|
||||
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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) {
|
||||
|
@ -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 }
|
||||
}
|
@ -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() }
|
||||
}
|
||||
}
|
||||
|
@ -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() {
|
||||
|
@ -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
|
||||
|
@ -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()
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
|
@ -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()
|
||||
|
@ -30,7 +30,6 @@ class FullNodeConfigurationTest {
|
||||
p2pAddress = NetworkHostAndPort("localhost", 0),
|
||||
rpcAddress = NetworkHostAndPort("localhost", 1),
|
||||
messagingServerAddress = null,
|
||||
extraAdvertisedServiceIds = emptyList(),
|
||||
notary = null,
|
||||
certificateChainCheckPolicies = emptyList(),
|
||||
devMode = true,
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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"]
|
||||
|
@ -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()
|
||||
}
|
||||
|
@ -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() }
|
||||
}
|
||||
|
@ -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")
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
|
@ -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"
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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()
|
@ -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()
|
||||
|
@ -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) }
|
||||
}
|
||||
|
||||
|
@ -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"
|
||||
}
|
||||
|
@ -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>()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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"))
|
@ -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")
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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
|
@ -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
|
||||
)
|
||||
|
@ -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())
|
||||
}
|
||||
|
||||
}
|
@ -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)
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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())
|
||||
}
|
||||
|
||||
}
|
@ -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)
|
||||
}
|
||||
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
corda.example : Example
|
||||
corda.example : Example
|
||||
corda.example : Example
|
@ -1,2 +0,0 @@
|
||||
corda.notary.simple : Notary Simple
|
||||
corda.example : Example
|
@ -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()
|
||||
|
@ -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("."))
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user