mirror of
https://github.com/corda/corda.git
synced 2025-06-17 06:38:21 +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:
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-capsule_test" target="1.6" />
|
||||||
<module name="explorer_main" target="1.8" />
|
<module name="explorer_main" target="1.8" />
|
||||||
<module name="explorer_test" target="1.8" />
|
<module name="explorer_test" target="1.8" />
|
||||||
|
<module name="finance_integrationTest" target="1.8" />
|
||||||
<module name="finance_main" target="1.8" />
|
<module name="finance_main" target="1.8" />
|
||||||
<module name="finance_test" target="1.8" />
|
<module name="finance_test" target="1.8" />
|
||||||
<module name="gradle-plugins-cordform-common_main" target="1.8" />
|
<module name="gradle-plugins-cordform-common_main" target="1.8" />
|
||||||
|
@ -235,13 +235,11 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) {
|
|||||||
node {
|
node {
|
||||||
name "O=Controller,OU=corda,L=London,C=GB"
|
name "O=Controller,OU=corda,L=London,C=GB"
|
||||||
notary = [validating : true]
|
notary = [validating : true]
|
||||||
advertisedServices = []
|
|
||||||
p2pPort 10002
|
p2pPort 10002
|
||||||
cordapps = []
|
cordapps = []
|
||||||
}
|
}
|
||||||
node {
|
node {
|
||||||
name "O=Bank A,OU=corda,L=London,C=GB"
|
name "O=Bank A,OU=corda,L=London,C=GB"
|
||||||
advertisedServices = []
|
|
||||||
p2pPort 10012
|
p2pPort 10012
|
||||||
rpcPort 10013
|
rpcPort 10013
|
||||||
webPort 10014
|
webPort 10014
|
||||||
@ -249,7 +247,6 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) {
|
|||||||
}
|
}
|
||||||
node {
|
node {
|
||||||
name "O=Bank B,OU=corda,L=London,C=GB"
|
name "O=Bank B,OU=corda,L=London,C=GB"
|
||||||
advertisedServices = []
|
|
||||||
p2pPort 10007
|
p2pPort 10007
|
||||||
rpcPort 10008
|
rpcPort 10008
|
||||||
webPort 10009
|
webPort 10009
|
||||||
|
@ -4,7 +4,6 @@ trustStorePassword : "trustpass"
|
|||||||
p2pAddress : "localhost:10002"
|
p2pAddress : "localhost:10002"
|
||||||
rpcAddress : "localhost:10003"
|
rpcAddress : "localhost:10003"
|
||||||
webAddress : "localhost:10004"
|
webAddress : "localhost:10004"
|
||||||
extraAdvertisedServiceIds : [ "corda.interest_rates" ]
|
|
||||||
networkMapService : {
|
networkMapService : {
|
||||||
address : "localhost:10000"
|
address : "localhost:10000"
|
||||||
legalName : "O=Network Map Service,OU=corda,L=London,C=GB"
|
legalName : "O=Network Map Service,OU=corda,L=London,C=GB"
|
||||||
|
@ -4,7 +4,6 @@ trustStorePassword : "trustpass"
|
|||||||
p2pAddress : "localhost:10005"
|
p2pAddress : "localhost:10005"
|
||||||
rpcAddress : "localhost:10006"
|
rpcAddress : "localhost:10006"
|
||||||
webAddress : "localhost:10007"
|
webAddress : "localhost:10007"
|
||||||
extraAdvertisedServiceIds : [ "corda.interest_rates" ]
|
|
||||||
networkMapService : {
|
networkMapService : {
|
||||||
address : "localhost:10000"
|
address : "localhost:10000"
|
||||||
legalName : "O=Network Map Service,OU=corda,L=London,C=GB"
|
legalName : "O=Network Map Service,OU=corda,L=London,C=GB"
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
gradlePluginsVersion=2.0.0
|
gradlePluginsVersion=2.0.1
|
||||||
kotlinVersion=1.1.50
|
kotlinVersion=1.1.50
|
||||||
guavaVersion=21.0
|
guavaVersion=21.0
|
||||||
bouncycastleVersion=1.57
|
bouncycastleVersion=1.57
|
||||||
|
@ -118,10 +118,9 @@ class AttachmentTests {
|
|||||||
// Make a node that doesn't do sanity checking at load time.
|
// Make a node that doesn't do sanity checking at load time.
|
||||||
val aliceNode = mockNet.createNotaryNode(legalName = ALICE.name, nodeFactory = object : MockNetwork.Factory<MockNetwork.MockNode> {
|
val aliceNode = mockNet.createNotaryNode(legalName = ALICE.name, nodeFactory = object : MockNetwork.Factory<MockNetwork.MockNode> {
|
||||||
override fun create(config: NodeConfiguration, network: MockNetwork, networkMapAddr: SingleMessageRecipient?,
|
override fun create(config: NodeConfiguration, network: MockNetwork, networkMapAddr: SingleMessageRecipient?,
|
||||||
advertisedServices: Set<ServiceInfo>, id: Int,
|
id: Int, notaryIdentity: Pair<ServiceInfo, KeyPair>?,
|
||||||
notaryIdentity: Pair<ServiceInfo, KeyPair>?,
|
|
||||||
entropyRoot: BigInteger): MockNetwork.MockNode {
|
entropyRoot: BigInteger): MockNetwork.MockNode {
|
||||||
return object : MockNetwork.MockNode(config, network, networkMapAddr, advertisedServices, id, notaryIdentity, entropyRoot) {
|
return object : MockNetwork.MockNode(config, network, networkMapAddr, id, notaryIdentity, entropyRoot) {
|
||||||
override fun start() = super.start().apply { attachments.checkAttachmentsOnLoad = false }
|
override fun start() = super.start().apply { attachments.checkAttachmentsOnLoad = false }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -162,9 +162,8 @@ class AttachmentSerializationTest {
|
|||||||
client.dispose()
|
client.dispose()
|
||||||
client = mockNet.createNode(client.internals.id, object : MockNetwork.Factory<MockNetwork.MockNode> {
|
client = mockNet.createNode(client.internals.id, object : MockNetwork.Factory<MockNetwork.MockNode> {
|
||||||
override fun create(config: NodeConfiguration, network: MockNetwork, networkMapAddr: SingleMessageRecipient?,
|
override fun create(config: NodeConfiguration, network: MockNetwork, networkMapAddr: SingleMessageRecipient?,
|
||||||
advertisedServices: Set<ServiceInfo>, id: Int, notaryIdentity: Pair<ServiceInfo, KeyPair>?,
|
id: Int, notaryIdentity: Pair<ServiceInfo, KeyPair>?, entropyRoot: BigInteger): MockNetwork.MockNode {
|
||||||
entropyRoot: BigInteger): MockNetwork.MockNode {
|
return object : MockNetwork.MockNode(config, network, networkMapAddr, id, notaryIdentity, entropyRoot) {
|
||||||
return object : MockNetwork.MockNode(config, network, networkMapAddr, advertisedServices, id, notaryIdentity, entropyRoot) {
|
|
||||||
override fun start() = super.start().apply { attachments.checkAttachmentsOnLoad = checkAttachmentsOnLoad }
|
override fun start() = super.start().apply { attachments.checkAttachmentsOnLoad = checkAttachmentsOnLoad }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,6 @@ notary/network map node:
|
|||||||
node {
|
node {
|
||||||
name "O=Controller,OU=corda,L=London,C=UK"
|
name "O=Controller,OU=corda,L=London,C=UK"
|
||||||
notary = [validating : true]
|
notary = [validating : true]
|
||||||
advertisedServices = []
|
|
||||||
p2pPort 10002
|
p2pPort 10002
|
||||||
rpcPort 10003
|
rpcPort 10003
|
||||||
webPort 10004
|
webPort 10004
|
||||||
@ -29,7 +28,6 @@ notary/network map node:
|
|||||||
}
|
}
|
||||||
node {
|
node {
|
||||||
name "CN=NodeA,O=NodeA,L=London,C=UK"
|
name "CN=NodeA,O=NodeA,L=London,C=UK"
|
||||||
advertisedServices = []
|
|
||||||
p2pPort 10005
|
p2pPort 10005
|
||||||
rpcPort 10006
|
rpcPort 10006
|
||||||
webPort 10007
|
webPort 10007
|
||||||
@ -38,7 +36,6 @@ notary/network map node:
|
|||||||
}
|
}
|
||||||
node {
|
node {
|
||||||
name "CN=NodeB,O=NodeB,L=New York,C=US"
|
name "CN=NodeB,O=NodeB,L=New York,C=US"
|
||||||
advertisedServices = []
|
|
||||||
p2pPort 10008
|
p2pPort 10008
|
||||||
rpcPort 10009
|
rpcPort 10009
|
||||||
webPort 10010
|
webPort 10010
|
||||||
@ -47,7 +44,6 @@ notary/network map node:
|
|||||||
}
|
}
|
||||||
node {
|
node {
|
||||||
name "CN=NodeC,O=NodeC,L=Paris,C=FR"
|
name "CN=NodeC,O=NodeC,L=Paris,C=FR"
|
||||||
advertisedServices = []
|
|
||||||
p2pPort 10011
|
p2pPort 10011
|
||||||
rpcPort 10012
|
rpcPort 10012
|
||||||
webPort 10013
|
webPort 10013
|
||||||
|
@ -77,7 +77,6 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) {
|
|||||||
node {
|
node {
|
||||||
name "O=Notary Service,OU=corda,L=London,C=GB"
|
name "O=Notary Service,OU=corda,L=London,C=GB"
|
||||||
notary = [validating : true]
|
notary = [validating : true]
|
||||||
advertisedServices = []
|
|
||||||
p2pPort 10002
|
p2pPort 10002
|
||||||
rpcPort 10003
|
rpcPort 10003
|
||||||
webPort 10004
|
webPort 10004
|
||||||
@ -85,7 +84,6 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) {
|
|||||||
}
|
}
|
||||||
node {
|
node {
|
||||||
name "O=Alice Corp,L=London,C=GB"
|
name "O=Alice Corp,L=London,C=GB"
|
||||||
advertisedServices = []
|
|
||||||
p2pPort 10005
|
p2pPort 10005
|
||||||
rpcPort 10006
|
rpcPort 10006
|
||||||
webPort 10007
|
webPort 10007
|
||||||
|
@ -4,5 +4,4 @@ trustStorePassword : "trustpass"
|
|||||||
p2pAddress : "my-network-map:10000"
|
p2pAddress : "my-network-map:10000"
|
||||||
webAddress : "localhost:10001"
|
webAddress : "localhost:10001"
|
||||||
sshdAddress : "localhost:10002"
|
sshdAddress : "localhost:10002"
|
||||||
extraAdvertisedServiceIds : []
|
|
||||||
useHTTPS : false
|
useHTTPS : false
|
||||||
|
@ -10,7 +10,6 @@ dataSourceProperties : {
|
|||||||
p2pAddress : "my-corda-node:10002"
|
p2pAddress : "my-corda-node:10002"
|
||||||
rpcAddress : "my-corda-node:10003"
|
rpcAddress : "my-corda-node:10003"
|
||||||
webAddress : "localhost:10004"
|
webAddress : "localhost:10004"
|
||||||
extraAdvertisedServiceIds : [ "corda.interest_rates" ]
|
|
||||||
networkMapService : {
|
networkMapService : {
|
||||||
address : "my-network-map:10000"
|
address : "my-network-map:10000"
|
||||||
legalName : "O=Network Map Service,OU=corda,L=London,C=GB"
|
legalName : "O=Network Map Service,OU=corda,L=London,C=GB"
|
||||||
|
@ -29,14 +29,12 @@ Let's take a look at the nodes we're going to deploy. Open the project's ``build
|
|||||||
node {
|
node {
|
||||||
name "O=Controller,L=London,C=GB"
|
name "O=Controller,L=London,C=GB"
|
||||||
notary = [validating : true]
|
notary = [validating : true]
|
||||||
advertisedServices = []
|
|
||||||
p2pPort 10002
|
p2pPort 10002
|
||||||
rpcPort 10003
|
rpcPort 10003
|
||||||
cordapps = ["net.corda:corda-finance:$corda_release_version"]
|
cordapps = ["net.corda:corda-finance:$corda_release_version"]
|
||||||
}
|
}
|
||||||
node {
|
node {
|
||||||
name "O=PartyA,L=London,C=GB"
|
name "O=PartyA,L=London,C=GB"
|
||||||
advertisedServices = []
|
|
||||||
p2pPort 10005
|
p2pPort 10005
|
||||||
rpcPort 10006
|
rpcPort 10006
|
||||||
webPort 10007
|
webPort 10007
|
||||||
@ -45,7 +43,6 @@ Let's take a look at the nodes we're going to deploy. Open the project's ``build
|
|||||||
}
|
}
|
||||||
node {
|
node {
|
||||||
name "O=PartyB,L=New York,C=US"
|
name "O=PartyB,L=New York,C=US"
|
||||||
advertisedServices = []
|
|
||||||
p2pPort 10008
|
p2pPort 10008
|
||||||
rpcPort 10009
|
rpcPort 10009
|
||||||
webPort 10010
|
webPort 10010
|
||||||
|
@ -295,12 +295,8 @@ NotaryService (SimpleNotaryService, ValidatingNotaryService, RaftValidatingNotar
|
|||||||
|
|
||||||
The ``NotaryService`` is an abstract base class for the various concrete
|
The ``NotaryService`` is an abstract base class for the various concrete
|
||||||
implementations of the Notary server flow. By default, a node does
|
implementations of the Notary server flow. By default, a node does
|
||||||
not run any ``NotaryService`` server component. However, the appropriate
|
not run any ``NotaryService`` server component. For that you need to specify the ``notary`` config.
|
||||||
implementation service is automatically started if the relevant
|
The node may then participate in controlling state uniqueness when contacted by nodes
|
||||||
``ServiceType`` id is included in the node's
|
|
||||||
``extraAdvertisedServiceIds`` configuration property. The node will then
|
|
||||||
advertise itself as a Notary via the ``NetworkMapService`` and may then
|
|
||||||
participate in controlling state uniqueness when contacted by nodes
|
|
||||||
using the ``NotaryFlow.Client`` ``subFlow``. The
|
using the ``NotaryFlow.Client`` ``subFlow``. The
|
||||||
``SimpleNotaryService`` only offers protection against double spend, but
|
``SimpleNotaryService`` only offers protection against double spend, but
|
||||||
does no further verification. The ``ValidatingNotaryService`` checks
|
does no further verification. The ``ValidatingNotaryService`` checks
|
||||||
|
@ -10,12 +10,25 @@ apply plugin: 'com.jfrog.artifactory'
|
|||||||
|
|
||||||
description 'Corda finance modules'
|
description 'Corda finance modules'
|
||||||
|
|
||||||
|
sourceSets {
|
||||||
|
integrationTest {
|
||||||
|
kotlin {
|
||||||
|
compileClasspath += main.output + test.output
|
||||||
|
runtimeClasspath += main.output + test.output
|
||||||
|
srcDir file('src/integration-test/kotlin')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
// Note the :finance module is a CorDapp in its own right
|
// Note the :finance module is a CorDapp in its own right
|
||||||
// and CorDapps using :finance features should use 'cordapp' not 'compile' linkage.
|
// and CorDapps using :finance features should use 'cordapp' not 'compile' linkage.
|
||||||
cordaCompile project(':core')
|
cordaCompile project(':core')
|
||||||
cordaCompile project(':confidential-identities')
|
cordaCompile project(':confidential-identities')
|
||||||
|
|
||||||
|
// TODO Remove this once we have app configs
|
||||||
|
compile "com.typesafe:config:$typesafe_config_version"
|
||||||
|
|
||||||
testCompile project(':test-utils')
|
testCompile project(':test-utils')
|
||||||
testCompile project(path: ':core', configuration: 'testArtifacts')
|
testCompile project(path: ':core', configuration: 'testArtifacts')
|
||||||
testCompile "junit:junit:$junit_version"
|
testCompile "junit:junit:$junit_version"
|
||||||
@ -23,6 +36,8 @@ dependencies {
|
|||||||
|
|
||||||
configurations {
|
configurations {
|
||||||
testArtifacts.extendsFrom testRuntime
|
testArtifacts.extendsFrom testRuntime
|
||||||
|
integrationTestCompile.extendsFrom testCompile
|
||||||
|
integrationTestRuntime.extendsFrom testRuntime
|
||||||
}
|
}
|
||||||
|
|
||||||
task testJar(type: Jar) {
|
task testJar(type: Jar) {
|
||||||
@ -30,6 +45,11 @@ task testJar(type: Jar) {
|
|||||||
from sourceSets.test.output
|
from sourceSets.test.output
|
||||||
}
|
}
|
||||||
|
|
||||||
|
task integrationTest(type: Test, dependsOn: []) {
|
||||||
|
testClassesDirs = sourceSets.integrationTest.output.classesDirs
|
||||||
|
classpath = sourceSets.integrationTest.runtimeClasspath
|
||||||
|
}
|
||||||
|
|
||||||
artifacts {
|
artifacts {
|
||||||
testArtifacts testJar
|
testArtifacts testJar
|
||||||
}
|
}
|
||||||
|
@ -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
|
package net.corda.finance.flows
|
||||||
|
|
||||||
import co.paralleluniverse.fibers.Suspendable
|
import co.paralleluniverse.fibers.Suspendable
|
||||||
import net.corda.core.flows.FlowException
|
import com.typesafe.config.ConfigFactory
|
||||||
import net.corda.core.flows.FlowLogic
|
import net.corda.core.flows.FlowLogic
|
||||||
import net.corda.core.flows.StartableByRPC
|
import net.corda.core.flows.StartableByRPC
|
||||||
|
import net.corda.core.internal.declaredField
|
||||||
|
import net.corda.core.internal.div
|
||||||
|
import net.corda.core.internal.read
|
||||||
|
import net.corda.core.node.AppServiceHub
|
||||||
|
import net.corda.core.node.services.CordaService
|
||||||
import net.corda.core.serialization.CordaSerializable
|
import net.corda.core.serialization.CordaSerializable
|
||||||
|
import net.corda.core.serialization.SingletonSerializeAsToken
|
||||||
import net.corda.finance.CHF
|
import net.corda.finance.CHF
|
||||||
import net.corda.finance.EUR
|
import net.corda.finance.EUR
|
||||||
import net.corda.finance.GBP
|
import net.corda.finance.GBP
|
||||||
import net.corda.finance.USD
|
import net.corda.finance.USD
|
||||||
|
import net.corda.finance.flows.ConfigHolder.Companion.supportedCurrencies
|
||||||
|
import java.nio.file.Path
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
|
// TODO Until apps have access to their own config, we'll hack things by first getting the baseDirectory, read the node.conf
|
||||||
|
// again to get our config and store it here for access by our flow
|
||||||
|
@CordaService
|
||||||
|
class ConfigHolder(services: AppServiceHub) : SingletonSerializeAsToken() {
|
||||||
|
companion object {
|
||||||
|
val supportedCurrencies = listOf(USD, GBP, CHF, EUR)
|
||||||
|
}
|
||||||
|
|
||||||
|
val issuableCurrencies: List<Currency>
|
||||||
|
|
||||||
|
init {
|
||||||
|
// Warning!! You are about to see a major hack!
|
||||||
|
val baseDirectory = services.declaredField<Any>("serviceHub").value
|
||||||
|
.let { it.javaClass.getMethod("getConfiguration").apply { isAccessible = true }.invoke(it) }
|
||||||
|
.declaredField<Path>("baseDirectory").value
|
||||||
|
val config = (baseDirectory / "node.conf").read { ConfigFactory.parseReader(it.reader()) }
|
||||||
|
if (config.hasPath("issuableCurrencies")) {
|
||||||
|
issuableCurrencies = config.getStringList("issuableCurrencies").map { Currency.getInstance(it) }
|
||||||
|
require(supportedCurrencies.containsAll(issuableCurrencies))
|
||||||
|
} else {
|
||||||
|
issuableCurrencies = emptyList()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Flow to obtain cash cordapp app configuration.
|
* Flow to obtain cash cordapp app configuration.
|
||||||
*/
|
*/
|
||||||
@StartableByRPC
|
@StartableByRPC
|
||||||
class CashConfigDataFlow : FlowLogic<CashConfiguration>() {
|
class CashConfigDataFlow : FlowLogic<CashConfiguration>() {
|
||||||
companion object {
|
|
||||||
private val supportedCurrencies = listOf(USD, GBP, CHF, EUR)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Suspendable
|
@Suspendable
|
||||||
override fun call(): CashConfiguration {
|
override fun call(): CashConfiguration {
|
||||||
val issuableCurrencies = supportedCurrencies.mapNotNull {
|
val configHolder = serviceHub.cordaService(ConfigHolder::class.java)
|
||||||
try {
|
return CashConfiguration(configHolder.issuableCurrencies, supportedCurrencies)
|
||||||
// Currently it uses checkFlowPermission to determine the list of issuable currency as a temporary hack.
|
|
||||||
// TODO: get the config from proper configuration source.
|
|
||||||
checkFlowPermission("corda.issuer.$it", emptyMap())
|
|
||||||
it
|
|
||||||
} catch (e: FlowException) {
|
|
||||||
null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return CashConfiguration(issuableCurrencies, supportedCurrencies)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* A list of advertised services ID strings.
|
|
||||||
*/
|
|
||||||
public List<String> advertisedServices = emptyList();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the RPC users for this node. This configuration block allows arbitrary configuration.
|
* Set the RPC users for this node. This configuration block allows arbitrary configuration.
|
||||||
* The recommended current structure is:
|
* The recommended current structure is:
|
||||||
@ -44,6 +39,8 @@ public class CordformNode implements NodeDefinition {
|
|||||||
*/
|
*/
|
||||||
public Map<String, Object> notary = null;
|
public Map<String, Object> notary = null;
|
||||||
|
|
||||||
|
public Map<String, Object> extraConfig = null;
|
||||||
|
|
||||||
protected Config config = ConfigFactory.empty();
|
protected Config config = ConfigFactory.empty();
|
||||||
|
|
||||||
public Config getConfig() {
|
public Config getConfig() {
|
||||||
|
@ -123,7 +123,9 @@ class Node extends CordformNode {
|
|||||||
if (notary) {
|
if (notary) {
|
||||||
config = config.withValue("notary", ConfigValueFactory.fromMap(notary))
|
config = config.withValue("notary", ConfigValueFactory.fromMap(notary))
|
||||||
}
|
}
|
||||||
config = config.withValue('extraAdvertisedServiceIds', ConfigValueFactory.fromIterable(advertisedServices*.toString()))
|
if (extraConfig) {
|
||||||
|
config = config.withFallback(ConfigFactory.parseMap(extraConfig))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -12,6 +12,7 @@ import net.corda.core.internal.uncheckedCast
|
|||||||
import net.corda.core.utilities.NetworkHostAndPort
|
import net.corda.core.utilities.NetworkHostAndPort
|
||||||
import org.slf4j.LoggerFactory
|
import org.slf4j.LoggerFactory
|
||||||
import java.lang.reflect.Field
|
import java.lang.reflect.Field
|
||||||
|
import java.lang.reflect.Modifier.isStatic
|
||||||
import java.lang.reflect.ParameterizedType
|
import java.lang.reflect.ParameterizedType
|
||||||
import java.net.Proxy
|
import java.net.Proxy
|
||||||
import java.net.URL
|
import java.net.URL
|
||||||
@ -142,7 +143,7 @@ fun Any.toConfig(): Config = ConfigValueFactory.fromMap(toConfigMap()).toConfig(
|
|||||||
private fun Any.toConfigMap(): Map<String, Any> {
|
private fun Any.toConfigMap(): Map<String, Any> {
|
||||||
val values = HashMap<String, Any>()
|
val values = HashMap<String, Any>()
|
||||||
for (field in javaClass.declaredFields) {
|
for (field in javaClass.declaredFields) {
|
||||||
if (field.isSynthetic) continue
|
if (isStatic(field.modifiers) || field.isSynthetic) continue
|
||||||
field.isAccessible = true
|
field.isAccessible = true
|
||||||
val value = field.get(this) ?: continue
|
val value = field.get(this) ?: continue
|
||||||
val configValue = if (value is String || value is Boolean || value is Number) {
|
val configValue = if (value is String || value is Boolean || value is Number) {
|
||||||
|
@ -181,6 +181,11 @@ class ConfigParsingTest {
|
|||||||
assertThat(OldData("old").toConfig()).isEqualTo(config("newValue" to "old"))
|
assertThat(OldData("old").toConfig()).isEqualTo(config("newValue" to "old"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `static field`() {
|
||||||
|
assertThat(DataWithCompanion(3).toConfig()).isEqualTo(config("value" to 3))
|
||||||
|
}
|
||||||
|
|
||||||
private inline fun <reified S : SingleData<V>, reified L : ListData<V>, V : Any> testPropertyType(
|
private inline fun <reified S : SingleData<V>, reified L : ListData<V>, V : Any> testPropertyType(
|
||||||
value1: V,
|
value1: V,
|
||||||
value2: V,
|
value2: V,
|
||||||
@ -259,6 +264,12 @@ class ConfigParsingTest {
|
|||||||
data class OldData(
|
data class OldData(
|
||||||
@OldConfig("oldValue")
|
@OldConfig("oldValue")
|
||||||
val newValue: String)
|
val newValue: String)
|
||||||
|
data class DataWithCompanion(val value: Int) {
|
||||||
|
companion object {
|
||||||
|
@Suppress("unused")
|
||||||
|
val companionValue = 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
enum class TestEnum { Value1, Value2 }
|
enum class TestEnum { Value1, Value2 }
|
||||||
}
|
}
|
@ -1,20 +1,20 @@
|
|||||||
package net.corda.node
|
package net.corda.node
|
||||||
|
|
||||||
import co.paralleluniverse.fibers.Suspendable
|
import co.paralleluniverse.fibers.Suspendable
|
||||||
import net.corda.core.internal.div
|
|
||||||
import net.corda.core.flows.FlowLogic
|
import net.corda.core.flows.FlowLogic
|
||||||
import net.corda.core.flows.StartableByRPC
|
import net.corda.core.flows.StartableByRPC
|
||||||
|
import net.corda.core.internal.div
|
||||||
import net.corda.core.messaging.startFlow
|
import net.corda.core.messaging.startFlow
|
||||||
import net.corda.core.utilities.getOrThrow
|
import net.corda.core.utilities.getOrThrow
|
||||||
import net.corda.testing.ALICE
|
|
||||||
import net.corda.node.internal.NodeStartup
|
import net.corda.node.internal.NodeStartup
|
||||||
import net.corda.node.services.FlowPermissions.Companion.startFlowPermission
|
import net.corda.node.services.FlowPermissions.Companion.startFlowPermission
|
||||||
|
import net.corda.nodeapi.User
|
||||||
import net.corda.nodeapi.internal.ServiceInfo
|
import net.corda.nodeapi.internal.ServiceInfo
|
||||||
import net.corda.nodeapi.internal.ServiceType
|
import net.corda.nodeapi.internal.ServiceType
|
||||||
import net.corda.nodeapi.User
|
import net.corda.testing.ALICE
|
||||||
|
import net.corda.testing.ProjectStructure.projectRootDir
|
||||||
import net.corda.testing.driver.ListenProcessDeathException
|
import net.corda.testing.driver.ListenProcessDeathException
|
||||||
import net.corda.testing.driver.NetworkMapStartStrategy
|
import net.corda.testing.driver.NetworkMapStartStrategy
|
||||||
import net.corda.testing.ProjectStructure.projectRootDir
|
|
||||||
import net.corda.testing.driver.driver
|
import net.corda.testing.driver.driver
|
||||||
import org.assertj.core.api.Assertions.assertThat
|
import org.assertj.core.api.Assertions.assertThat
|
||||||
import org.assertj.core.api.Assertions.assertThatThrownBy
|
import org.assertj.core.api.Assertions.assertThatThrownBy
|
||||||
@ -60,7 +60,7 @@ class BootTests {
|
|||||||
fun `node quits on failure to register with network map`() {
|
fun `node quits on failure to register with network map`() {
|
||||||
val tooManyAdvertisedServices = (1..100).map { ServiceInfo(ServiceType.notary.getSubType("$it")) }.toSet()
|
val tooManyAdvertisedServices = (1..100).map { ServiceInfo(ServiceType.notary.getSubType("$it")) }.toSet()
|
||||||
driver(networkMapStartStrategy = NetworkMapStartStrategy.Nominated(ALICE.name)) {
|
driver(networkMapStartStrategy = NetworkMapStartStrategy.Nominated(ALICE.name)) {
|
||||||
val future = startNode(providedName = ALICE.name, advertisedServices = tooManyAdvertisedServices)
|
val future = startNode(providedName = ALICE.name)
|
||||||
assertFailsWith(ListenProcessDeathException::class) { future.getOrThrow() }
|
assertFailsWith(ListenProcessDeathException::class) { future.getOrThrow() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -70,7 +70,6 @@ import net.corda.node.services.vault.NodeVaultService
|
|||||||
import net.corda.node.services.vault.VaultSoftLockManager
|
import net.corda.node.services.vault.VaultSoftLockManager
|
||||||
import net.corda.node.utilities.*
|
import net.corda.node.utilities.*
|
||||||
import net.corda.node.utilities.AddOrRemove.ADD
|
import net.corda.node.utilities.AddOrRemove.ADD
|
||||||
import net.corda.nodeapi.internal.ServiceInfo
|
|
||||||
import org.apache.activemq.artemis.utils.ReusableLatch
|
import org.apache.activemq.artemis.utils.ReusableLatch
|
||||||
import org.slf4j.Logger
|
import org.slf4j.Logger
|
||||||
import rx.Observable
|
import rx.Observable
|
||||||
@ -101,7 +100,6 @@ import net.corda.core.crypto.generateKeyPair as cryptoGenerateKeyPair
|
|||||||
// In theory the NodeInfo for the node should be passed in, instead, however currently this is constructed by the
|
// In theory the NodeInfo for the node should be passed in, instead, however currently this is constructed by the
|
||||||
// AbstractNode. It should be possible to generate the NodeInfo outside of AbstractNode, so it can be passed in.
|
// AbstractNode. It should be possible to generate the NodeInfo outside of AbstractNode, so it can be passed in.
|
||||||
abstract class AbstractNode(config: NodeConfiguration,
|
abstract class AbstractNode(config: NodeConfiguration,
|
||||||
val advertisedServices: Set<ServiceInfo>,
|
|
||||||
val platformClock: Clock,
|
val platformClock: Clock,
|
||||||
protected val versionInfo: VersionInfo,
|
protected val versionInfo: VersionInfo,
|
||||||
@VisibleForTesting val busyNodeLatch: ReusableLatch = ReusableLatch()) : SingletonSerializeAsToken() {
|
@VisibleForTesting val busyNodeLatch: ReusableLatch = ReusableLatch()) : SingletonSerializeAsToken() {
|
||||||
|
@ -21,7 +21,6 @@ import net.corda.node.services.RPCUserService
|
|||||||
import net.corda.node.services.RPCUserServiceImpl
|
import net.corda.node.services.RPCUserServiceImpl
|
||||||
import net.corda.node.services.api.NetworkMapCacheInternal
|
import net.corda.node.services.api.NetworkMapCacheInternal
|
||||||
import net.corda.node.services.api.SchemaService
|
import net.corda.node.services.api.SchemaService
|
||||||
import net.corda.nodeapi.internal.ServiceInfo
|
|
||||||
import net.corda.node.services.config.FullNodeConfiguration
|
import net.corda.node.services.config.FullNodeConfiguration
|
||||||
import net.corda.node.services.messaging.ArtemisMessagingServer
|
import net.corda.node.services.messaging.ArtemisMessagingServer
|
||||||
import net.corda.node.services.messaging.ArtemisMessagingServer.Companion.ipDetectRequestProperty
|
import net.corda.node.services.messaging.ArtemisMessagingServer.Companion.ipDetectRequestProperty
|
||||||
@ -60,14 +59,11 @@ import kotlin.system.exitProcess
|
|||||||
* loads important data off disk and starts listening for connections.
|
* loads important data off disk and starts listening for connections.
|
||||||
*
|
*
|
||||||
* @param configuration This is typically loaded from a TypeSafe HOCON configuration file.
|
* @param configuration This is typically loaded from a TypeSafe HOCON configuration file.
|
||||||
* @param advertisedServices The services this node advertises. This must be a subset of the services it runs,
|
|
||||||
* but nodes are not required to advertise services they run (hence subset).
|
|
||||||
*/
|
*/
|
||||||
open class Node(override val configuration: FullNodeConfiguration,
|
open class Node(override val configuration: FullNodeConfiguration,
|
||||||
advertisedServices: Set<ServiceInfo>,
|
|
||||||
versionInfo: VersionInfo,
|
versionInfo: VersionInfo,
|
||||||
val initialiseSerialization: Boolean = true
|
val initialiseSerialization: Boolean = true
|
||||||
) : AbstractNode(configuration, advertisedServices, createClock(configuration), versionInfo) {
|
) : AbstractNode(configuration, createClock(configuration), versionInfo) {
|
||||||
companion object {
|
companion object {
|
||||||
private val logger = loggerFor<Node>()
|
private val logger = loggerFor<Node>()
|
||||||
var renderBasicInfoToConsole = true
|
var renderBasicInfoToConsole = true
|
||||||
|
@ -12,7 +12,6 @@ import net.corda.node.services.transactions.bftSMaRtSerialFilter
|
|||||||
import net.corda.node.shell.InteractiveShell
|
import net.corda.node.shell.InteractiveShell
|
||||||
import net.corda.node.utilities.registration.HTTPNetworkRegistrationService
|
import net.corda.node.utilities.registration.HTTPNetworkRegistrationService
|
||||||
import net.corda.node.utilities.registration.NetworkRegistrationHelper
|
import net.corda.node.utilities.registration.NetworkRegistrationHelper
|
||||||
import net.corda.nodeapi.internal.ServiceInfo
|
|
||||||
import net.corda.nodeapi.internal.addShutdownHook
|
import net.corda.nodeapi.internal.addShutdownHook
|
||||||
import org.fusesource.jansi.Ansi
|
import org.fusesource.jansi.Ansi
|
||||||
import org.fusesource.jansi.AnsiConsole
|
import org.fusesource.jansi.AnsiConsole
|
||||||
@ -86,13 +85,10 @@ open class NodeStartup(val args: Array<String>) {
|
|||||||
|
|
||||||
open protected fun preNetworkRegistration(conf: FullNodeConfiguration) = Unit
|
open protected fun preNetworkRegistration(conf: FullNodeConfiguration) = Unit
|
||||||
|
|
||||||
open protected fun createNode(conf: FullNodeConfiguration, versionInfo: VersionInfo, services: Set<ServiceInfo>): Node {
|
open protected fun createNode(conf: FullNodeConfiguration, versionInfo: VersionInfo): Node = Node(conf, versionInfo)
|
||||||
return Node(conf, services, versionInfo)
|
|
||||||
}
|
|
||||||
|
|
||||||
open protected fun startNode(conf: FullNodeConfiguration, versionInfo: VersionInfo, startTime: Long, cmdlineOptions: CmdLineOptions) {
|
open protected fun startNode(conf: FullNodeConfiguration, versionInfo: VersionInfo, startTime: Long, cmdlineOptions: CmdLineOptions) {
|
||||||
val advertisedServices = conf.calculateServices()
|
val node = createNode(conf, versionInfo)
|
||||||
val node = createNode(conf, versionInfo, advertisedServices)
|
|
||||||
if (cmdlineOptions.justGenerateNodeInfo) {
|
if (cmdlineOptions.justGenerateNodeInfo) {
|
||||||
// Perform the minimum required start-up logic to be able to write a nodeInfo to disk
|
// Perform the minimum required start-up logic to be able to write a nodeInfo to disk
|
||||||
node.generateNodeInfo()
|
node.generateNodeInfo()
|
||||||
|
@ -7,7 +7,6 @@ import net.corda.node.internal.NetworkMapInfo
|
|||||||
import net.corda.node.services.messaging.CertificateChainCheckPolicy
|
import net.corda.node.services.messaging.CertificateChainCheckPolicy
|
||||||
import net.corda.nodeapi.User
|
import net.corda.nodeapi.User
|
||||||
import net.corda.nodeapi.config.NodeSSLConfiguration
|
import net.corda.nodeapi.config.NodeSSLConfiguration
|
||||||
import net.corda.nodeapi.internal.ServiceInfo
|
|
||||||
import java.net.URL
|
import java.net.URL
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
import java.util.*
|
import java.util.*
|
||||||
@ -82,7 +81,6 @@ data class FullNodeConfiguration(
|
|||||||
// TODO This field is slightly redundant as p2pAddress is sufficient to hold the address of the node's MQ broker.
|
// TODO This field is slightly redundant as p2pAddress is sufficient to hold the address of the node's MQ broker.
|
||||||
// Instead this should be a Boolean indicating whether that broker is an internal one started by the node or an external one
|
// Instead this should be a Boolean indicating whether that broker is an internal one started by the node or an external one
|
||||||
val messagingServerAddress: NetworkHostAndPort?,
|
val messagingServerAddress: NetworkHostAndPort?,
|
||||||
val extraAdvertisedServiceIds: List<String>,
|
|
||||||
override val notary: NotaryConfig?,
|
override val notary: NotaryConfig?,
|
||||||
override val certificateChainCheckPolicies: List<CertChainPolicyConfig>,
|
override val certificateChainCheckPolicies: List<CertChainPolicyConfig>,
|
||||||
override val devMode: Boolean = false,
|
override val devMode: Boolean = false,
|
||||||
@ -103,13 +101,6 @@ data class FullNodeConfiguration(
|
|||||||
require(myLegalName.commonName == null) { "Common name must be null: $myLegalName" }
|
require(myLegalName.commonName == null) { "Common name must be null: $myLegalName" }
|
||||||
require(minimumPlatformVersion >= 1) { "minimumPlatformVersion cannot be less than 1" }
|
require(minimumPlatformVersion >= 1) { "minimumPlatformVersion cannot be less than 1" }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun calculateServices(): Set<ServiceInfo> {
|
|
||||||
return extraAdvertisedServiceIds
|
|
||||||
.filter(String::isNotBlank)
|
|
||||||
.map { ServiceInfo.parse(it) }
|
|
||||||
.toSet()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class VerifierType {
|
enum class VerifierType {
|
||||||
|
@ -24,7 +24,6 @@ import net.corda.core.utilities.*
|
|||||||
import net.corda.node.services.api.FlowAppAuditEvent
|
import net.corda.node.services.api.FlowAppAuditEvent
|
||||||
import net.corda.node.services.api.FlowPermissionAuditEvent
|
import net.corda.node.services.api.FlowPermissionAuditEvent
|
||||||
import net.corda.node.services.api.ServiceHubInternal
|
import net.corda.node.services.api.ServiceHubInternal
|
||||||
import net.corda.node.services.config.FullNodeConfiguration
|
|
||||||
import net.corda.node.services.statemachine.FlowSessionState.Initiating
|
import net.corda.node.services.statemachine.FlowSessionState.Initiating
|
||||||
import net.corda.node.utilities.CordaPersistence
|
import net.corda.node.utilities.CordaPersistence
|
||||||
import net.corda.node.utilities.DatabaseTransaction
|
import net.corda.node.utilities.DatabaseTransaction
|
||||||
@ -262,10 +261,7 @@ class FlowStateMachineImpl<R>(override val id: StateMachineRunId,
|
|||||||
|
|
||||||
// TODO Dummy implementation of access to application specific permission controls and audit logging
|
// TODO Dummy implementation of access to application specific permission controls and audit logging
|
||||||
override fun checkFlowPermission(permissionName: String, extraAuditData: Map<String, String>) {
|
override fun checkFlowPermission(permissionName: String, extraAuditData: Map<String, String>) {
|
||||||
// This is a hack to allow cash app access list of permitted issuer currency.
|
val permissionGranted = true // TODO define permission control service on ServiceHubInternal and actually check authorization.
|
||||||
// TODO: replace this with cordapp configuration.
|
|
||||||
val config = serviceHub.configuration as? FullNodeConfiguration
|
|
||||||
val permissionGranted = config?.extraAdvertisedServiceIds?.contains(permissionName) != false
|
|
||||||
val checkPermissionEvent = FlowPermissionAuditEvent(
|
val checkPermissionEvent = FlowPermissionAuditEvent(
|
||||||
serviceHub.clock.instant(),
|
serviceHub.clock.instant(),
|
||||||
flowInitiator,
|
flowInitiator,
|
||||||
@ -276,6 +272,7 @@ class FlowStateMachineImpl<R>(override val id: StateMachineRunId,
|
|||||||
permissionName,
|
permissionName,
|
||||||
permissionGranted)
|
permissionGranted)
|
||||||
serviceHub.auditService.recordAuditEvent(checkPermissionEvent)
|
serviceHub.auditService.recordAuditEvent(checkPermissionEvent)
|
||||||
|
@Suppress("ConstantConditionIf")
|
||||||
if (!permissionGranted) {
|
if (!permissionGranted) {
|
||||||
throw FlowPermissionException("User $flowInitiator not permissioned for $permissionName on flow $id")
|
throw FlowPermissionException("User $flowInitiator not permissioned for $permissionName on flow $id")
|
||||||
}
|
}
|
||||||
@ -398,14 +395,8 @@ class FlowStateMachineImpl<R>(override val id: StateMachineRunId,
|
|||||||
waitForConfirmation: Boolean,
|
waitForConfirmation: Boolean,
|
||||||
retryable: Boolean = false
|
retryable: Boolean = false
|
||||||
): FlowSessionInternal {
|
): FlowSessionInternal {
|
||||||
val session = openSessions[Pair(sessionFlow, otherParty)]
|
val session = openSessions[Pair(sessionFlow, otherParty)] ?: throw IllegalStateException("Expected an Uninitiated session for $otherParty")
|
||||||
if (session == null) {
|
val state = session.state as? FlowSessionState.Uninitiated ?: throw IllegalStateException("Tried to initiate a session $session, but it's already initiating/initiated")
|
||||||
throw IllegalStateException("Expected an Uninitiated session for $otherParty")
|
|
||||||
}
|
|
||||||
val state = session.state
|
|
||||||
if (state !is FlowSessionState.Uninitiated) {
|
|
||||||
throw IllegalStateException("Tried to initiate a session $session, but it's already initiating/initiated")
|
|
||||||
}
|
|
||||||
logger.trace { "Initiating a new session with ${state.otherParty}" }
|
logger.trace { "Initiating a new session with ${state.otherParty}" }
|
||||||
session.state = FlowSessionState.Initiating(state.otherParty)
|
session.state = FlowSessionState.Initiating(state.otherParty)
|
||||||
session.retryable = retryable
|
session.retryable = retryable
|
||||||
|
@ -275,9 +275,8 @@ class TwoPartyTradeFlowTests(val anonymous: Boolean) {
|
|||||||
// that Bob was waiting on before the reboot occurred.
|
// that Bob was waiting on before the reboot occurred.
|
||||||
bobNode = mockNet.createNode(bobAddr.id, object : MockNetwork.Factory<MockNetwork.MockNode> {
|
bobNode = mockNet.createNode(bobAddr.id, object : MockNetwork.Factory<MockNetwork.MockNode> {
|
||||||
override fun create(config: NodeConfiguration, network: MockNetwork, networkMapAddr: SingleMessageRecipient?,
|
override fun create(config: NodeConfiguration, network: MockNetwork, networkMapAddr: SingleMessageRecipient?,
|
||||||
advertisedServices: Set<ServiceInfo>, id: Int, notaryIdentity: Pair<ServiceInfo, KeyPair>?,
|
id: Int, notaryIdentity: Pair<ServiceInfo, KeyPair>?, entropyRoot: BigInteger): MockNetwork.MockNode {
|
||||||
entropyRoot: BigInteger): MockNetwork.MockNode {
|
return MockNetwork.MockNode(config, network, networkMapAddr, bobAddr.id, notaryIdentity, entropyRoot)
|
||||||
return MockNetwork.MockNode(config, network, networkMapAddr, advertisedServices, bobAddr.id, notaryIdentity, entropyRoot)
|
|
||||||
}
|
}
|
||||||
}, BOB.name)
|
}, BOB.name)
|
||||||
|
|
||||||
@ -312,18 +311,15 @@ class TwoPartyTradeFlowTests(val anonymous: Boolean) {
|
|||||||
|
|
||||||
// Creates a mock node with an overridden storage service that uses a RecordingMap, that lets us test the order
|
// Creates a mock node with an overridden storage service that uses a RecordingMap, that lets us test the order
|
||||||
// of gets and puts.
|
// of gets and puts.
|
||||||
private fun makeNodeWithTracking(
|
private fun makeNodeWithTracking(name: CordaX500Name): StartedNode<MockNetwork.MockNode> {
|
||||||
networkMapAddress: SingleMessageRecipient?,
|
|
||||||
name: CordaX500Name): StartedNode<MockNetwork.MockNode> {
|
|
||||||
// Create a node in the mock network ...
|
// Create a node in the mock network ...
|
||||||
return mockNet.createNode(nodeFactory = object : MockNetwork.Factory<MockNetwork.MockNode> {
|
return mockNet.createNode(nodeFactory = object : MockNetwork.Factory<MockNetwork.MockNode> {
|
||||||
override fun create(config: NodeConfiguration,
|
override fun create(config: NodeConfiguration,
|
||||||
network: MockNetwork,
|
network: MockNetwork,
|
||||||
networkMapAddr: SingleMessageRecipient?,
|
networkMapAddr: SingleMessageRecipient?,
|
||||||
advertisedServices: Set<ServiceInfo>, id: Int,
|
id: Int, notaryIdentity: Pair<ServiceInfo, KeyPair>?,
|
||||||
notaryIdentity: Pair<ServiceInfo, KeyPair>?,
|
|
||||||
entropyRoot: BigInteger): MockNetwork.MockNode {
|
entropyRoot: BigInteger): MockNetwork.MockNode {
|
||||||
return object : MockNetwork.MockNode(config, network, networkMapAddr, advertisedServices, id, notaryIdentity, entropyRoot) {
|
return object : MockNetwork.MockNode(config, network, networkMapAddr, id, notaryIdentity, entropyRoot) {
|
||||||
// That constructs a recording tx storage
|
// That constructs a recording tx storage
|
||||||
override fun makeTransactionStorage(): WritableTransactionStorage {
|
override fun makeTransactionStorage(): WritableTransactionStorage {
|
||||||
return RecordingTransactionStorage(database, super.makeTransactionStorage())
|
return RecordingTransactionStorage(database, super.makeTransactionStorage())
|
||||||
@ -338,9 +334,9 @@ class TwoPartyTradeFlowTests(val anonymous: Boolean) {
|
|||||||
mockNet = MockNetwork(false)
|
mockNet = MockNetwork(false)
|
||||||
|
|
||||||
val notaryNode = mockNet.createNotaryNode()
|
val notaryNode = mockNet.createNotaryNode()
|
||||||
val aliceNode = makeNodeWithTracking(notaryNode.network.myAddress, ALICE.name)
|
val aliceNode = makeNodeWithTracking(ALICE.name)
|
||||||
val bobNode = makeNodeWithTracking(notaryNode.network.myAddress, BOB.name)
|
val bobNode = makeNodeWithTracking(BOB.name)
|
||||||
val bankNode = makeNodeWithTracking(notaryNode.network.myAddress, BOC.name)
|
val bankNode = makeNodeWithTracking(BOC.name)
|
||||||
val issuer = bankNode.info.chooseIdentity().ref(1, 2, 3)
|
val issuer = bankNode.info.chooseIdentity().ref(1, 2, 3)
|
||||||
mockNet.runNetwork()
|
mockNet.runNetwork()
|
||||||
notaryNode.internals.ensureRegistered()
|
notaryNode.internals.ensureRegistered()
|
||||||
@ -443,9 +439,9 @@ class TwoPartyTradeFlowTests(val anonymous: Boolean) {
|
|||||||
mockNet = MockNetwork(false)
|
mockNet = MockNetwork(false)
|
||||||
|
|
||||||
val notaryNode = mockNet.createNotaryNode()
|
val notaryNode = mockNet.createNotaryNode()
|
||||||
val aliceNode = makeNodeWithTracking(notaryNode.network.myAddress, ALICE.name)
|
val aliceNode = makeNodeWithTracking(ALICE.name)
|
||||||
val bobNode = makeNodeWithTracking(notaryNode.network.myAddress, BOB.name)
|
val bobNode = makeNodeWithTracking(BOB.name)
|
||||||
val bankNode = makeNodeWithTracking(notaryNode.network.myAddress, BOC.name)
|
val bankNode = makeNodeWithTracking(BOC.name)
|
||||||
val issuer = bankNode.info.chooseIdentity().ref(1, 2, 3)
|
val issuer = bankNode.info.chooseIdentity().ref(1, 2, 3)
|
||||||
|
|
||||||
mockNet.runNetwork()
|
mockNet.runNetwork()
|
||||||
|
@ -30,7 +30,6 @@ class FullNodeConfigurationTest {
|
|||||||
p2pAddress = NetworkHostAndPort("localhost", 0),
|
p2pAddress = NetworkHostAndPort("localhost", 0),
|
||||||
rpcAddress = NetworkHostAndPort("localhost", 1),
|
rpcAddress = NetworkHostAndPort("localhost", 1),
|
||||||
messagingServerAddress = null,
|
messagingServerAddress = null,
|
||||||
extraAdvertisedServiceIds = emptyList(),
|
|
||||||
notary = null,
|
notary = null,
|
||||||
certificateChainCheckPolicies = emptyList(),
|
certificateChainCheckPolicies = emptyList(),
|
||||||
devMode = true,
|
devMode = true,
|
||||||
|
@ -8,7 +8,6 @@ import net.corda.core.serialization.deserialize
|
|||||||
import net.corda.core.utilities.getOrThrow
|
import net.corda.core.utilities.getOrThrow
|
||||||
import net.corda.node.internal.StartedNode
|
import net.corda.node.internal.StartedNode
|
||||||
import net.corda.node.services.api.NetworkMapCacheInternal
|
import net.corda.node.services.api.NetworkMapCacheInternal
|
||||||
import net.corda.nodeapi.internal.ServiceInfo
|
|
||||||
import net.corda.node.services.config.NodeConfiguration
|
import net.corda.node.services.config.NodeConfiguration
|
||||||
import net.corda.node.services.messaging.MessagingService
|
import net.corda.node.services.messaging.MessagingService
|
||||||
import net.corda.node.services.messaging.send
|
import net.corda.node.services.messaging.send
|
||||||
@ -25,6 +24,7 @@ import net.corda.node.services.network.NetworkMapService.Companion.SUBSCRIPTION_
|
|||||||
import net.corda.node.utilities.AddOrRemove
|
import net.corda.node.utilities.AddOrRemove
|
||||||
import net.corda.node.utilities.AddOrRemove.ADD
|
import net.corda.node.utilities.AddOrRemove.ADD
|
||||||
import net.corda.node.utilities.AddOrRemove.REMOVE
|
import net.corda.node.utilities.AddOrRemove.REMOVE
|
||||||
|
import net.corda.nodeapi.internal.ServiceInfo
|
||||||
import net.corda.testing.*
|
import net.corda.testing.*
|
||||||
import net.corda.testing.node.MockNetwork
|
import net.corda.testing.node.MockNetwork
|
||||||
import net.corda.testing.node.MockNetwork.MockNode
|
import net.corda.testing.node.MockNetwork.MockNode
|
||||||
@ -270,11 +270,10 @@ abstract class AbstractNetworkMapServiceTest<out S : AbstractNetworkMapService>
|
|||||||
override fun create(config: NodeConfiguration,
|
override fun create(config: NodeConfiguration,
|
||||||
network: MockNetwork,
|
network: MockNetwork,
|
||||||
networkMapAddr: SingleMessageRecipient?,
|
networkMapAddr: SingleMessageRecipient?,
|
||||||
advertisedServices: Set<ServiceInfo>,
|
|
||||||
id: Int,
|
id: Int,
|
||||||
notaryIdentity: Pair<ServiceInfo, KeyPair>?,
|
notaryIdentity: Pair<ServiceInfo, KeyPair>?,
|
||||||
entropyRoot: BigInteger): MockNode {
|
entropyRoot: BigInteger): MockNode {
|
||||||
return object : MockNode(config, network, null, advertisedServices, id, notaryIdentity, entropyRoot) {
|
return object : MockNode(config, network, null, id, notaryIdentity, entropyRoot) {
|
||||||
override fun makeNetworkMapService(network: MessagingService, networkMapCache: NetworkMapCacheInternal) = NullNetworkMapService
|
override fun makeNetworkMapService(network: MessagingService, networkMapCache: NetworkMapCacheInternal) = NullNetworkMapService
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,12 +2,11 @@ package net.corda.node.services.network
|
|||||||
|
|
||||||
import net.corda.core.messaging.SingleMessageRecipient
|
import net.corda.core.messaging.SingleMessageRecipient
|
||||||
import net.corda.node.services.api.NetworkMapCacheInternal
|
import net.corda.node.services.api.NetworkMapCacheInternal
|
||||||
import net.corda.nodeapi.internal.ServiceInfo
|
|
||||||
import net.corda.node.services.config.NodeConfiguration
|
import net.corda.node.services.config.NodeConfiguration
|
||||||
import net.corda.node.services.messaging.MessagingService
|
import net.corda.node.services.messaging.MessagingService
|
||||||
|
import net.corda.nodeapi.internal.ServiceInfo
|
||||||
import net.corda.testing.node.MockNetwork
|
import net.corda.testing.node.MockNetwork
|
||||||
import net.corda.testing.node.MockNetwork.MockNode
|
import net.corda.testing.node.MockNetwork.MockNode
|
||||||
import net.corda.testing.node.MockServices.Companion.MOCK_VERSION_INFO
|
|
||||||
import java.math.BigInteger
|
import java.math.BigInteger
|
||||||
import java.security.KeyPair
|
import java.security.KeyPair
|
||||||
|
|
||||||
@ -32,11 +31,10 @@ class PersistentNetworkMapServiceTest : AbstractNetworkMapServiceTest<Persistent
|
|||||||
override fun create(config: NodeConfiguration,
|
override fun create(config: NodeConfiguration,
|
||||||
network: MockNetwork,
|
network: MockNetwork,
|
||||||
networkMapAddr: SingleMessageRecipient?,
|
networkMapAddr: SingleMessageRecipient?,
|
||||||
advertisedServices: Set<ServiceInfo>,
|
|
||||||
id: Int,
|
id: Int,
|
||||||
notaryIdentity: Pair<ServiceInfo, KeyPair>?,
|
notaryIdentity: Pair<ServiceInfo, KeyPair>?,
|
||||||
entropyRoot: BigInteger): MockNode {
|
entropyRoot: BigInteger): MockNode {
|
||||||
return object : MockNode(config, network, networkMapAddr, advertisedServices, id, notaryIdentity, entropyRoot) {
|
return object : MockNode(config, network, networkMapAddr, id, notaryIdentity, entropyRoot) {
|
||||||
override fun makeNetworkMapService(network: MessagingService, networkMapCache: NetworkMapCacheInternal) = SwizzleNetworkMapService(network, networkMapCache)
|
override fun makeNetworkMapService(network: MessagingService, networkMapCache: NetworkMapCacheInternal) = SwizzleNetworkMapService(network, networkMapCache)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -135,7 +135,7 @@ class FlowFrameworkTests {
|
|||||||
node2.internals.acceptableLiveFiberCountOnStop = 1
|
node2.internals.acceptableLiveFiberCountOnStop = 1
|
||||||
node2.dispose()
|
node2.dispose()
|
||||||
mockNet.runNetwork()
|
mockNet.runNetwork()
|
||||||
val restoredFlow = node2.restartAndGetRestoredFlow<InitiatedReceiveFlow>(node1)
|
val restoredFlow = node2.restartAndGetRestoredFlow<InitiatedReceiveFlow>()
|
||||||
assertThat(restoredFlow.receivedPayloads[0]).isEqualTo("Hello")
|
assertThat(restoredFlow.receivedPayloads[0]).isEqualTo("Hello")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -184,7 +184,7 @@ class FlowFrameworkTests {
|
|||||||
node2.smm.executor.flush()
|
node2.smm.executor.flush()
|
||||||
node2.internals.disableDBCloseOnStop()
|
node2.internals.disableDBCloseOnStop()
|
||||||
node2.dispose() // kill receiver
|
node2.dispose() // kill receiver
|
||||||
val restoredFlow = node2.restartAndGetRestoredFlow<ReceiveFlow>(node1)
|
val restoredFlow = node2.restartAndGetRestoredFlow<ReceiveFlow>()
|
||||||
assertThat(restoredFlow.receivedPayloads[0]).isEqualTo("Hello")
|
assertThat(restoredFlow.receivedPayloads[0]).isEqualTo("Hello")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -214,7 +214,7 @@ class FlowFrameworkTests {
|
|||||||
assertEquals(1, node2.checkpointStorage.checkpoints().size) // confirm checkpoint
|
assertEquals(1, node2.checkpointStorage.checkpoints().size) // confirm checkpoint
|
||||||
node2.services.networkMapCache.clearNetworkMapCache()
|
node2.services.networkMapCache.clearNetworkMapCache()
|
||||||
}
|
}
|
||||||
val node2b = mockNet.createNode(node2.internals.id, advertisedServices = *node2.internals.advertisedServices.toTypedArray())
|
val node2b = mockNet.createNode(node2.internals.id)
|
||||||
node2.internals.manuallyCloseDB()
|
node2.internals.manuallyCloseDB()
|
||||||
val (firstAgain, fut1) = node2b.getSingleFlow<PingPongFlow>()
|
val (firstAgain, fut1) = node2b.getSingleFlow<PingPongFlow>()
|
||||||
// Run the network which will also fire up the second flow. First message should get deduped. So message data stays in sync.
|
// Run the network which will also fire up the second flow. First message should get deduped. So message data stays in sync.
|
||||||
@ -685,10 +685,10 @@ class FlowFrameworkTests {
|
|||||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
//region Helpers
|
//region Helpers
|
||||||
|
|
||||||
private inline fun <reified P : FlowLogic<*>> StartedNode<MockNode>.restartAndGetRestoredFlow(networkMapNode: StartedNode<*>? = null) = internals.run {
|
private inline fun <reified P : FlowLogic<*>> StartedNode<MockNode>.restartAndGetRestoredFlow() = internals.run {
|
||||||
disableDBCloseOnStop() // Handover DB to new node copy
|
disableDBCloseOnStop() // Handover DB to new node copy
|
||||||
stop()
|
stop()
|
||||||
val newNode = mockNet.createNode(id, advertisedServices = *advertisedServices.toTypedArray())
|
val newNode = mockNet.createNode(id)
|
||||||
newNode.internals.acceptableLiveFiberCountOnStop = 1
|
newNode.internals.acceptableLiveFiberCountOnStop = 1
|
||||||
manuallyCloseDB()
|
manuallyCloseDB()
|
||||||
mockNet.runNetwork() // allow NetworkMapService messages to stabilise and thus start the state machine
|
mockNet.runNetwork() // allow NetworkMapService messages to stabilise and thus start the state machine
|
||||||
|
@ -42,7 +42,6 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) {
|
|||||||
node {
|
node {
|
||||||
name "O=Notary Service,L=Zurich,C=CH"
|
name "O=Notary Service,L=Zurich,C=CH"
|
||||||
notary = [validating : true]
|
notary = [validating : true]
|
||||||
advertisedServices = []
|
|
||||||
p2pPort 10002
|
p2pPort 10002
|
||||||
rpcPort 10003
|
rpcPort 10003
|
||||||
cordapps = []
|
cordapps = []
|
||||||
@ -50,7 +49,6 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) {
|
|||||||
}
|
}
|
||||||
node {
|
node {
|
||||||
name "O=Bank A,L=London,C=GB"
|
name "O=Bank A,L=London,C=GB"
|
||||||
advertisedServices = []
|
|
||||||
p2pPort 10005
|
p2pPort 10005
|
||||||
rpcPort 10006
|
rpcPort 10006
|
||||||
cordapps = []
|
cordapps = []
|
||||||
@ -58,7 +56,6 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) {
|
|||||||
}
|
}
|
||||||
node {
|
node {
|
||||||
name "O=Bank B,L=New York,C=US"
|
name "O=Bank B,L=New York,C=US"
|
||||||
advertisedServices = []
|
|
||||||
p2pPort 10008
|
p2pPort 10008
|
||||||
rpcPort 10009
|
rpcPort 10009
|
||||||
webPort 10010
|
webPort 10010
|
||||||
|
@ -55,14 +55,13 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) {
|
|||||||
node {
|
node {
|
||||||
name "O=Notary Service,L=Zurich,C=CH"
|
name "O=Notary Service,L=Zurich,C=CH"
|
||||||
notary = [validating : true]
|
notary = [validating : true]
|
||||||
advertisedServices = []
|
|
||||||
p2pPort 10002
|
p2pPort 10002
|
||||||
rpcPort 10003
|
rpcPort 10003
|
||||||
cordapps = ["net.corda:finance:$corda_release_version"]
|
cordapps = ["net.corda:finance:$corda_release_version"]
|
||||||
}
|
}
|
||||||
node {
|
node {
|
||||||
name "O=BankOfCorda,L=London,C=GB"
|
name "O=BankOfCorda,L=London,C=GB"
|
||||||
advertisedServices = ["corda.issuer.USD"]
|
extraConfig = [issuableCurrencies : ["USD"]]
|
||||||
p2pPort 10005
|
p2pPort 10005
|
||||||
rpcPort 10006
|
rpcPort 10006
|
||||||
webPort 10007
|
webPort 10007
|
||||||
@ -79,7 +78,6 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) {
|
|||||||
}
|
}
|
||||||
node {
|
node {
|
||||||
name "O=BigCorporation,L=New York,C=US"
|
name "O=BigCorporation,L=New York,C=US"
|
||||||
advertisedServices = []
|
|
||||||
p2pPort 10008
|
p2pPort 10008
|
||||||
rpcPort 10009
|
rpcPort 10009
|
||||||
webPort 10010
|
webPort 10010
|
||||||
|
@ -55,7 +55,6 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) {
|
|||||||
node {
|
node {
|
||||||
name "O=Notary Service,L=Zurich,C=CH"
|
name "O=Notary Service,L=Zurich,C=CH"
|
||||||
notary = [validating : true]
|
notary = [validating : true]
|
||||||
advertisedServices = ["corda.interest_rates"]
|
|
||||||
p2pPort 10002
|
p2pPort 10002
|
||||||
rpcPort 10003
|
rpcPort 10003
|
||||||
webPort 10004
|
webPort 10004
|
||||||
@ -64,7 +63,6 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) {
|
|||||||
}
|
}
|
||||||
node {
|
node {
|
||||||
name "O=Bank A,L=London,C=GB"
|
name "O=Bank A,L=London,C=GB"
|
||||||
advertisedServices = []
|
|
||||||
p2pPort 10005
|
p2pPort 10005
|
||||||
rpcPort 10006
|
rpcPort 10006
|
||||||
webPort 10007
|
webPort 10007
|
||||||
@ -73,7 +71,6 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) {
|
|||||||
}
|
}
|
||||||
node {
|
node {
|
||||||
name "O=Bank B,L=New York,C=US"
|
name "O=Bank B,L=New York,C=US"
|
||||||
advertisedServices = []
|
|
||||||
p2pPort 10008
|
p2pPort 10008
|
||||||
rpcPort 10009
|
rpcPort 10009
|
||||||
webPort 10010
|
webPort 10010
|
||||||
|
@ -49,9 +49,9 @@ abstract class Simulation(val networkSendManuallyPumped: Boolean,
|
|||||||
// This puts together a mock network of SimulatedNodes.
|
// This puts together a mock network of SimulatedNodes.
|
||||||
|
|
||||||
open class SimulatedNode(config: NodeConfiguration, mockNet: MockNetwork, networkMapAddress: SingleMessageRecipient?,
|
open class SimulatedNode(config: NodeConfiguration, mockNet: MockNetwork, networkMapAddress: SingleMessageRecipient?,
|
||||||
advertisedServices: Set<ServiceInfo>, id: Int, notaryIdentity: Pair<ServiceInfo, KeyPair>?,
|
id: Int, notaryIdentity: Pair<ServiceInfo, KeyPair>?,
|
||||||
entropyRoot: BigInteger)
|
entropyRoot: BigInteger)
|
||||||
: MockNetwork.MockNode(config, mockNet, networkMapAddress, advertisedServices, id, notaryIdentity, entropyRoot) {
|
: MockNetwork.MockNode(config, mockNet, networkMapAddress, id, notaryIdentity, entropyRoot) {
|
||||||
override val started: StartedNode<SimulatedNode>? get() = uncheckedCast(super.started)
|
override val started: StartedNode<SimulatedNode>? get() = uncheckedCast(super.started)
|
||||||
override fun findMyLocation(): WorldMapLocation? {
|
override fun findMyLocation(): WorldMapLocation? {
|
||||||
return configuration.myLegalName.locality.let { CityDatabase[it] }
|
return configuration.myLegalName.locality.let { CityDatabase[it] }
|
||||||
@ -62,15 +62,14 @@ abstract class Simulation(val networkSendManuallyPumped: Boolean,
|
|||||||
var counter = 0
|
var counter = 0
|
||||||
|
|
||||||
override fun create(config: NodeConfiguration, network: MockNetwork, networkMapAddr: SingleMessageRecipient?,
|
override fun create(config: NodeConfiguration, network: MockNetwork, networkMapAddr: SingleMessageRecipient?,
|
||||||
advertisedServices: Set<ServiceInfo>, id: Int, notaryIdentity: Pair<ServiceInfo, KeyPair>?,
|
id: Int, notaryIdentity: Pair<ServiceInfo, KeyPair>?, entropyRoot: BigInteger): SimulatedNode {
|
||||||
entropyRoot: BigInteger): SimulatedNode {
|
|
||||||
val letter = 'A' + counter
|
val letter = 'A' + counter
|
||||||
val (city, country) = bankLocations[counter++ % bankLocations.size]
|
val (city, country) = bankLocations[counter++ % bankLocations.size]
|
||||||
|
|
||||||
val cfg = testNodeConfiguration(
|
val cfg = testNodeConfiguration(
|
||||||
baseDirectory = config.baseDirectory,
|
baseDirectory = config.baseDirectory,
|
||||||
myLegalName = CordaX500Name(organisation = "Bank $letter", locality = city, country = country))
|
myLegalName = CordaX500Name(organisation = "Bank $letter", locality = city, country = country))
|
||||||
return SimulatedNode(cfg, network, networkMapAddr, advertisedServices, id, notaryIdentity, entropyRoot)
|
return SimulatedNode(cfg, network, networkMapAddr, id, notaryIdentity, entropyRoot)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun createAll(): List<SimulatedNode> {
|
fun createAll(): List<SimulatedNode> {
|
||||||
@ -85,25 +84,23 @@ abstract class Simulation(val networkSendManuallyPumped: Boolean,
|
|||||||
|
|
||||||
object NetworkMapNodeFactory : MockNetwork.Factory<SimulatedNode> {
|
object NetworkMapNodeFactory : MockNetwork.Factory<SimulatedNode> {
|
||||||
override fun create(config: NodeConfiguration, network: MockNetwork, networkMapAddr: SingleMessageRecipient?,
|
override fun create(config: NodeConfiguration, network: MockNetwork, networkMapAddr: SingleMessageRecipient?,
|
||||||
advertisedServices: Set<ServiceInfo>, id: Int, notaryIdentity: Pair<ServiceInfo, KeyPair>?,
|
id: Int, notaryIdentity: Pair<ServiceInfo, KeyPair>?, entropyRoot: BigInteger): SimulatedNode {
|
||||||
entropyRoot: BigInteger): SimulatedNode {
|
|
||||||
val cfg = testNodeConfiguration(
|
val cfg = testNodeConfiguration(
|
||||||
baseDirectory = config.baseDirectory,
|
baseDirectory = config.baseDirectory,
|
||||||
myLegalName = DUMMY_MAP.name)
|
myLegalName = DUMMY_MAP.name)
|
||||||
return object : SimulatedNode(cfg, network, networkMapAddr, advertisedServices, id, notaryIdentity, entropyRoot) {}
|
return object : SimulatedNode(cfg, network, networkMapAddr, id, notaryIdentity, entropyRoot) {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
object NotaryNodeFactory : MockNetwork.Factory<SimulatedNode> {
|
object NotaryNodeFactory : MockNetwork.Factory<SimulatedNode> {
|
||||||
override fun create(config: NodeConfiguration, network: MockNetwork, networkMapAddr: SingleMessageRecipient?,
|
override fun create(config: NodeConfiguration, network: MockNetwork, networkMapAddr: SingleMessageRecipient?,
|
||||||
advertisedServices: Set<ServiceInfo>, id: Int, notaryIdentity: Pair<ServiceInfo, KeyPair>?,
|
id: Int, notaryIdentity: Pair<ServiceInfo, KeyPair>?, entropyRoot: BigInteger): SimulatedNode {
|
||||||
entropyRoot: BigInteger): SimulatedNode {
|
|
||||||
requireNotNull(config.notary)
|
requireNotNull(config.notary)
|
||||||
val cfg = testNodeConfiguration(
|
val cfg = testNodeConfiguration(
|
||||||
baseDirectory = config.baseDirectory,
|
baseDirectory = config.baseDirectory,
|
||||||
myLegalName = DUMMY_NOTARY.name,
|
myLegalName = DUMMY_NOTARY.name,
|
||||||
notaryConfig = config.notary)
|
notaryConfig = config.notary)
|
||||||
return SimulatedNode(cfg, network, networkMapAddr, advertisedServices, id, notaryIdentity, entropyRoot)
|
return SimulatedNode(cfg, network, networkMapAddr, id, notaryIdentity, entropyRoot)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -112,12 +109,11 @@ abstract class Simulation(val networkSendManuallyPumped: Boolean,
|
|||||||
val RATES_SERVICE_NAME = CordaX500Name(organisation = "Rates Service Provider", locality = "Madrid", country = "ES")
|
val RATES_SERVICE_NAME = CordaX500Name(organisation = "Rates Service Provider", locality = "Madrid", country = "ES")
|
||||||
|
|
||||||
override fun create(config: NodeConfiguration, network: MockNetwork, networkMapAddr: SingleMessageRecipient?,
|
override fun create(config: NodeConfiguration, network: MockNetwork, networkMapAddr: SingleMessageRecipient?,
|
||||||
advertisedServices: Set<ServiceInfo>, id: Int, notaryIdentity: Pair<ServiceInfo, KeyPair>?,
|
id: Int, notaryIdentity: Pair<ServiceInfo, KeyPair>?, entropyRoot: BigInteger): SimulatedNode {
|
||||||
entropyRoot: BigInteger): SimulatedNode {
|
|
||||||
val cfg = testNodeConfiguration(
|
val cfg = testNodeConfiguration(
|
||||||
baseDirectory = config.baseDirectory,
|
baseDirectory = config.baseDirectory,
|
||||||
myLegalName = RATES_SERVICE_NAME)
|
myLegalName = RATES_SERVICE_NAME)
|
||||||
return object : SimulatedNode(cfg, network, networkMapAddr, advertisedServices, id, notaryIdentity, entropyRoot) {
|
return object : SimulatedNode(cfg, network, networkMapAddr, id, notaryIdentity, entropyRoot) {
|
||||||
override fun start() = super.start().apply {
|
override fun start() = super.start().apply {
|
||||||
registerInitiatedFlow(NodeInterestRates.FixQueryHandler::class.java)
|
registerInitiatedFlow(NodeInterestRates.FixQueryHandler::class.java)
|
||||||
registerInitiatedFlow(NodeInterestRates.FixSignHandler::class.java)
|
registerInitiatedFlow(NodeInterestRates.FixSignHandler::class.java)
|
||||||
@ -133,12 +129,11 @@ abstract class Simulation(val networkSendManuallyPumped: Boolean,
|
|||||||
|
|
||||||
object RegulatorFactory : MockNetwork.Factory<SimulatedNode> {
|
object RegulatorFactory : MockNetwork.Factory<SimulatedNode> {
|
||||||
override fun create(config: NodeConfiguration, network: MockNetwork, networkMapAddr: SingleMessageRecipient?,
|
override fun create(config: NodeConfiguration, network: MockNetwork, networkMapAddr: SingleMessageRecipient?,
|
||||||
advertisedServices: Set<ServiceInfo>, id: Int, notaryIdentity: Pair<ServiceInfo, KeyPair>?,
|
id: Int, notaryIdentity: Pair<ServiceInfo, KeyPair>?, entropyRoot: BigInteger): SimulatedNode {
|
||||||
entropyRoot: BigInteger): SimulatedNode {
|
|
||||||
val cfg = testNodeConfiguration(
|
val cfg = testNodeConfiguration(
|
||||||
baseDirectory = config.baseDirectory,
|
baseDirectory = config.baseDirectory,
|
||||||
myLegalName = DUMMY_REGULATOR.name)
|
myLegalName = DUMMY_REGULATOR.name)
|
||||||
return object : SimulatedNode(cfg, network, networkMapAddr, advertisedServices, id, notaryIdentity, entropyRoot) {
|
return object : SimulatedNode(cfg, network, networkMapAddr, id, notaryIdentity, entropyRoot) {
|
||||||
// TODO: Regulatory nodes don't actually exist properly, this is a last minute demo request.
|
// TODO: Regulatory nodes don't actually exist properly, this is a last minute demo request.
|
||||||
// So we just fire a message at a node that doesn't know how to handle it, and it'll ignore it.
|
// So we just fire a message at a node that doesn't know how to handle it, and it'll ignore it.
|
||||||
// But that's fine for visualisation purposes.
|
// But that's fine for visualisation purposes.
|
||||||
|
@ -68,13 +68,11 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) {
|
|||||||
node {
|
node {
|
||||||
name "O=Notary Service,L=Zurich,C=CH"
|
name "O=Notary Service,L=Zurich,C=CH"
|
||||||
notary = [validating : true]
|
notary = [validating : true]
|
||||||
advertisedServices = []
|
|
||||||
p2pPort 10002
|
p2pPort 10002
|
||||||
cordapps = ["net.corda:finance:$corda_release_version"]
|
cordapps = ["net.corda:finance:$corda_release_version"]
|
||||||
}
|
}
|
||||||
node {
|
node {
|
||||||
name "O=Bank A,L=London,C=GB"
|
name "O=Bank A,L=London,C=GB"
|
||||||
advertisedServices = []
|
|
||||||
p2pPort 10004
|
p2pPort 10004
|
||||||
webPort 10005
|
webPort 10005
|
||||||
rpcPort 10006
|
rpcPort 10006
|
||||||
@ -83,7 +81,6 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) {
|
|||||||
}
|
}
|
||||||
node {
|
node {
|
||||||
name "O=Bank B,L=New York,C=US"
|
name "O=Bank B,L=New York,C=US"
|
||||||
advertisedServices = []
|
|
||||||
p2pPort 10007
|
p2pPort 10007
|
||||||
webPort 10008
|
webPort 10008
|
||||||
rpcPort 10009
|
rpcPort 10009
|
||||||
@ -92,7 +89,6 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) {
|
|||||||
}
|
}
|
||||||
node {
|
node {
|
||||||
name "O=Bank C,L=Tokyo,C=JP"
|
name "O=Bank C,L=Tokyo,C=JP"
|
||||||
advertisedServices = []
|
|
||||||
p2pPort 10010
|
p2pPort 10010
|
||||||
webPort 10011
|
webPort 10011
|
||||||
rpcPort 10012
|
rpcPort 10012
|
||||||
|
@ -56,13 +56,11 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) {
|
|||||||
node {
|
node {
|
||||||
name "O=Notary Service,L=Zurich,C=CH"
|
name "O=Notary Service,L=Zurich,C=CH"
|
||||||
notary = [validating : true]
|
notary = [validating : true]
|
||||||
advertisedServices = []
|
|
||||||
p2pPort 10002
|
p2pPort 10002
|
||||||
cordapps = ["net.corda:finance:$corda_release_version"]
|
cordapps = ["net.corda:finance:$corda_release_version"]
|
||||||
}
|
}
|
||||||
node {
|
node {
|
||||||
name "O=Bank A,L=London,C=GB"
|
name "O=Bank A,L=London,C=GB"
|
||||||
advertisedServices = []
|
|
||||||
p2pPort 10005
|
p2pPort 10005
|
||||||
rpcPort 10006
|
rpcPort 10006
|
||||||
cordapps = ["net.corda:finance:$corda_release_version"]
|
cordapps = ["net.corda:finance:$corda_release_version"]
|
||||||
@ -70,7 +68,6 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) {
|
|||||||
}
|
}
|
||||||
node {
|
node {
|
||||||
name "O=Bank B,L=New York,C=US"
|
name "O=Bank B,L=New York,C=US"
|
||||||
advertisedServices = []
|
|
||||||
p2pPort 10008
|
p2pPort 10008
|
||||||
rpcPort 10009
|
rpcPort 10009
|
||||||
cordapps = ["net.corda:finance:$corda_release_version"]
|
cordapps = ["net.corda:finance:$corda_release_version"]
|
||||||
@ -78,7 +75,6 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) {
|
|||||||
}
|
}
|
||||||
node {
|
node {
|
||||||
name "O=BankOfCorda,L=New York,C=US"
|
name "O=BankOfCorda,L=New York,C=US"
|
||||||
advertisedServices = []
|
|
||||||
p2pPort 10011
|
p2pPort 10011
|
||||||
rpcPort 10012
|
rpcPort 10012
|
||||||
cordapps = ["net.corda:finance:$corda_release_version"]
|
cordapps = ["net.corda:finance:$corda_release_version"]
|
||||||
|
@ -30,7 +30,6 @@ import net.corda.node.utilities.ServiceIdentityGenerator
|
|||||||
import net.corda.nodeapi.User
|
import net.corda.nodeapi.User
|
||||||
import net.corda.nodeapi.config.parseAs
|
import net.corda.nodeapi.config.parseAs
|
||||||
import net.corda.nodeapi.config.toConfig
|
import net.corda.nodeapi.config.toConfig
|
||||||
import net.corda.nodeapi.internal.ServiceInfo
|
|
||||||
import net.corda.nodeapi.internal.addShutdownHook
|
import net.corda.nodeapi.internal.addShutdownHook
|
||||||
import net.corda.testing.*
|
import net.corda.testing.*
|
||||||
import net.corda.testing.node.MockServices.Companion.MOCK_VERSION_INFO
|
import net.corda.testing.node.MockServices.Companion.MOCK_VERSION_INFO
|
||||||
@ -78,7 +77,6 @@ interface DriverDSLExposedInterface : CordformContext {
|
|||||||
* when called from Java code.
|
* when called from Java code.
|
||||||
* @param providedName Optional name of the node, which will be its legal name in [Party]. Defaults to something
|
* @param providedName Optional name of the node, which will be its legal name in [Party]. Defaults to something
|
||||||
* random. Note that this must be unique as the driver uses it as a primary key!
|
* random. Note that this must be unique as the driver uses it as a primary key!
|
||||||
* @param advertisedServices The set of services to be advertised by the node. Defaults to empty set.
|
|
||||||
* @param verifierType The type of transaction verifier to use. See: [VerifierType]
|
* @param verifierType The type of transaction verifier to use. See: [VerifierType]
|
||||||
* @param rpcUsers List of users who are authorised to use the RPC system. Defaults to empty list.
|
* @param rpcUsers List of users who are authorised to use the RPC system. Defaults to empty list.
|
||||||
* @param startInSameProcess Determines if the node should be started inside the same process the Driver is running
|
* @param startInSameProcess Determines if the node should be started inside the same process the Driver is running
|
||||||
@ -88,7 +86,6 @@ interface DriverDSLExposedInterface : CordformContext {
|
|||||||
fun startNode(
|
fun startNode(
|
||||||
defaultParameters: NodeParameters = NodeParameters(),
|
defaultParameters: NodeParameters = NodeParameters(),
|
||||||
providedName: CordaX500Name? = defaultParameters.providedName,
|
providedName: CordaX500Name? = defaultParameters.providedName,
|
||||||
advertisedServices: Set<ServiceInfo> = defaultParameters.advertisedServices,
|
|
||||||
rpcUsers: List<User> = defaultParameters.rpcUsers,
|
rpcUsers: List<User> = defaultParameters.rpcUsers,
|
||||||
verifierType: VerifierType = defaultParameters.verifierType,
|
verifierType: VerifierType = defaultParameters.verifierType,
|
||||||
customOverrides: Map<String, Any?> = defaultParameters.customOverrides,
|
customOverrides: Map<String, Any?> = defaultParameters.customOverrides,
|
||||||
@ -267,7 +264,6 @@ sealed class PortAllocation {
|
|||||||
*/
|
*/
|
||||||
data class NodeParameters(
|
data class NodeParameters(
|
||||||
val providedName: CordaX500Name? = null,
|
val providedName: CordaX500Name? = null,
|
||||||
val advertisedServices: Set<ServiceInfo> = emptySet(),
|
|
||||||
val rpcUsers: List<User> = emptyList(),
|
val rpcUsers: List<User> = emptyList(),
|
||||||
val verifierType: VerifierType = VerifierType.InMemory,
|
val verifierType: VerifierType = VerifierType.InMemory,
|
||||||
val customOverrides: Map<String, Any?> = emptyMap(),
|
val customOverrides: Map<String, Any?> = emptyMap(),
|
||||||
@ -275,7 +271,6 @@ data class NodeParameters(
|
|||||||
val maximumHeapSize: String = "200m"
|
val maximumHeapSize: String = "200m"
|
||||||
) {
|
) {
|
||||||
fun setProvidedName(providedName: CordaX500Name?) = copy(providedName = providedName)
|
fun setProvidedName(providedName: CordaX500Name?) = copy(providedName = providedName)
|
||||||
fun setAdvertisedServices(advertisedServices: Set<ServiceInfo>) = copy(advertisedServices = advertisedServices)
|
|
||||||
fun setRpcUsers(rpcUsers: List<User>) = copy(rpcUsers = rpcUsers)
|
fun setRpcUsers(rpcUsers: List<User>) = copy(rpcUsers = rpcUsers)
|
||||||
fun setVerifierType(verifierType: VerifierType) = copy(verifierType = verifierType)
|
fun setVerifierType(verifierType: VerifierType) = copy(verifierType = verifierType)
|
||||||
fun setCustomerOverrides(customOverrides: Map<String, Any?>) = copy(customOverrides = customOverrides)
|
fun setCustomerOverrides(customOverrides: Map<String, Any?>) = copy(customOverrides = customOverrides)
|
||||||
@ -686,7 +681,6 @@ class DriverDSL(
|
|||||||
override fun startNode(
|
override fun startNode(
|
||||||
defaultParameters: NodeParameters,
|
defaultParameters: NodeParameters,
|
||||||
providedName: CordaX500Name?,
|
providedName: CordaX500Name?,
|
||||||
advertisedServices: Set<ServiceInfo>,
|
|
||||||
rpcUsers: List<User>,
|
rpcUsers: List<User>,
|
||||||
verifierType: VerifierType,
|
verifierType: VerifierType,
|
||||||
customOverrides: Map<String, Any?>,
|
customOverrides: Map<String, Any?>,
|
||||||
@ -710,7 +704,6 @@ class DriverDSL(
|
|||||||
"p2pAddress" to p2pAddress.toString(),
|
"p2pAddress" to p2pAddress.toString(),
|
||||||
"rpcAddress" to rpcAddress.toString(),
|
"rpcAddress" to rpcAddress.toString(),
|
||||||
"webAddress" to webAddress.toString(),
|
"webAddress" to webAddress.toString(),
|
||||||
"extraAdvertisedServiceIds" to advertisedServices.map { it.toString() },
|
|
||||||
"networkMapService" to networkMapServiceConfigLookup(name),
|
"networkMapService" to networkMapServiceConfigLookup(name),
|
||||||
"useTestClock" to useTestClock,
|
"useTestClock" to useTestClock,
|
||||||
"rpcUsers" to if (rpcUsers.isEmpty()) defaultRpcUserList else rpcUsers.map { it.toMap() },
|
"rpcUsers" to if (rpcUsers.isEmpty()) defaultRpcUserList else rpcUsers.map { it.toMap() },
|
||||||
@ -741,7 +734,6 @@ class DriverDSL(
|
|||||||
baseDirectory = baseDirectory(name),
|
baseDirectory = baseDirectory(name),
|
||||||
allowMissingConfig = true,
|
allowMissingConfig = true,
|
||||||
configOverrides = node.config + notary + mapOf(
|
configOverrides = node.config + notary + mapOf(
|
||||||
"extraAdvertisedServiceIds" to node.advertisedServices,
|
|
||||||
"networkMapService" to networkMapServiceConfigLookup(name),
|
"networkMapService" to networkMapServiceConfigLookup(name),
|
||||||
"rpcUsers" to if (rpcUsers.isEmpty()) defaultRpcUserList else rpcUsers
|
"rpcUsers" to if (rpcUsers.isEmpty()) defaultRpcUserList else rpcUsers
|
||||||
)
|
)
|
||||||
@ -935,7 +927,7 @@ class DriverDSL(
|
|||||||
// Write node.conf
|
// Write node.conf
|
||||||
writeConfig(nodeConf.baseDirectory, "node.conf", config)
|
writeConfig(nodeConf.baseDirectory, "node.conf", config)
|
||||||
// TODO pass the version in?
|
// TODO pass the version in?
|
||||||
val node = Node(nodeConf, nodeConf.calculateServices(), MOCK_VERSION_INFO, initialiseSerialization = false).start()
|
val node = Node(nodeConf, MOCK_VERSION_INFO, initialiseSerialization = false).start()
|
||||||
val nodeThread = thread(name = nodeConf.myLegalName.organisation) {
|
val nodeThread = thread(name = nodeConf.myLegalName.organisation) {
|
||||||
node.internals.run()
|
node.internals.run()
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,6 @@ import net.corda.core.identity.CordaX500Name
|
|||||||
import net.corda.node.services.config.NotaryConfig
|
import net.corda.node.services.config.NotaryConfig
|
||||||
import net.corda.nodeapi.User
|
import net.corda.nodeapi.User
|
||||||
import net.corda.nodeapi.config.toConfig
|
import net.corda.nodeapi.config.toConfig
|
||||||
import net.corda.nodeapi.internal.ServiceInfo
|
|
||||||
|
|
||||||
fun CordformDefinition.node(configure: CordformNode.() -> Unit) {
|
fun CordformDefinition.node(configure: CordformNode.() -> Unit) {
|
||||||
addNode { cordformNode -> cordformNode.configure() }
|
addNode { cordformNode -> cordformNode.configure() }
|
||||||
@ -23,7 +22,3 @@ fun CordformNode.rpcUsers(vararg users: User) {
|
|||||||
fun CordformNode.notary(notaryConfig: NotaryConfig) {
|
fun CordformNode.notary(notaryConfig: NotaryConfig) {
|
||||||
notary = notaryConfig.toConfig().root().unwrapped()
|
notary = notaryConfig.toConfig().root().unwrapped()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun CordformNode.advertisedServices(vararg services: ServiceInfo) {
|
|
||||||
advertisedServices = services.map { it.toString() }
|
|
||||||
}
|
|
||||||
|
@ -115,15 +115,13 @@ class MockNetwork(private val networkSendManuallyPumped: Boolean = false,
|
|||||||
* but can be overriden to cause nodes to have stable or colliding identity/service keys.
|
* but can be overriden to cause nodes to have stable or colliding identity/service keys.
|
||||||
*/
|
*/
|
||||||
fun create(config: NodeConfiguration, network: MockNetwork, networkMapAddr: SingleMessageRecipient?,
|
fun create(config: NodeConfiguration, network: MockNetwork, networkMapAddr: SingleMessageRecipient?,
|
||||||
advertisedServices: Set<ServiceInfo>, id: Int, notaryIdentity: Pair<ServiceInfo, KeyPair>?,
|
id: Int, notaryIdentity: Pair<ServiceInfo, KeyPair>?, entropyRoot: BigInteger): N
|
||||||
entropyRoot: BigInteger): N
|
|
||||||
}
|
}
|
||||||
|
|
||||||
object DefaultFactory : Factory<MockNode> {
|
object DefaultFactory : Factory<MockNode> {
|
||||||
override fun create(config: NodeConfiguration, network: MockNetwork, networkMapAddr: SingleMessageRecipient?,
|
override fun create(config: NodeConfiguration, network: MockNetwork, networkMapAddr: SingleMessageRecipient?,
|
||||||
advertisedServices: Set<ServiceInfo>, id: Int, notaryIdentity: Pair<ServiceInfo, KeyPair>?,
|
id: Int, notaryIdentity: Pair<ServiceInfo, KeyPair>?, entropyRoot: BigInteger): MockNode {
|
||||||
entropyRoot: BigInteger): MockNode {
|
return MockNode(config, network, networkMapAddr, id, notaryIdentity, entropyRoot)
|
||||||
return MockNode(config, network, networkMapAddr, advertisedServices, id, notaryIdentity, entropyRoot)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -159,11 +157,10 @@ class MockNetwork(private val networkSendManuallyPumped: Boolean = false,
|
|||||||
open class MockNode(config: NodeConfiguration,
|
open class MockNode(config: NodeConfiguration,
|
||||||
val mockNet: MockNetwork,
|
val mockNet: MockNetwork,
|
||||||
override val networkMapAddress: SingleMessageRecipient?,
|
override val networkMapAddress: SingleMessageRecipient?,
|
||||||
advertisedServices: Set<ServiceInfo>,
|
|
||||||
val id: Int,
|
val id: Int,
|
||||||
internal val notaryIdentity: Pair<ServiceInfo, KeyPair>?,
|
internal val notaryIdentity: Pair<ServiceInfo, KeyPair>?,
|
||||||
val entropyRoot: BigInteger = BigInteger.valueOf(random63BitValue())) :
|
val entropyRoot: BigInteger = BigInteger.valueOf(random63BitValue())) :
|
||||||
AbstractNode(config, advertisedServices, TestClock(), MOCK_VERSION_INFO, mockNet.busyLatch) {
|
AbstractNode(config, TestClock(), MOCK_VERSION_INFO, mockNet.busyLatch) {
|
||||||
var counter = entropyRoot
|
var counter = entropyRoot
|
||||||
override val log: Logger = loggerFor<MockNode>()
|
override val log: Logger = loggerFor<MockNode>()
|
||||||
override val serverThread: AffinityExecutor =
|
override val serverThread: AffinityExecutor =
|
||||||
@ -303,7 +300,6 @@ class MockNetwork(private val networkSendManuallyPumped: Boolean = false,
|
|||||||
nodeFactory = nodeFactory ?: defaultFactory,
|
nodeFactory = nodeFactory ?: defaultFactory,
|
||||||
legalName = MOCK_NET_MAP.name,
|
legalName = MOCK_NET_MAP.name,
|
||||||
notaryIdentity = null,
|
notaryIdentity = null,
|
||||||
advertisedServices = arrayOf(),
|
|
||||||
entropyRoot = BigInteger.valueOf(random63BitValue()),
|
entropyRoot = BigInteger.valueOf(random63BitValue()),
|
||||||
configOverrides = {},
|
configOverrides = {},
|
||||||
start = true
|
start = true
|
||||||
@ -315,18 +311,16 @@ class MockNetwork(private val networkSendManuallyPumped: Boolean = false,
|
|||||||
fun createUnstartedNode(forcedID: Int? = null,
|
fun createUnstartedNode(forcedID: Int? = null,
|
||||||
legalName: CordaX500Name? = null, notaryIdentity: Pair<ServiceInfo, KeyPair>? = null,
|
legalName: CordaX500Name? = null, notaryIdentity: Pair<ServiceInfo, KeyPair>? = null,
|
||||||
entropyRoot: BigInteger = BigInteger.valueOf(random63BitValue()),
|
entropyRoot: BigInteger = BigInteger.valueOf(random63BitValue()),
|
||||||
vararg advertisedServices: ServiceInfo,
|
|
||||||
configOverrides: (NodeConfiguration) -> Any? = {}): MockNode {
|
configOverrides: (NodeConfiguration) -> Any? = {}): MockNode {
|
||||||
return createUnstartedNode(forcedID, defaultFactory, legalName, notaryIdentity, entropyRoot, *advertisedServices, configOverrides = configOverrides)
|
return createUnstartedNode(forcedID, defaultFactory, legalName, notaryIdentity, entropyRoot, configOverrides = configOverrides)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun <N : MockNode> createUnstartedNode(forcedID: Int? = null, nodeFactory: Factory<N>,
|
fun <N : MockNode> createUnstartedNode(forcedID: Int? = null, nodeFactory: Factory<N>,
|
||||||
legalName: CordaX500Name? = null, notaryIdentity: Pair<ServiceInfo, KeyPair>? = null,
|
legalName: CordaX500Name? = null, notaryIdentity: Pair<ServiceInfo, KeyPair>? = null,
|
||||||
entropyRoot: BigInteger = BigInteger.valueOf(random63BitValue()),
|
entropyRoot: BigInteger = BigInteger.valueOf(random63BitValue()),
|
||||||
vararg advertisedServices: ServiceInfo,
|
|
||||||
configOverrides: (NodeConfiguration) -> Any? = {}): N {
|
configOverrides: (NodeConfiguration) -> Any? = {}): N {
|
||||||
val networkMapAddress = networkMapNode.network.myAddress
|
val networkMapAddress = networkMapNode.network.myAddress
|
||||||
return createNodeImpl(networkMapAddress, forcedID, nodeFactory, false, legalName, notaryIdentity, entropyRoot, advertisedServices, configOverrides)
|
return createNodeImpl(networkMapAddress, forcedID, nodeFactory, false, legalName, notaryIdentity, entropyRoot, configOverrides)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -340,25 +334,22 @@ class MockNetwork(private val networkSendManuallyPumped: Boolean = false,
|
|||||||
fun createNode(forcedID: Int? = null,
|
fun createNode(forcedID: Int? = null,
|
||||||
legalName: CordaX500Name? = null, notaryIdentity: Pair<ServiceInfo, KeyPair>? = null,
|
legalName: CordaX500Name? = null, notaryIdentity: Pair<ServiceInfo, KeyPair>? = null,
|
||||||
entropyRoot: BigInteger = BigInteger.valueOf(random63BitValue()),
|
entropyRoot: BigInteger = BigInteger.valueOf(random63BitValue()),
|
||||||
vararg advertisedServices: ServiceInfo,
|
|
||||||
configOverrides: (NodeConfiguration) -> Any? = {}): StartedNode<MockNode> {
|
configOverrides: (NodeConfiguration) -> Any? = {}): StartedNode<MockNode> {
|
||||||
return createNode(forcedID, defaultFactory, legalName, notaryIdentity, entropyRoot, *advertisedServices, configOverrides = configOverrides)
|
return createNode(forcedID, defaultFactory, legalName, notaryIdentity, entropyRoot, configOverrides = configOverrides)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Like the other [createNode] but takes a [Factory] and propagates its [MockNode] subtype. */
|
/** Like the other [createNode] but takes a [Factory] and propagates its [MockNode] subtype. */
|
||||||
fun <N : MockNode> createNode(forcedID: Int? = null, nodeFactory: Factory<N>,
|
fun <N : MockNode> createNode(forcedID: Int? = null, nodeFactory: Factory<N>,
|
||||||
legalName: CordaX500Name? = null, notaryIdentity: Pair<ServiceInfo, KeyPair>? = null,
|
legalName: CordaX500Name? = null, notaryIdentity: Pair<ServiceInfo, KeyPair>? = null,
|
||||||
entropyRoot: BigInteger = BigInteger.valueOf(random63BitValue()),
|
entropyRoot: BigInteger = BigInteger.valueOf(random63BitValue()),
|
||||||
vararg advertisedServices: ServiceInfo,
|
|
||||||
configOverrides: (NodeConfiguration) -> Any? = {}): StartedNode<N> {
|
configOverrides: (NodeConfiguration) -> Any? = {}): StartedNode<N> {
|
||||||
val networkMapAddress = networkMapNode.network.myAddress
|
val networkMapAddress = networkMapNode.network.myAddress
|
||||||
return uncheckedCast(createNodeImpl(networkMapAddress, forcedID, nodeFactory, true, legalName, notaryIdentity, entropyRoot, advertisedServices, configOverrides).started)!!
|
return uncheckedCast(createNodeImpl(networkMapAddress, forcedID, nodeFactory, true, legalName, notaryIdentity, entropyRoot, configOverrides).started)!!
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun <N : MockNode> createNodeImpl(networkMapAddress: SingleMessageRecipient?, forcedID: Int?, nodeFactory: Factory<N>,
|
private fun <N : MockNode> createNodeImpl(networkMapAddress: SingleMessageRecipient?, forcedID: Int?, nodeFactory: Factory<N>,
|
||||||
start: Boolean, legalName: CordaX500Name?, notaryIdentity: Pair<ServiceInfo, KeyPair>?,
|
start: Boolean, legalName: CordaX500Name?, notaryIdentity: Pair<ServiceInfo, KeyPair>?,
|
||||||
entropyRoot: BigInteger,
|
entropyRoot: BigInteger,
|
||||||
advertisedServices: Array<out ServiceInfo>,
|
|
||||||
configOverrides: (NodeConfiguration) -> Any?): N {
|
configOverrides: (NodeConfiguration) -> Any?): N {
|
||||||
val id = forcedID ?: nextNodeId++
|
val id = forcedID ?: nextNodeId++
|
||||||
val config = testNodeConfiguration(
|
val config = testNodeConfiguration(
|
||||||
@ -367,7 +358,7 @@ class MockNetwork(private val networkSendManuallyPumped: Boolean = false,
|
|||||||
whenever(it.dataSourceProperties).thenReturn(makeTestDataSourceProperties("node_${id}_net_$networkId"))
|
whenever(it.dataSourceProperties).thenReturn(makeTestDataSourceProperties("node_${id}_net_$networkId"))
|
||||||
configOverrides(it)
|
configOverrides(it)
|
||||||
}
|
}
|
||||||
return nodeFactory.create(config, this, networkMapAddress, advertisedServices.toSet(), id, notaryIdentity, entropyRoot).apply {
|
return nodeFactory.create(config, this, networkMapAddress, id, notaryIdentity, entropyRoot).apply {
|
||||||
if (start) {
|
if (start) {
|
||||||
start()
|
start()
|
||||||
if (threadPerNode && networkMapAddress != null) nodeReadyFuture.getOrThrow() // XXX: What about manually-started nodes?
|
if (threadPerNode && networkMapAddress != null) nodeReadyFuture.getOrThrow() // XXX: What about manually-started nodes?
|
||||||
@ -425,7 +416,7 @@ class MockNetwork(private val networkSendManuallyPumped: Boolean = false,
|
|||||||
return when (msgRecipient) {
|
return when (msgRecipient) {
|
||||||
is SingleMessageRecipient -> nodes.single { it.started!!.network.myAddress == msgRecipient }
|
is SingleMessageRecipient -> nodes.single { it.started!!.network.myAddress == msgRecipient }
|
||||||
is InMemoryMessagingNetwork.ServiceHandle -> {
|
is InMemoryMessagingNetwork.ServiceHandle -> {
|
||||||
nodes.firstOrNull { it.advertisedServices.any { it.name == msgRecipient.party.name } }
|
nodes.firstOrNull { it.started!!.info.isLegalIdentity(msgRecipient.party) }
|
||||||
?: throw IllegalArgumentException("Couldn't find node advertising service with owning party name: ${msgRecipient.party.name} ")
|
?: throw IllegalArgumentException("Couldn't find node advertising service with owning party name: ${msgRecipient.party.name} ")
|
||||||
}
|
}
|
||||||
else -> throw IllegalArgumentException("Method not implemented for different type of message recipients")
|
else -> throw IllegalArgumentException("Method not implemented for different type of message recipients")
|
||||||
|
@ -14,7 +14,6 @@ import net.corda.node.utilities.ServiceIdentityGenerator
|
|||||||
import net.corda.nodeapi.User
|
import net.corda.nodeapi.User
|
||||||
import net.corda.nodeapi.config.parseAs
|
import net.corda.nodeapi.config.parseAs
|
||||||
import net.corda.nodeapi.config.toConfig
|
import net.corda.nodeapi.config.toConfig
|
||||||
import net.corda.nodeapi.internal.ServiceInfo
|
|
||||||
import net.corda.testing.DUMMY_MAP
|
import net.corda.testing.DUMMY_MAP
|
||||||
import net.corda.testing.TestDependencyInjectionBase
|
import net.corda.testing.TestDependencyInjectionBase
|
||||||
import net.corda.testing.driver.addressMustNotBeBoundFuture
|
import net.corda.testing.driver.addressMustNotBeBoundFuture
|
||||||
@ -83,11 +82,10 @@ abstract class NodeBasedTest : TestDependencyInjectionBase() {
|
|||||||
*/
|
*/
|
||||||
fun startNetworkMapNode(legalName: CordaX500Name = DUMMY_MAP.name,
|
fun startNetworkMapNode(legalName: CordaX500Name = DUMMY_MAP.name,
|
||||||
platformVersion: Int = 1,
|
platformVersion: Int = 1,
|
||||||
advertisedServices: Set<ServiceInfo> = emptySet(),
|
|
||||||
rpcUsers: List<User> = emptyList(),
|
rpcUsers: List<User> = emptyList(),
|
||||||
configOverrides: Map<String, Any> = emptyMap()): StartedNode<Node> {
|
configOverrides: Map<String, Any> = emptyMap()): StartedNode<Node> {
|
||||||
check(_networkMapNode == null || _networkMapNode!!.info.legalIdentitiesAndCerts.first().name == legalName)
|
check(_networkMapNode == null || _networkMapNode!!.info.legalIdentitiesAndCerts.first().name == legalName)
|
||||||
return startNodeInternal(legalName, platformVersion, advertisedServices, rpcUsers, configOverrides).apply {
|
return startNodeInternal(legalName, platformVersion, rpcUsers, configOverrides).apply {
|
||||||
_networkMapNode = this
|
_networkMapNode = this
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -95,7 +93,6 @@ abstract class NodeBasedTest : TestDependencyInjectionBase() {
|
|||||||
@JvmOverloads
|
@JvmOverloads
|
||||||
fun startNode(legalName: CordaX500Name,
|
fun startNode(legalName: CordaX500Name,
|
||||||
platformVersion: Int = 1,
|
platformVersion: Int = 1,
|
||||||
advertisedServices: Set<ServiceInfo> = emptySet(),
|
|
||||||
rpcUsers: List<User> = emptyList(),
|
rpcUsers: List<User> = emptyList(),
|
||||||
configOverrides: Map<String, Any> = emptyMap(),
|
configOverrides: Map<String, Any> = emptyMap(),
|
||||||
noNetworkMap: Boolean = false,
|
noNetworkMap: Boolean = false,
|
||||||
@ -119,7 +116,6 @@ abstract class NodeBasedTest : TestDependencyInjectionBase() {
|
|||||||
val node = startNodeInternal(
|
val node = startNodeInternal(
|
||||||
legalName,
|
legalName,
|
||||||
platformVersion,
|
platformVersion,
|
||||||
advertisedServices,
|
|
||||||
rpcUsers,
|
rpcUsers,
|
||||||
networkMapConf + configOverrides,
|
networkMapConf + configOverrides,
|
||||||
noNetworkMap)
|
noNetworkMap)
|
||||||
@ -175,7 +171,6 @@ abstract class NodeBasedTest : TestDependencyInjectionBase() {
|
|||||||
|
|
||||||
private fun startNodeInternal(legalName: CordaX500Name,
|
private fun startNodeInternal(legalName: CordaX500Name,
|
||||||
platformVersion: Int,
|
platformVersion: Int,
|
||||||
advertisedServices: Set<ServiceInfo>,
|
|
||||||
rpcUsers: List<User>,
|
rpcUsers: List<User>,
|
||||||
configOverrides: Map<String, Any>,
|
configOverrides: Map<String, Any>,
|
||||||
noNetworkMap: Boolean = false): StartedNode<Node> {
|
noNetworkMap: Boolean = false): StartedNode<Node> {
|
||||||
@ -189,14 +184,15 @@ abstract class NodeBasedTest : TestDependencyInjectionBase() {
|
|||||||
"myLegalName" to legalName.toString(),
|
"myLegalName" to legalName.toString(),
|
||||||
"p2pAddress" to p2pAddress,
|
"p2pAddress" to p2pAddress,
|
||||||
"rpcAddress" to localPort[1].toString(),
|
"rpcAddress" to localPort[1].toString(),
|
||||||
"extraAdvertisedServiceIds" to advertisedServices.map { it.toString() },
|
|
||||||
"rpcUsers" to rpcUsers.map { it.toMap() },
|
"rpcUsers" to rpcUsers.map { it.toMap() },
|
||||||
"noNetworkMap" to noNetworkMap
|
"noNetworkMap" to noNetworkMap
|
||||||
) + configOverrides
|
) + configOverrides
|
||||||
)
|
)
|
||||||
|
|
||||||
val parsedConfig = config.parseAs<FullNodeConfiguration>()
|
val parsedConfig = config.parseAs<FullNodeConfiguration>()
|
||||||
val node = Node(parsedConfig, parsedConfig.calculateServices(), MOCK_VERSION_INFO.copy(platformVersion = platformVersion),
|
val node = Node(
|
||||||
|
parsedConfig,
|
||||||
|
MOCK_VERSION_INFO.copy(platformVersion = platformVersion),
|
||||||
initialiseSerialization = false).start()
|
initialiseSerialization = false).start()
|
||||||
nodes += node
|
nodes += node
|
||||||
thread(name = legalName.organisation) {
|
thread(name = legalName.organisation) {
|
||||||
|
@ -70,9 +70,6 @@ node in a new tab.
|
|||||||
|
|
||||||

|

|
||||||
|
|
||||||
This time, there will be additional services available. Select `corda.cash` and
|
|
||||||
`corda.issuer.GBP`, and then press the `Start node` button.
|
|
||||||
|
|
||||||
When you press the `Launch Web Server` this time, your browser should open to a
|
When you press the `Launch Web Server` this time, your browser should open to a
|
||||||
page saying:
|
page saying:
|
||||||
> ### Installed CorDapps
|
> ### Installed CorDapps
|
||||||
|
@ -71,6 +71,7 @@ dependencies {
|
|||||||
|
|
||||||
testCompile "junit:junit:$junit_version"
|
testCompile "junit:junit:$junit_version"
|
||||||
testCompile "org.jetbrains.kotlin:kotlin-test:$kotlin_version"
|
testCompile "org.jetbrains.kotlin:kotlin-test:$kotlin_version"
|
||||||
|
testCompile "org.assertj:assertj-core:${assertj_version}"
|
||||||
testCompile "org.mockito:mockito-core:$mockito_version"
|
testCompile "org.mockito:mockito-core:$mockito_version"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,13 +5,12 @@ import net.corda.core.internal.div
|
|||||||
import net.corda.core.internal.list
|
import net.corda.core.internal.list
|
||||||
import net.corda.core.utilities.loggerFor
|
import net.corda.core.utilities.loggerFor
|
||||||
import net.corda.demobench.model.JVMConfig
|
import net.corda.demobench.model.JVMConfig
|
||||||
import net.corda.demobench.model.NodeConfig
|
import net.corda.demobench.model.NodeConfigWrapper
|
||||||
import net.corda.demobench.model.forceDirectory
|
|
||||||
import net.corda.demobench.readErrorLines
|
import net.corda.demobench.readErrorLines
|
||||||
import tornadofx.*
|
import tornadofx.*
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.nio.file.Files
|
import java.nio.file.Files
|
||||||
import java.nio.file.StandardCopyOption.*
|
import java.nio.file.StandardCopyOption.REPLACE_EXISTING
|
||||||
import java.util.concurrent.Executors
|
import java.util.concurrent.Executors
|
||||||
|
|
||||||
class Explorer internal constructor(private val explorerController: ExplorerController) : AutoCloseable {
|
class Explorer internal constructor(private val explorerController: ExplorerController) : AutoCloseable {
|
||||||
@ -23,29 +22,32 @@ class Explorer internal constructor(private val explorerController: ExplorerCont
|
|||||||
private var process: Process? = null
|
private var process: Process? = null
|
||||||
|
|
||||||
@Throws(IOException::class)
|
@Throws(IOException::class)
|
||||||
fun open(config: NodeConfig, onExit: (NodeConfig) -> Unit) {
|
fun open(config: NodeConfigWrapper, onExit: (NodeConfigWrapper) -> Unit) {
|
||||||
val explorerDir = config.explorerDir.toFile()
|
val explorerDir = config.explorerDir
|
||||||
|
|
||||||
if (!explorerDir.forceDirectory()) {
|
try {
|
||||||
log.warn("Failed to create working directory '{}'", explorerDir.absolutePath)
|
explorerDir.createDirectories()
|
||||||
|
} catch (e: IOException) {
|
||||||
|
log.warn("Failed to create working directory '{}'", explorerDir.toAbsolutePath())
|
||||||
onExit(config)
|
onExit(config)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val legalName = config.nodeConfig.myLegalName
|
||||||
try {
|
try {
|
||||||
installApps(config)
|
installApps(config)
|
||||||
|
|
||||||
val user = config.users.elementAt(0)
|
val user = config.nodeConfig.rpcUsers[0]
|
||||||
val p = explorerController.process(
|
val p = explorerController.process(
|
||||||
"--host=localhost",
|
"--host=localhost",
|
||||||
"--port=${config.rpcPort}",
|
"--port=${config.nodeConfig.rpcAddress.port}",
|
||||||
"--username=${user.username}",
|
"--username=${user.username}",
|
||||||
"--password=${user.password}")
|
"--password=${user.password}")
|
||||||
.directory(explorerDir)
|
.directory(explorerDir.toFile())
|
||||||
.start()
|
.start()
|
||||||
process = p
|
process = p
|
||||||
|
|
||||||
log.info("Launched Node Explorer for '{}'", config.legalName)
|
log.info("Launched Node Explorer for '{}'", legalName)
|
||||||
|
|
||||||
// Close these streams because no-one is using them.
|
// Close these streams because no-one is using them.
|
||||||
safeClose(p.outputStream)
|
safeClose(p.outputStream)
|
||||||
@ -57,21 +59,21 @@ class Explorer internal constructor(private val explorerController: ExplorerCont
|
|||||||
process = null
|
process = null
|
||||||
|
|
||||||
if (errors.isEmpty()) {
|
if (errors.isEmpty()) {
|
||||||
log.info("Node Explorer for '{}' has exited (value={})", config.legalName, exitValue)
|
log.info("Node Explorer for '{}' has exited (value={})", legalName, exitValue)
|
||||||
} else {
|
} else {
|
||||||
log.error("Node Explorer for '{}' has exited (value={}, {})", config.legalName, exitValue, errors)
|
log.error("Node Explorer for '{}' has exited (value={}, {})", legalName, exitValue, errors)
|
||||||
}
|
}
|
||||||
|
|
||||||
onExit(config)
|
onExit(config)
|
||||||
}
|
}
|
||||||
} catch (e: IOException) {
|
} catch (e: IOException) {
|
||||||
log.error("Failed to launch Node Explorer for '{}': {}", config.legalName, e.message)
|
log.error("Failed to launch Node Explorer for '{}': {}", legalName, e.message)
|
||||||
onExit(config)
|
onExit(config)
|
||||||
throw e
|
throw e
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun installApps(config: NodeConfig) {
|
private fun installApps(config: NodeConfigWrapper) {
|
||||||
// Make sure that the explorer has cordapps on its class path. This is only necessary because currently apps
|
// Make sure that the explorer has cordapps on its class path. This is only necessary because currently apps
|
||||||
// require the original class files to deserialise states: Kryo serialisation doesn't let us write generic
|
// require the original class files to deserialise states: Kryo serialisation doesn't let us write generic
|
||||||
// tools that work with serialised data structures. But the AMQP serialisation revamp will fix this by
|
// tools that work with serialised data structures. But the AMQP serialisation revamp will fix this by
|
||||||
|
@ -1,76 +1,46 @@
|
|||||||
package net.corda.demobench.model
|
package net.corda.demobench.model
|
||||||
|
|
||||||
import com.typesafe.config.Config
|
import com.typesafe.config.Config
|
||||||
import net.corda.core.identity.CordaX500Name
|
import net.corda.core.internal.div
|
||||||
import net.corda.core.utilities.NetworkHostAndPort
|
import net.corda.core.utilities.NetworkHostAndPort
|
||||||
|
import net.corda.nodeapi.config.parseAs
|
||||||
import tornadofx.*
|
import tornadofx.*
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.nio.file.Files
|
import java.nio.file.Files
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
|
|
||||||
class InstallFactory : Controller() {
|
class InstallFactory : Controller() {
|
||||||
|
|
||||||
private val nodeController by inject<NodeController>()
|
private val nodeController by inject<NodeController>()
|
||||||
private val serviceController by inject<ServiceController>()
|
|
||||||
|
|
||||||
@Throws(IOException::class)
|
@Throws(IOException::class)
|
||||||
fun toInstallConfig(config: Config, baseDir: Path): InstallConfig {
|
fun toInstallConfig(config: Config, baseDir: Path): InstallConfig {
|
||||||
val p2pPort = config.parsePort("p2pAddress")
|
fun NetworkHostAndPort.checkPort() {
|
||||||
val rpcPort = config.parsePort("rpcAddress")
|
require(nodeController.isPortValid(port)) { "Invalid port $port" }
|
||||||
val webPort = config.parsePort("webAddress")
|
}
|
||||||
val h2Port = config.getInt("h2port")
|
|
||||||
val x500name = CordaX500Name.parse(config.getString("myLegalName"))
|
val nodeConfig = config.parseAs<NodeConfig>()
|
||||||
val extraServices = config.parseExtraServices("extraAdvertisedServiceIds")
|
nodeConfig.p2pAddress.checkPort()
|
||||||
|
nodeConfig.rpcAddress.checkPort()
|
||||||
|
nodeConfig.webAddress.checkPort()
|
||||||
|
|
||||||
val tempDir = Files.createTempDirectory(baseDir, ".node")
|
val tempDir = Files.createTempDirectory(baseDir, ".node")
|
||||||
|
|
||||||
val nodeConfig = NodeConfig(
|
if (nodeConfig.isNetworkMap) {
|
||||||
tempDir,
|
log.info("Node '${nodeConfig.myLegalName}' is the network map")
|
||||||
x500name,
|
|
||||||
p2pPort,
|
|
||||||
rpcPort,
|
|
||||||
webPort,
|
|
||||||
h2Port,
|
|
||||||
extraServices,
|
|
||||||
config.getObjectList("rpcUsers").map { toUser(it.unwrapped()) }.toList()
|
|
||||||
)
|
|
||||||
|
|
||||||
if (config.hasPath("networkMapService")) {
|
|
||||||
val nmap = config.getConfig("networkMapService")
|
|
||||||
nodeConfig.networkMap = NetworkMapConfig(CordaX500Name.parse(nmap.getString("legalName")), nmap.parsePort("address"))
|
|
||||||
} else {
|
|
||||||
log.info("Node '${nodeConfig.legalName}' is the network map")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return InstallConfig(tempDir, nodeConfig)
|
return InstallConfig(tempDir, NodeConfigWrapper(tempDir, nodeConfig))
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun Config.parsePort(path: String): Int {
|
|
||||||
val address = this.getString(path)
|
|
||||||
val port = NetworkHostAndPort.parse(address).port
|
|
||||||
require(nodeController.isPortValid(port), { "Invalid port $port from '$path'." })
|
|
||||||
return port
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun Config.parseExtraServices(path: String): MutableList<String> {
|
|
||||||
val services = serviceController.services.values.toSortedSet()
|
|
||||||
return this.getStringList(path)
|
|
||||||
.filter { !it.isNullOrEmpty() }
|
|
||||||
.map { svc ->
|
|
||||||
require(svc in services, { "Unknown service '$svc'." })
|
|
||||||
svc
|
|
||||||
}.toMutableList()
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wraps the configuration information for a Node
|
* Wraps the configuration information for a Node
|
||||||
* which isn't ready to be instantiated yet.
|
* which isn't ready to be instantiated yet.
|
||||||
*/
|
*/
|
||||||
class InstallConfig internal constructor(val baseDir: Path, private val config: NodeConfig) : HasPlugins {
|
class InstallConfig internal constructor(val baseDir: Path, private val config: NodeConfigWrapper) : HasPlugins {
|
||||||
val key = config.key
|
val key = config.key
|
||||||
override val pluginDir: Path = baseDir.resolve("plugins")
|
override val pluginDir: Path = baseDir / "plugins"
|
||||||
|
|
||||||
fun deleteBaseDir(): Boolean = baseDir.toFile().deleteRecursively()
|
fun deleteBaseDir(): Boolean = baseDir.toFile().deleteRecursively()
|
||||||
fun installTo(installDir: Path) = config.moveTo(installDir)
|
fun installTo(installDir: Path) = config.copy(baseDir = installDir)
|
||||||
}
|
}
|
||||||
|
@ -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
|
package net.corda.demobench.model
|
||||||
|
|
||||||
import com.typesafe.config.*
|
import com.typesafe.config.ConfigRenderOptions
|
||||||
import net.corda.core.identity.CordaX500Name
|
import net.corda.core.identity.CordaX500Name
|
||||||
|
import net.corda.core.internal.copyToDirectory
|
||||||
|
import net.corda.core.internal.createDirectories
|
||||||
|
import net.corda.core.internal.div
|
||||||
|
import net.corda.core.utilities.NetworkHostAndPort
|
||||||
import net.corda.nodeapi.User
|
import net.corda.nodeapi.User
|
||||||
import java.io.File
|
import net.corda.nodeapi.config.toConfig
|
||||||
import java.nio.file.Files
|
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
import java.nio.file.StandardCopyOption
|
import java.nio.file.StandardCopyOption
|
||||||
|
|
||||||
class NodeConfig constructor(
|
/**
|
||||||
baseDir: Path,
|
* This is a subset of FullNodeConfiguration, containing only those configs which we need. The node uses reference.conf
|
||||||
legalName: CordaX500Name,
|
* to fill in the defaults so we're not required to specify them here.
|
||||||
p2pPort: Int,
|
*/
|
||||||
val rpcPort: Int,
|
data class NodeConfig(
|
||||||
val webPort: Int,
|
val myLegalName: CordaX500Name,
|
||||||
val h2Port: Int,
|
val p2pAddress: NetworkHostAndPort,
|
||||||
val extraServices: MutableList<String> = mutableListOf(),
|
val rpcAddress: NetworkHostAndPort,
|
||||||
val users: List<User> = listOf(defaultUser),
|
/** This is not used by the node but by the webserver which looks at node.conf. */
|
||||||
var networkMap: NetworkMapConfig? = null
|
val webAddress: NetworkHostAndPort,
|
||||||
) : NetworkMapConfig(legalName, p2pPort), HasPlugins {
|
val notary: NotaryService?,
|
||||||
|
val networkMapService: NetworkMapConfig?,
|
||||||
|
val h2port: Int,
|
||||||
|
val rpcUsers: List<User> = listOf(defaultUser),
|
||||||
|
/** This is an extra config used by the Cash app. */
|
||||||
|
val issuableCurrencies: List<String> = emptyList()
|
||||||
|
) {
|
||||||
companion object {
|
companion object {
|
||||||
val renderOptions: ConfigRenderOptions = ConfigRenderOptions.defaults().setOriginComments(false)
|
val renderOptions: ConfigRenderOptions = ConfigRenderOptions.defaults().setOriginComments(false)
|
||||||
val defaultUser = user("guest")
|
val defaultUser = user("guest")
|
||||||
}
|
}
|
||||||
|
|
||||||
val nearestCity: String = legalName.locality
|
@Suppress("unused")
|
||||||
val nodeDir: Path = baseDir.resolve(key)
|
private val detectPublicIp = false
|
||||||
override val pluginDir: Path = nodeDir.resolve("plugins")
|
@Suppress("unused")
|
||||||
val explorerDir: Path = baseDir.resolve("$key-explorer")
|
private val useTestClock = true
|
||||||
|
|
||||||
|
val isNetworkMap: Boolean get() = networkMapService == null
|
||||||
|
|
||||||
|
fun toText(): String = toConfig().root().render(renderOptions)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is a mirror of NetworkMapInfo.
|
||||||
|
*/
|
||||||
|
data class NetworkMapConfig(val legalName: CordaX500Name, val address: NetworkHostAndPort)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is a subset of NotaryConfig. It implements [ExtraService] to avoid unnecessary copying.
|
||||||
|
*/
|
||||||
|
data class NotaryService(val validating: Boolean) : ExtraService {
|
||||||
|
override fun toString(): String = "${if (validating) "V" else "Non-v"}alidating Notary"
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO Think of a better name
|
||||||
|
data class NodeConfigWrapper(val baseDir: Path, val nodeConfig: NodeConfig) : HasPlugins {
|
||||||
|
val key: String = nodeConfig.myLegalName.organisation.toKey()
|
||||||
|
val nodeDir: Path = baseDir / key
|
||||||
|
val explorerDir: Path = baseDir / "$key-explorer"
|
||||||
|
override val pluginDir: Path = nodeDir / "plugins"
|
||||||
var state: NodeState = NodeState.STARTING
|
var state: NodeState = NodeState.STARTING
|
||||||
|
|
||||||
val isCashIssuer: Boolean = extraServices.any {
|
fun install(cordapps: Collection<Path>) {
|
||||||
it.startsWith("corda.issuer.")
|
if (cordapps.isEmpty()) return
|
||||||
}
|
pluginDir.createDirectories()
|
||||||
|
for (cordapp in cordapps) {
|
||||||
fun isNetworkMap(): Boolean = networkMap == null
|
cordapp.copyToDirectory(pluginDir, StandardCopyOption.REPLACE_EXISTING)
|
||||||
|
|
||||||
/*
|
|
||||||
* The configuration object depends upon the networkMap,
|
|
||||||
* which is mutable.
|
|
||||||
*/
|
|
||||||
fun toFileConfig(): Config {
|
|
||||||
return ConfigFactory.empty()
|
|
||||||
.withValue("myLegalName", valueFor(legalName.toString()))
|
|
||||||
.withValue("p2pAddress", addressValueFor(p2pPort))
|
|
||||||
.withValue("extraAdvertisedServiceIds", valueFor(extraServices))
|
|
||||||
.withFallback(optional("networkMapService", networkMap, { c, n ->
|
|
||||||
c.withValue("address", addressValueFor(n.p2pPort))
|
|
||||||
.withValue("legalName", valueFor(n.legalName.toString()))
|
|
||||||
}))
|
|
||||||
.withValue("webAddress", addressValueFor(webPort))
|
|
||||||
.withValue("rpcAddress", addressValueFor(rpcPort))
|
|
||||||
.withValue("rpcUsers", valueFor(users.map(User::toMap).toList()))
|
|
||||||
.withValue("h2port", valueFor(h2Port))
|
|
||||||
.withValue("useTestClock", valueFor(true))
|
|
||||||
.withValue("detectPublicIp", valueFor(false))
|
|
||||||
}
|
|
||||||
|
|
||||||
fun toText(): String = toFileConfig().root().render(renderOptions)
|
|
||||||
|
|
||||||
fun moveTo(baseDir: Path) = NodeConfig(
|
|
||||||
baseDir, legalName, p2pPort, rpcPort, webPort, h2Port, extraServices, users, networkMap
|
|
||||||
)
|
|
||||||
|
|
||||||
fun install(plugins: Collection<Path>) {
|
|
||||||
if (plugins.isNotEmpty() && pluginDir.toFile().forceDirectory()) {
|
|
||||||
plugins.forEach {
|
|
||||||
Files.copy(it, pluginDir.resolve(it.fileName.toString()), StandardCopyOption.REPLACE_EXISTING)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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")
|
fun String.toKey() = filter { !it.isWhitespace() }.toLowerCase()
|
||||||
|
|
||||||
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()
|
|
||||||
|
@ -1,6 +1,12 @@
|
|||||||
package net.corda.demobench.model
|
package net.corda.demobench.model
|
||||||
|
|
||||||
|
import javafx.beans.binding.IntegerExpression
|
||||||
import net.corda.core.identity.CordaX500Name
|
import net.corda.core.identity.CordaX500Name
|
||||||
|
import net.corda.core.internal.copyToDirectory
|
||||||
|
import net.corda.core.internal.createDirectories
|
||||||
|
import net.corda.core.internal.div
|
||||||
|
import net.corda.core.internal.noneOrSingle
|
||||||
|
import net.corda.core.utilities.NetworkHostAndPort
|
||||||
import net.corda.demobench.plugin.PluginController
|
import net.corda.demobench.plugin.PluginController
|
||||||
import net.corda.demobench.pty.R3Pty
|
import net.corda.demobench.pty.R3Pty
|
||||||
import tornadofx.*
|
import tornadofx.*
|
||||||
@ -22,18 +28,17 @@ class NodeController(check: atRuntime = ::checkExists) : Controller() {
|
|||||||
|
|
||||||
private val jvm by inject<JVMConfig>()
|
private val jvm by inject<JVMConfig>()
|
||||||
private val pluginController by inject<PluginController>()
|
private val pluginController by inject<PluginController>()
|
||||||
private val serviceController by inject<ServiceController>()
|
|
||||||
|
|
||||||
private var baseDir: Path = baseDirFor(ManagementFactory.getRuntimeMXBean().startTime)
|
private var baseDir: Path = baseDirFor(ManagementFactory.getRuntimeMXBean().startTime)
|
||||||
private val cordaPath: Path = jvm.applicationDir.resolve("corda").resolve("corda.jar")
|
private val cordaPath: Path = jvm.applicationDir.resolve("corda").resolve("corda.jar")
|
||||||
private val command = jvm.commandFor(cordaPath).toTypedArray()
|
private val command = jvm.commandFor(cordaPath).toTypedArray()
|
||||||
|
|
||||||
private val nodes = LinkedHashMap<String, NodeConfig>()
|
private val nodes = LinkedHashMap<String, NodeConfigWrapper>()
|
||||||
private val port = AtomicInteger(firstPort)
|
private val port = AtomicInteger(firstPort)
|
||||||
|
|
||||||
private var networkMapConfig: NetworkMapConfig? = null
|
private var networkMapConfig: NetworkMapConfig? = null
|
||||||
|
|
||||||
val activeNodes: List<NodeConfig>
|
val activeNodes: List<NodeConfigWrapper>
|
||||||
get() = nodes.values.filter {
|
get() = nodes.values.filter {
|
||||||
(it.state == NodeState.RUNNING) || (it.state == NodeState.STARTING)
|
(it.state == NodeState.RUNNING) || (it.state == NodeState.STARTING)
|
||||||
}
|
}
|
||||||
@ -50,38 +55,45 @@ class NodeController(check: atRuntime = ::checkExists) : Controller() {
|
|||||||
/**
|
/**
|
||||||
* Validate a Node configuration provided by [net.corda.demobench.views.NodeTabView].
|
* Validate a Node configuration provided by [net.corda.demobench.views.NodeTabView].
|
||||||
*/
|
*/
|
||||||
fun validate(nodeData: NodeData): NodeConfig? {
|
fun validate(nodeData: NodeData): NodeConfigWrapper? {
|
||||||
|
fun IntegerExpression.toLocalAddress() = NetworkHostAndPort("localhost", value)
|
||||||
|
|
||||||
val location = nodeData.nearestCity.value
|
val location = nodeData.nearestCity.value
|
||||||
val config = NodeConfig(
|
val nodeConfig = NodeConfig(
|
||||||
baseDir,
|
myLegalName = CordaX500Name(
|
||||||
CordaX500Name(
|
|
||||||
organisation = nodeData.legalName.value.trim(),
|
organisation = nodeData.legalName.value.trim(),
|
||||||
locality = location.description,
|
locality = location.description,
|
||||||
country = location.countryCode
|
country = location.countryCode
|
||||||
),
|
),
|
||||||
nodeData.p2pPort.value,
|
p2pAddress = nodeData.p2pPort.toLocalAddress(),
|
||||||
nodeData.rpcPort.value,
|
rpcAddress = nodeData.rpcPort.toLocalAddress(),
|
||||||
nodeData.webPort.value,
|
webAddress = nodeData.webPort.toLocalAddress(),
|
||||||
nodeData.h2Port.value,
|
notary = nodeData.extraServices.filterIsInstance<NotaryService>().noneOrSingle(),
|
||||||
nodeData.extraServices.map { serviceController.services[it]!! }.toMutableList()
|
networkMapService = networkMapConfig, // The first node becomes the network map
|
||||||
|
h2port = nodeData.h2Port.value,
|
||||||
|
issuableCurrencies = nodeData.extraServices.filterIsInstance<CurrencyIssuer>().map { it.currency.toString() }
|
||||||
)
|
)
|
||||||
|
|
||||||
if (nodes.putIfAbsent(config.key, config) != null) {
|
val wrapper = NodeConfigWrapper(baseDir, nodeConfig)
|
||||||
log.warning("Node with key '${config.key}' already exists.")
|
|
||||||
|
if (nodes.putIfAbsent(wrapper.key, wrapper) != null) {
|
||||||
|
log.warning("Node with key '${wrapper.key}' already exists.")
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
// The first node becomes our network map
|
if (nodeConfig.isNetworkMap) {
|
||||||
chooseNetworkMap(config)
|
networkMapConfig = nodeConfig.let { NetworkMapConfig(it.myLegalName, it.p2pAddress) }
|
||||||
|
log.info("Network map provided by: ${nodeConfig.myLegalName}")
|
||||||
return config
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun dispose(config: NodeConfig) {
|
return wrapper
|
||||||
|
}
|
||||||
|
|
||||||
|
fun dispose(config: NodeConfigWrapper) {
|
||||||
config.state = NodeState.DEAD
|
config.state = NodeState.DEAD
|
||||||
|
|
||||||
if (config.networkMap == null) {
|
if (config.nodeConfig.isNetworkMap) {
|
||||||
log.warning("Network map service (Node '${config.legalName}') has exited.")
|
log.warning("Network map service (Node '${config.nodeConfig.myLegalName}') has exited.")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -95,41 +107,28 @@ class NodeController(check: atRuntime = ::checkExists) : Controller() {
|
|||||||
|
|
||||||
fun hasNetworkMap(): Boolean = networkMapConfig != null
|
fun hasNetworkMap(): Boolean = networkMapConfig != null
|
||||||
|
|
||||||
private fun chooseNetworkMap(config: NodeConfig) {
|
fun runCorda(pty: R3Pty, config: NodeConfigWrapper): Boolean {
|
||||||
if (hasNetworkMap()) {
|
|
||||||
config.networkMap = networkMapConfig
|
|
||||||
} else {
|
|
||||||
networkMapConfig = config
|
|
||||||
log.info("Network map provided by: ${config.legalName}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun runCorda(pty: R3Pty, config: NodeConfig): Boolean {
|
|
||||||
val nodeDir = config.nodeDir.toFile()
|
|
||||||
|
|
||||||
if (nodeDir.forceDirectory()) {
|
|
||||||
try {
|
try {
|
||||||
|
config.nodeDir.createDirectories()
|
||||||
|
|
||||||
// Install any built-in plugins into the working directory.
|
// Install any built-in plugins into the working directory.
|
||||||
pluginController.populate(config)
|
pluginController.populate(config)
|
||||||
|
|
||||||
// Write this node's configuration file into its working directory.
|
// Write this node's configuration file into its working directory.
|
||||||
val confFile = nodeDir.resolve("node.conf")
|
val confFile = config.nodeDir / "node.conf"
|
||||||
confFile.writeText(config.toText())
|
Files.write(confFile, config.nodeConfig.toText().toByteArray())
|
||||||
|
|
||||||
// Execute the Corda node
|
// Execute the Corda node
|
||||||
val cordaEnv = System.getenv().toMutableMap().apply {
|
val cordaEnv = System.getenv().toMutableMap().apply {
|
||||||
jvm.setCapsuleCacheDir(this)
|
jvm.setCapsuleCacheDir(this)
|
||||||
}
|
}
|
||||||
pty.run(command, cordaEnv, nodeDir.toString())
|
pty.run(command, cordaEnv, config.nodeDir.toString())
|
||||||
log.info("Launched node: ${config.legalName}")
|
log.info("Launched node: ${config.nodeConfig.myLegalName}")
|
||||||
return true
|
return true
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
log.log(Level.SEVERE, "Failed to launch Corda: ${e.message}", e)
|
log.log(Level.SEVERE, "Failed to launch Corda: ${e.message}", e)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun reset() {
|
fun reset() {
|
||||||
@ -144,15 +143,15 @@ class NodeController(check: atRuntime = ::checkExists) : Controller() {
|
|||||||
/**
|
/**
|
||||||
* Add a [NodeConfig] object that has been loaded from a profile.
|
* Add a [NodeConfig] object that has been loaded from a profile.
|
||||||
*/
|
*/
|
||||||
fun register(config: NodeConfig): Boolean {
|
fun register(config: NodeConfigWrapper): Boolean {
|
||||||
if (nodes.putIfAbsent(config.key, config) != null) {
|
if (nodes.putIfAbsent(config.key, config) != null) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
updatePort(config)
|
updatePort(config.nodeConfig)
|
||||||
|
|
||||||
if ((networkMapConfig == null) && config.isNetworkMap()) {
|
if (networkMapConfig == null && config.nodeConfig.isNetworkMap) {
|
||||||
networkMapConfig = config
|
networkMapConfig = config.nodeConfig.let { NetworkMapConfig(it.myLegalName, it.p2pAddress) }
|
||||||
}
|
}
|
||||||
|
|
||||||
return true
|
return true
|
||||||
@ -162,12 +161,12 @@ class NodeController(check: atRuntime = ::checkExists) : Controller() {
|
|||||||
* Creates a node directory that can host a running instance of Corda.
|
* Creates a node directory that can host a running instance of Corda.
|
||||||
*/
|
*/
|
||||||
@Throws(IOException::class)
|
@Throws(IOException::class)
|
||||||
fun install(config: InstallConfig): NodeConfig {
|
fun install(config: InstallConfig): NodeConfigWrapper {
|
||||||
val installed = config.installTo(baseDir)
|
val installed = config.installTo(baseDir)
|
||||||
|
|
||||||
pluginController.userPluginsFor(config).forEach {
|
pluginController.userPluginsFor(config).forEach {
|
||||||
val pluginDir = Files.createDirectories(installed.pluginDir)
|
installed.pluginDir.createDirectories()
|
||||||
val plugin = Files.copy(it, pluginDir.resolve(it.fileName.toString()))
|
val plugin = it.copyToDirectory(installed.pluginDir)
|
||||||
log.info("Installed: $plugin")
|
log.info("Installed: $plugin")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -179,7 +178,7 @@ class NodeController(check: atRuntime = ::checkExists) : Controller() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun updatePort(config: NodeConfig) {
|
private fun updatePort(config: NodeConfig) {
|
||||||
val nextPort = 1 + arrayOf(config.p2pPort, config.rpcPort, config.webPort, config.h2Port).max() as Int
|
val nextPort = 1 + arrayOf(config.p2pAddress.port, config.rpcAddress.port, config.webAddress.port, config.h2port).max() as Int
|
||||||
port.getAndUpdate { Math.max(nextPort, it) }
|
port.getAndUpdate { Math.max(nextPort, it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,8 +4,10 @@ import javafx.beans.property.SimpleIntegerProperty
|
|||||||
import javafx.beans.property.SimpleListProperty
|
import javafx.beans.property.SimpleListProperty
|
||||||
import javafx.beans.property.SimpleObjectProperty
|
import javafx.beans.property.SimpleObjectProperty
|
||||||
import javafx.beans.property.SimpleStringProperty
|
import javafx.beans.property.SimpleStringProperty
|
||||||
|
import javafx.collections.FXCollections.observableArrayList
|
||||||
import net.corda.finance.utils.CityDatabase
|
import net.corda.finance.utils.CityDatabase
|
||||||
import tornadofx.*
|
import tornadofx.*
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
object SuggestedDetails {
|
object SuggestedDetails {
|
||||||
val banks = listOf(
|
val banks = listOf(
|
||||||
@ -35,7 +37,7 @@ class NodeData {
|
|||||||
val rpcPort = SimpleIntegerProperty()
|
val rpcPort = SimpleIntegerProperty()
|
||||||
val webPort = SimpleIntegerProperty()
|
val webPort = SimpleIntegerProperty()
|
||||||
val h2Port = SimpleIntegerProperty()
|
val h2Port = SimpleIntegerProperty()
|
||||||
val extraServices = SimpleListProperty(mutableListOf<String>().observable())
|
val extraServices = SimpleListProperty(observableArrayList<ExtraService>())
|
||||||
}
|
}
|
||||||
|
|
||||||
class NodeDataModel : ItemViewModel<NodeData>(NodeData()) {
|
class NodeDataModel : ItemViewModel<NodeData>(NodeData()) {
|
||||||
@ -46,3 +48,9 @@ class NodeDataModel : ItemViewModel<NodeData>(NodeData()) {
|
|||||||
val webPort = bind { item?.webPort }
|
val webPort = bind { item?.webPort }
|
||||||
val h2Port = bind { item?.h2Port }
|
val h2Port = bind { item?.h2Port }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface ExtraService
|
||||||
|
|
||||||
|
data class CurrencyIssuer(val currency: Currency) : ExtraService {
|
||||||
|
override fun toString(): String = "Issuer $currency"
|
||||||
|
}
|
||||||
|
@ -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.core.internal.exists
|
||||||
import net.corda.demobench.model.HasPlugins
|
import net.corda.demobench.model.HasPlugins
|
||||||
import net.corda.demobench.model.JVMConfig
|
import net.corda.demobench.model.JVMConfig
|
||||||
import net.corda.demobench.model.NodeConfig
|
import net.corda.demobench.model.NodeConfigWrapper
|
||||||
import tornadofx.*
|
import tornadofx.*
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.nio.file.Files
|
import java.nio.file.Files
|
||||||
@ -24,16 +24,14 @@ class PluginController : Controller() {
|
|||||||
* Install any built-in plugins that this node requires.
|
* Install any built-in plugins that this node requires.
|
||||||
*/
|
*/
|
||||||
@Throws(IOException::class)
|
@Throws(IOException::class)
|
||||||
fun populate(config: NodeConfig) {
|
fun populate(config: NodeConfigWrapper) {
|
||||||
if (!config.pluginDir.exists()) {
|
|
||||||
config.pluginDir.createDirectories()
|
config.pluginDir.createDirectories()
|
||||||
}
|
|
||||||
if (finance.exists()) {
|
if (finance.exists()) {
|
||||||
finance.copyToDirectory(config.pluginDir, StandardCopyOption.REPLACE_EXISTING)
|
finance.copyToDirectory(config.pluginDir, StandardCopyOption.REPLACE_EXISTING)
|
||||||
log.info("Installed 'Finance' plugin")
|
log.info("Installed 'Finance' plugin")
|
||||||
}
|
}
|
||||||
// Nodes cannot issue cash unless they contain the "Bank of Corda" plugin.
|
// Nodes cannot issue cash unless they contain the "Bank of Corda" plugin.
|
||||||
if (config.isCashIssuer && bankOfCorda.exists()) {
|
if (config.nodeConfig.issuableCurrencies.isNotEmpty() && bankOfCorda.exists()) {
|
||||||
bankOfCorda.copyToDirectory(config.pluginDir, StandardCopyOption.REPLACE_EXISTING)
|
bankOfCorda.copyToDirectory(config.pluginDir, StandardCopyOption.REPLACE_EXISTING)
|
||||||
log.info("Installed 'Bank of Corda' plugin")
|
log.info("Installed 'Bank of Corda' plugin")
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,8 @@ import com.typesafe.config.Config
|
|||||||
import com.typesafe.config.ConfigFactory
|
import com.typesafe.config.ConfigFactory
|
||||||
import javafx.stage.FileChooser
|
import javafx.stage.FileChooser
|
||||||
import javafx.stage.FileChooser.ExtensionFilter
|
import javafx.stage.FileChooser.ExtensionFilter
|
||||||
|
import net.corda.core.internal.createDirectories
|
||||||
|
import net.corda.core.internal.div
|
||||||
import net.corda.demobench.model.InstallConfig
|
import net.corda.demobench.model.InstallConfig
|
||||||
import net.corda.demobench.model.InstallFactory
|
import net.corda.demobench.model.InstallFactory
|
||||||
import net.corda.demobench.model.JVMConfig
|
import net.corda.demobench.model.JVMConfig
|
||||||
@ -58,8 +60,8 @@ class ProfileController : Controller() {
|
|||||||
FileSystems.newFileSystem(URI.create("jar:" + target.toURI()), mapOf("create" to "true")).use { fs ->
|
FileSystems.newFileSystem(URI.create("jar:" + target.toURI()), mapOf("create" to "true")).use { fs ->
|
||||||
configs.forEach { config ->
|
configs.forEach { config ->
|
||||||
// Write the configuration file.
|
// Write the configuration file.
|
||||||
val nodeDir = Files.createDirectories(fs.getPath(config.key))
|
val nodeDir = fs.getPath(config.key).createDirectories()
|
||||||
val file = Files.write(nodeDir.resolve("node.conf"), config.toText().toByteArray(UTF_8))
|
val file = Files.write(nodeDir / "node.conf", config.nodeConfig.toText().toByteArray(UTF_8))
|
||||||
log.info("Wrote: $file")
|
log.info("Wrote: $file")
|
||||||
|
|
||||||
// Write all of the non-built-in plugins.
|
// Write all of the non-built-in plugins.
|
||||||
|
@ -5,18 +5,17 @@ import net.corda.client.rpc.CordaRPCConnection
|
|||||||
import net.corda.core.messaging.CordaRPCOps
|
import net.corda.core.messaging.CordaRPCOps
|
||||||
import net.corda.core.utilities.NetworkHostAndPort
|
import net.corda.core.utilities.NetworkHostAndPort
|
||||||
import net.corda.core.utilities.loggerFor
|
import net.corda.core.utilities.loggerFor
|
||||||
import net.corda.demobench.model.NodeConfig
|
import net.corda.demobench.model.NodeConfigWrapper
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.concurrent.TimeUnit.SECONDS
|
import java.util.concurrent.TimeUnit.SECONDS
|
||||||
|
|
||||||
class NodeRPC(config: NodeConfig, start: (NodeConfig, CordaRPCOps) -> Unit, invoke: (CordaRPCOps) -> Unit) : AutoCloseable {
|
class NodeRPC(config: NodeConfigWrapper, start: (NodeConfigWrapper, CordaRPCOps) -> Unit, invoke: (CordaRPCOps) -> Unit) : AutoCloseable {
|
||||||
|
|
||||||
private companion object {
|
private companion object {
|
||||||
val log = loggerFor<NodeRPC>()
|
val log = loggerFor<NodeRPC>()
|
||||||
val oneSecond = SECONDS.toMillis(1)
|
val oneSecond = SECONDS.toMillis(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
private val rpcClient = CordaRPCClient(NetworkHostAndPort("localhost", config.rpcPort))
|
private val rpcClient = CordaRPCClient(NetworkHostAndPort("localhost", config.nodeConfig.rpcAddress.port))
|
||||||
private var rpcConnection: CordaRPCConnection? = null
|
private var rpcConnection: CordaRPCConnection? = null
|
||||||
private val timer = Timer()
|
private val timer = Timer()
|
||||||
|
|
||||||
@ -24,7 +23,7 @@ class NodeRPC(config: NodeConfig, start: (NodeConfig, CordaRPCOps) -> Unit, invo
|
|||||||
val setupTask = object : TimerTask() {
|
val setupTask = object : TimerTask() {
|
||||||
override fun run() {
|
override fun run() {
|
||||||
try {
|
try {
|
||||||
val user = config.users.elementAt(0)
|
val user = config.nodeConfig.rpcUsers[0]
|
||||||
val connection = rpcClient.start(user.username, user.password)
|
val connection = rpcClient.start(user.username, user.password)
|
||||||
rpcConnection = connection
|
rpcConnection = connection
|
||||||
val ops = connection.proxy
|
val ops = connection.proxy
|
||||||
@ -42,7 +41,7 @@ class NodeRPC(config: NodeConfig, start: (NodeConfig, CordaRPCOps) -> Unit, invo
|
|||||||
}
|
}
|
||||||
}, 0, oneSecond)
|
}, 0, oneSecond)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
log.warn("Node '{}' not ready yet (Error: {})", config.legalName, e.message)
|
log.warn("Node '{}' not ready yet (Error: {})", config.nodeConfig.myLegalName, e.message)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,10 +16,14 @@ import javafx.scene.layout.Priority
|
|||||||
import javafx.stage.FileChooser
|
import javafx.stage.FileChooser
|
||||||
import javafx.util.StringConverter
|
import javafx.util.StringConverter
|
||||||
import net.corda.core.internal.*
|
import net.corda.core.internal.*
|
||||||
import net.corda.finance.utils.CityDatabase
|
|
||||||
import net.corda.finance.utils.WorldMapLocation
|
|
||||||
import net.corda.demobench.model.*
|
import net.corda.demobench.model.*
|
||||||
import net.corda.demobench.ui.CloseableTab
|
import net.corda.demobench.ui.CloseableTab
|
||||||
|
import net.corda.finance.CHF
|
||||||
|
import net.corda.finance.EUR
|
||||||
|
import net.corda.finance.GBP
|
||||||
|
import net.corda.finance.USD
|
||||||
|
import net.corda.finance.utils.CityDatabase
|
||||||
|
import net.corda.finance.utils.WorldMapLocation
|
||||||
import org.controlsfx.control.CheckListView
|
import org.controlsfx.control.CheckListView
|
||||||
import tornadofx.*
|
import tornadofx.*
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
@ -39,10 +43,10 @@ class NodeTabView : Fragment() {
|
|||||||
val cordappPathsFile: Path = jvm.dataHome / "cordapp-paths.txt"
|
val cordappPathsFile: Path = jvm.dataHome / "cordapp-paths.txt"
|
||||||
|
|
||||||
fun loadDefaultCordappPaths(): MutableList<Path> {
|
fun loadDefaultCordappPaths(): MutableList<Path> {
|
||||||
if (cordappPathsFile.exists())
|
return if (cordappPathsFile.exists())
|
||||||
return cordappPathsFile.readAllLines().map { Paths.get(it) }.filter { it.exists() }.toMutableList()
|
cordappPathsFile.readAllLines().map { Paths.get(it) }.filter { it.exists() }.toMutableList()
|
||||||
else
|
else
|
||||||
return ArrayList()
|
ArrayList()
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is shared between tabs.
|
// This is shared between tabs.
|
||||||
@ -58,11 +62,9 @@ class NodeTabView : Fragment() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private val nodeController by inject<NodeController>()
|
private val nodeController by inject<NodeController>()
|
||||||
private val serviceController by inject<ServiceController>()
|
|
||||||
private val chooser = FileChooser()
|
private val chooser = FileChooser()
|
||||||
|
|
||||||
private val model = NodeDataModel()
|
private val model = NodeDataModel()
|
||||||
private val availableServices: List<String> = if (nodeController.hasNetworkMap()) serviceController.issuers.keys.toList() else serviceController.notaries.keys.toList()
|
|
||||||
|
|
||||||
private val nodeTerminalView = find<NodeTerminalView>()
|
private val nodeTerminalView = find<NodeTerminalView>()
|
||||||
private val nodeConfigView = stackpane {
|
private val nodeConfigView = stackpane {
|
||||||
@ -112,8 +114,13 @@ class NodeTabView : Fragment() {
|
|||||||
|
|
||||||
fieldset("Additional configuration") {
|
fieldset("Additional configuration") {
|
||||||
styleClass.addAll("services-panel")
|
styleClass.addAll("services-panel")
|
||||||
|
val extraServices = if (nodeController.hasNetworkMap()) {
|
||||||
|
listOf(USD, GBP, CHF, EUR).map { CurrencyIssuer(it) }
|
||||||
|
} else {
|
||||||
|
listOf(NotaryService(true), NotaryService(false))
|
||||||
|
}
|
||||||
|
|
||||||
val servicesList = CheckListView(availableServices.observable()).apply {
|
val servicesList = CheckListView(extraServices.observable()).apply {
|
||||||
vboxConstraints { vGrow = Priority.ALWAYS }
|
vboxConstraints { vGrow = Priority.ALWAYS }
|
||||||
model.item.extraServices.set(checkModel.checkedItems)
|
model.item.extraServices.set(checkModel.checkedItems)
|
||||||
if (!nodeController.hasNetworkMap()) {
|
if (!nodeController.hasNetworkMap()) {
|
||||||
@ -263,17 +270,17 @@ class NodeTabView : Fragment() {
|
|||||||
/**
|
/**
|
||||||
* Launches a preconfigured Corda node, e.g. from a saved profile.
|
* Launches a preconfigured Corda node, e.g. from a saved profile.
|
||||||
*/
|
*/
|
||||||
fun launch(config: NodeConfig) {
|
fun launch(config: NodeConfigWrapper) {
|
||||||
nodeController.register(config)
|
nodeController.register(config)
|
||||||
launchNode(config)
|
launchNode(config)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun launchNode(config: NodeConfig) {
|
private fun launchNode(config: NodeConfigWrapper) {
|
||||||
val countryCode = CityDatabase.cityMap[config.nearestCity]?.countryCode
|
val countryCode = CityDatabase.cityMap[config.nodeConfig.myLegalName.locality]?.countryCode
|
||||||
if (countryCode != null) {
|
if (countryCode != null) {
|
||||||
nodeTab.graphic = ImageView(flags.get()[countryCode]).apply { fitWidth = 24.0; isPreserveRatio = true }
|
nodeTab.graphic = ImageView(flags.get()[countryCode]).apply { fitWidth = 24.0; isPreserveRatio = true }
|
||||||
}
|
}
|
||||||
nodeTab.text = config.legalName.organisation
|
nodeTab.text = config.nodeConfig.myLegalName.organisation
|
||||||
nodeTerminalView.open(config) { exitCode ->
|
nodeTerminalView.open(config) { exitCode ->
|
||||||
Platform.runLater {
|
Platform.runLater {
|
||||||
if (exitCode == 0) {
|
if (exitCode == 0) {
|
||||||
|
@ -20,6 +20,7 @@ import net.corda.core.messaging.vaultTrackBy
|
|||||||
import net.corda.core.node.services.vault.PageSpecification
|
import net.corda.core.node.services.vault.PageSpecification
|
||||||
import net.corda.demobench.explorer.ExplorerController
|
import net.corda.demobench.explorer.ExplorerController
|
||||||
import net.corda.demobench.model.NodeConfig
|
import net.corda.demobench.model.NodeConfig
|
||||||
|
import net.corda.demobench.model.NodeConfigWrapper
|
||||||
import net.corda.demobench.model.NodeController
|
import net.corda.demobench.model.NodeController
|
||||||
import net.corda.demobench.model.NodeState
|
import net.corda.demobench.model.NodeState
|
||||||
import net.corda.demobench.pty.R3Pty
|
import net.corda.demobench.pty.R3Pty
|
||||||
@ -69,8 +70,8 @@ class NodeTerminalView : Fragment() {
|
|||||||
private lateinit var logo: ImageView
|
private lateinit var logo: ImageView
|
||||||
private lateinit var swingTerminal: SwingNode
|
private lateinit var swingTerminal: SwingNode
|
||||||
|
|
||||||
fun open(config: NodeConfig, onExit: (Int) -> Unit) {
|
fun open(config: NodeConfigWrapper, onExit: (Int) -> Unit) {
|
||||||
nodeName.text = config.legalName.organisation
|
nodeName.text = config.nodeConfig.myLegalName.organisation
|
||||||
|
|
||||||
swingTerminal = SwingNode()
|
swingTerminal = SwingNode()
|
||||||
swingTerminal.setOnMouseClicked {
|
swingTerminal.setOnMouseClicked {
|
||||||
@ -85,13 +86,13 @@ class NodeTerminalView : Fragment() {
|
|||||||
root.isVisible = true
|
root.isVisible = true
|
||||||
|
|
||||||
SwingUtilities.invokeLater({
|
SwingUtilities.invokeLater({
|
||||||
val r3pty = R3Pty(config.legalName, TerminalSettingsProvider(), Dimension(160, 80), onExit)
|
val r3pty = R3Pty(config.nodeConfig.myLegalName, TerminalSettingsProvider(), Dimension(160, 80), onExit)
|
||||||
pty = r3pty
|
pty = r3pty
|
||||||
|
|
||||||
if (nodeController.runCorda(r3pty, config)) {
|
if (nodeController.runCorda(r3pty, config)) {
|
||||||
swingTerminal.content = r3pty.terminal
|
swingTerminal.content = r3pty.terminal
|
||||||
|
|
||||||
configureDatabaseButton(config)
|
configureDatabaseButton(config.nodeConfig)
|
||||||
configureExplorerButton(config)
|
configureExplorerButton(config)
|
||||||
configureWebButton(config)
|
configureWebButton(config)
|
||||||
|
|
||||||
@ -105,7 +106,7 @@ class NodeTerminalView : Fragment() {
|
|||||||
* and close the RPC client if it has.
|
* and close the RPC client if it has.
|
||||||
*/
|
*/
|
||||||
if (!r3pty.isConnected) {
|
if (!r3pty.isConnected) {
|
||||||
log.severe("Node '${config.legalName}' has failed to start.")
|
log.severe("Node '${config.nodeConfig.myLegalName}' has failed to start.")
|
||||||
swingTerminal.content = null
|
swingTerminal.content = null
|
||||||
rpc?.close()
|
rpc?.close()
|
||||||
}
|
}
|
||||||
@ -119,7 +120,7 @@ class NodeTerminalView : Fragment() {
|
|||||||
* launched the explorer and only reenable it when
|
* launched the explorer and only reenable it when
|
||||||
* the explorer has exited.
|
* the explorer has exited.
|
||||||
*/
|
*/
|
||||||
private fun configureExplorerButton(config: NodeConfig) {
|
private fun configureExplorerButton(config: NodeConfigWrapper) {
|
||||||
launchExplorerButton.setOnAction {
|
launchExplorerButton.setOnAction {
|
||||||
launchExplorerButton.isDisable = true
|
launchExplorerButton.isDisable = true
|
||||||
|
|
||||||
@ -131,7 +132,7 @@ class NodeTerminalView : Fragment() {
|
|||||||
|
|
||||||
private fun configureDatabaseButton(config: NodeConfig) {
|
private fun configureDatabaseButton(config: NodeConfig) {
|
||||||
viewDatabaseButton.setOnAction {
|
viewDatabaseButton.setOnAction {
|
||||||
viewer.openBrowser(config.h2Port)
|
viewer.openBrowser(config.h2port)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -144,7 +145,7 @@ class NodeTerminalView : Fragment() {
|
|||||||
* launched the web server and only reenable it when
|
* launched the web server and only reenable it when
|
||||||
* the web server has exited.
|
* the web server has exited.
|
||||||
*/
|
*/
|
||||||
private fun configureWebButton(config: NodeConfig) {
|
private fun configureWebButton(config: NodeConfigWrapper) {
|
||||||
launchWebButton.setOnAction {
|
launchWebButton.setOnAction {
|
||||||
if (webURL != null) {
|
if (webURL != null) {
|
||||||
app.hostServices.showDocument(webURL.toString())
|
app.hostServices.showDocument(webURL.toString())
|
||||||
@ -161,12 +162,12 @@ class NodeTerminalView : Fragment() {
|
|||||||
launchWebButton.text = ""
|
launchWebButton.text = ""
|
||||||
launchWebButton.graphic = ProgressIndicator()
|
launchWebButton.graphic = ProgressIndicator()
|
||||||
|
|
||||||
log.info("Starting web server for ${config.legalName}")
|
log.info("Starting web server for ${config.nodeConfig.myLegalName}")
|
||||||
webServer.open(config).then {
|
webServer.open(config).then {
|
||||||
Platform.runLater {
|
Platform.runLater {
|
||||||
launchWebButton.graphic = null
|
launchWebButton.graphic = null
|
||||||
it.match(success = {
|
it.match(success = {
|
||||||
log.info("Web server for ${config.legalName} started on $it")
|
log.info("Web server for ${config.nodeConfig.myLegalName} started on $it")
|
||||||
webURL = it
|
webURL = it
|
||||||
launchWebButton.text = "Reopen\nweb site"
|
launchWebButton.text = "Reopen\nweb site"
|
||||||
app.hostServices.showDocument(it.toString())
|
app.hostServices.showDocument(it.toString())
|
||||||
@ -178,13 +179,13 @@ class NodeTerminalView : Fragment() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun launchRPC(config: NodeConfig) = NodeRPC(
|
private fun launchRPC(config: NodeConfigWrapper) = NodeRPC(
|
||||||
config = config,
|
config = config,
|
||||||
start = this::initialise,
|
start = this::initialise,
|
||||||
invoke = this::pollCashBalances
|
invoke = this::pollCashBalances
|
||||||
)
|
)
|
||||||
|
|
||||||
private fun initialise(config: NodeConfig, ops: CordaRPCOps) {
|
private fun initialise(config: NodeConfigWrapper, ops: CordaRPCOps) {
|
||||||
try {
|
try {
|
||||||
val (txInit, txNext) = ops.internalVerifiedTransactionsFeed()
|
val (txInit, txNext) = ops.internalVerifiedTransactionsFeed()
|
||||||
val (stateInit, stateNext) = ops.vaultTrackBy<ContractState>(paging = pageSpecification)
|
val (stateInit, stateNext) = ops.vaultTrackBy<ContractState>(paging = pageSpecification)
|
||||||
@ -212,7 +213,7 @@ class NodeTerminalView : Fragment() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
config.state = NodeState.RUNNING
|
config.state = NodeState.RUNNING
|
||||||
log.info("Node '${config.legalName}' is now ready.")
|
log.info("Node '${config.nodeConfig.myLegalName}' is now ready.")
|
||||||
|
|
||||||
header.isDisable = false
|
header.isDisable = false
|
||||||
}
|
}
|
||||||
@ -225,7 +226,7 @@ class NodeTerminalView : Fragment() {
|
|||||||
)
|
)
|
||||||
|
|
||||||
Platform.runLater {
|
Platform.runLater {
|
||||||
balance.value = if (cashBalances.isNullOrEmpty()) "0" else cashBalances
|
balance.value = if (cashBalances.isEmpty()) "0" else cashBalances
|
||||||
}
|
}
|
||||||
} catch (e: ClassNotFoundException) {
|
} catch (e: ClassNotFoundException) {
|
||||||
// TODO: Remove this special case once Rick's serialisation work means we can deserialise states that weren't on our own classpath.
|
// TODO: Remove this special case once Rick's serialisation work means we can deserialise states that weren't on our own classpath.
|
||||||
|
@ -2,11 +2,11 @@ package net.corda.demobench.web
|
|||||||
|
|
||||||
import com.google.common.util.concurrent.RateLimiter
|
import com.google.common.util.concurrent.RateLimiter
|
||||||
import net.corda.core.concurrent.CordaFuture
|
import net.corda.core.concurrent.CordaFuture
|
||||||
import net.corda.core.utilities.minutes
|
|
||||||
import net.corda.core.internal.concurrent.openFuture
|
import net.corda.core.internal.concurrent.openFuture
|
||||||
import net.corda.core.internal.until
|
import net.corda.core.internal.until
|
||||||
import net.corda.core.utilities.loggerFor
|
import net.corda.core.utilities.loggerFor
|
||||||
import net.corda.demobench.model.NodeConfig
|
import net.corda.core.utilities.minutes
|
||||||
|
import net.corda.demobench.model.NodeConfigWrapper
|
||||||
import net.corda.demobench.readErrorLines
|
import net.corda.demobench.readErrorLines
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.net.HttpURLConnection
|
import java.net.HttpURLConnection
|
||||||
@ -25,7 +25,7 @@ class WebServer internal constructor(private val webServerController: WebServerC
|
|||||||
private var process: Process? = null
|
private var process: Process? = null
|
||||||
|
|
||||||
@Throws(IOException::class)
|
@Throws(IOException::class)
|
||||||
fun open(config: NodeConfig): CordaFuture<URI> {
|
fun open(config: NodeConfigWrapper): CordaFuture<URI> {
|
||||||
val nodeDir = config.nodeDir.toFile()
|
val nodeDir = config.nodeDir.toFile()
|
||||||
|
|
||||||
if (!nodeDir.isDirectory) {
|
if (!nodeDir.isDirectory) {
|
||||||
@ -33,13 +33,14 @@ class WebServer internal constructor(private val webServerController: WebServerC
|
|||||||
return openFuture()
|
return openFuture()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val legalName = config.nodeConfig.myLegalName
|
||||||
try {
|
try {
|
||||||
val p = webServerController.process()
|
val p = webServerController.process()
|
||||||
.directory(nodeDir)
|
.directory(nodeDir)
|
||||||
.start()
|
.start()
|
||||||
process = p
|
process = p
|
||||||
|
|
||||||
log.info("Launched Web Server for '{}'", config.legalName)
|
log.info("Launched Web Server for '{}'", legalName)
|
||||||
|
|
||||||
// Close these streams because no-one is using them.
|
// Close these streams because no-one is using them.
|
||||||
safeClose(p.outputStream)
|
safeClose(p.outputStream)
|
||||||
@ -51,22 +52,22 @@ class WebServer internal constructor(private val webServerController: WebServerC
|
|||||||
process = null
|
process = null
|
||||||
|
|
||||||
if (errors.isEmpty()) {
|
if (errors.isEmpty()) {
|
||||||
log.info("Web Server for '{}' has exited (value={})", config.legalName, exitValue)
|
log.info("Web Server for '{}' has exited (value={})", legalName, exitValue)
|
||||||
} else {
|
} else {
|
||||||
log.error("Web Server for '{}' has exited (value={}, {})", config.legalName, exitValue, errors)
|
log.error("Web Server for '{}' has exited (value={}, {})", legalName, exitValue, errors)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val future = openFuture<URI>()
|
val future = openFuture<URI>()
|
||||||
thread {
|
thread {
|
||||||
future.capture {
|
future.capture {
|
||||||
log.info("Waiting for web server for ${config.legalName} to start ...")
|
log.info("Waiting for web server for $legalName to start ...")
|
||||||
waitForStart(config.webPort)
|
waitForStart(config.nodeConfig.webAddress.port)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return future
|
return future
|
||||||
} catch (e: IOException) {
|
} catch (e: IOException) {
|
||||||
log.error("Failed to launch Web Server for '{}': {}", config.legalName, e.message)
|
log.error("Failed to launch Web Server for '{}': {}", legalName, e.message)
|
||||||
throw e
|
throw e
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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.config.LoggingConfig
|
||||||
import net.corda.demobench.model.JVMConfigTest
|
import net.corda.demobench.model.JVMConfigTest
|
||||||
import net.corda.demobench.model.NodeControllerTest
|
import net.corda.demobench.model.NodeControllerTest
|
||||||
import net.corda.demobench.model.ServiceControllerTest
|
|
||||||
import org.junit.BeforeClass
|
import org.junit.BeforeClass
|
||||||
import org.junit.runner.RunWith
|
import org.junit.runner.RunWith
|
||||||
import org.junit.runners.Suite
|
import org.junit.runners.Suite
|
||||||
@ -13,7 +12,6 @@ import org.junit.runners.Suite
|
|||||||
*/
|
*/
|
||||||
@RunWith(Suite::class)
|
@RunWith(Suite::class)
|
||||||
@Suite.SuiteClasses(
|
@Suite.SuiteClasses(
|
||||||
ServiceControllerTest::class,
|
|
||||||
NodeControllerTest::class,
|
NodeControllerTest::class,
|
||||||
JVMConfigTest::class
|
JVMConfigTest::class
|
||||||
)
|
)
|
||||||
|
@ -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
|
package net.corda.demobench.model
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonInclude
|
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper
|
|
||||||
import com.fasterxml.jackson.databind.SerializationFeature
|
|
||||||
import com.typesafe.config.ConfigFactory
|
import com.typesafe.config.ConfigFactory
|
||||||
import com.typesafe.config.ConfigValueFactory
|
import com.typesafe.config.ConfigValueFactory
|
||||||
import net.corda.core.identity.CordaX500Name
|
import net.corda.core.identity.CordaX500Name
|
||||||
import net.corda.core.internal.div
|
|
||||||
import net.corda.core.utilities.NetworkHostAndPort
|
import net.corda.core.utilities.NetworkHostAndPort
|
||||||
import net.corda.node.internal.NetworkMapInfo
|
import net.corda.node.internal.NetworkMapInfo
|
||||||
import net.corda.node.services.config.FullNodeConfiguration
|
import net.corda.node.services.config.FullNodeConfiguration
|
||||||
import net.corda.nodeapi.User
|
import net.corda.nodeapi.User
|
||||||
import net.corda.nodeapi.config.parseAs
|
import net.corda.nodeapi.config.parseAs
|
||||||
|
import net.corda.nodeapi.config.toConfig
|
||||||
import net.corda.testing.DUMMY_NOTARY
|
import net.corda.testing.DUMMY_NOTARY
|
||||||
import net.corda.webserver.WebServerConfig
|
import net.corda.webserver.WebServerConfig
|
||||||
|
import org.assertj.core.api.Assertions.assertThat
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import java.io.StringWriter
|
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
import java.nio.file.Paths
|
import java.nio.file.Paths
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
import kotlin.test.assertFalse
|
import kotlin.test.assertFalse
|
||||||
import kotlin.test.assertNull
|
|
||||||
import kotlin.test.assertTrue
|
import kotlin.test.assertTrue
|
||||||
|
|
||||||
|
|
||||||
class NodeConfigTest {
|
class NodeConfigTest {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private val baseDir: Path = Paths.get(".").toAbsolutePath()
|
private val baseDir: Path = Paths.get(".").toAbsolutePath()
|
||||||
private val myLegalName = CordaX500Name(organisation = "My Name", locality = "New York", country = "US")
|
private val myLegalName = CordaX500Name(organisation = "My Name", locality = "New York", country = "US")
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `test name`() {
|
|
||||||
val config = createConfig(legalName = myLegalName)
|
|
||||||
assertEquals(myLegalName, config.legalName)
|
|
||||||
assertEquals("myname", config.key)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `test node directory`() {
|
|
||||||
val config = createConfig(legalName = myLegalName)
|
|
||||||
assertEquals(baseDir / "myname", config.nodeDir)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `test explorer directory`() {
|
|
||||||
val config = createConfig(legalName = myLegalName)
|
|
||||||
assertEquals(baseDir / "myname-explorer", config.explorerDir)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `test plugin directory`() {
|
|
||||||
val config = createConfig(legalName = myLegalName)
|
|
||||||
assertEquals(baseDir / "myname" / "plugins", config.pluginDir)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `test P2P port`() {
|
|
||||||
val config = createConfig(p2pPort = 10001)
|
|
||||||
assertEquals(10001, config.p2pPort)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `test rpc port`() {
|
|
||||||
val config = createConfig(rpcPort = 40002)
|
|
||||||
assertEquals(40002, config.rpcPort)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `test web port`() {
|
|
||||||
val config = createConfig(webPort = 20001)
|
|
||||||
assertEquals(20001, config.webPort)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `test H2 port`() {
|
|
||||||
val config = createConfig(h2Port = 30001)
|
|
||||||
assertEquals(30001, config.h2Port)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `test services`() {
|
|
||||||
val config = createConfig(services = mutableListOf("my.service"))
|
|
||||||
assertEquals(listOf("my.service"), config.extraServices)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `test users`() {
|
|
||||||
val config = createConfig(users = listOf(user("myuser")))
|
|
||||||
assertEquals(listOf(user("myuser")), config.users)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `test default state`() {
|
|
||||||
val config = createConfig()
|
|
||||||
assertEquals(NodeState.STARTING, config.state)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `test network map`() {
|
|
||||||
val config = createConfig()
|
|
||||||
assertNull(config.networkMap)
|
|
||||||
assertTrue(config.isNetworkMap())
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `test cash issuer`() {
|
|
||||||
val config = createConfig(services = mutableListOf("corda.issuer.GBP"))
|
|
||||||
assertTrue(config.isCashIssuer)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `test not cash issuer`() {
|
|
||||||
val config = createConfig(services = mutableListOf("corda.issuerubbish"))
|
|
||||||
assertFalse(config.isCashIssuer)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reformat JSON via Jackson to ensure a consistent format for comparison purposes.
|
|
||||||
*/
|
|
||||||
private fun prettyPrint(content: String): String {
|
|
||||||
val mapper = ObjectMapper()
|
|
||||||
mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL)
|
|
||||||
mapper.enable(SerializationFeature.INDENT_OUTPUT)
|
|
||||||
val sw = StringWriter()
|
|
||||||
val parsed = mapper.readTree(content)
|
|
||||||
mapper.writeValue(sw, parsed)
|
|
||||||
return sw.toString()
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `test config text`() {
|
|
||||||
val config = createConfig(
|
|
||||||
legalName = myLegalName,
|
|
||||||
p2pPort = 10001,
|
|
||||||
rpcPort = 40002,
|
|
||||||
webPort = 20001,
|
|
||||||
h2Port = 30001,
|
|
||||||
services = mutableListOf("my.service"),
|
|
||||||
users = listOf(user("jenny"))
|
|
||||||
)
|
|
||||||
assertEquals(prettyPrint("{"
|
|
||||||
+ "\"detectPublicIp\":false,"
|
|
||||||
+ "\"extraAdvertisedServiceIds\":[\"my.service\"],"
|
|
||||||
+ "\"h2port\":30001,"
|
|
||||||
+ "\"myLegalName\":\"C=US,L=New York,O=My Name\","
|
|
||||||
+ "\"p2pAddress\":\"localhost:10001\","
|
|
||||||
+ "\"rpcAddress\":\"localhost:40002\","
|
|
||||||
+ "\"rpcUsers\":["
|
|
||||||
+ "{\"password\":\"letmein\",\"permissions\":[\"ALL\"],\"username\":\"jenny\"}"
|
|
||||||
+ "],"
|
|
||||||
+ "\"useTestClock\":true,"
|
|
||||||
+ "\"webAddress\":\"localhost:20001\""
|
|
||||||
+ "}"), prettyPrint(config.toText()))
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `test config text with network map`() {
|
|
||||||
val config = createConfig(
|
|
||||||
legalName = myLegalName,
|
|
||||||
p2pPort = 10001,
|
|
||||||
rpcPort = 40002,
|
|
||||||
webPort = 20001,
|
|
||||||
h2Port = 30001,
|
|
||||||
services = mutableListOf("my.service"),
|
|
||||||
users = listOf(user("jenny"))
|
|
||||||
)
|
|
||||||
config.networkMap = NetworkMapConfig(DUMMY_NOTARY.name, 12345)
|
|
||||||
|
|
||||||
assertEquals(prettyPrint("{"
|
|
||||||
+ "\"detectPublicIp\":false,"
|
|
||||||
+ "\"extraAdvertisedServiceIds\":[\"my.service\"],"
|
|
||||||
+ "\"h2port\":30001,"
|
|
||||||
+ "\"myLegalName\":\"C=US,L=New York,O=My Name\","
|
|
||||||
+ "\"networkMapService\":{\"address\":\"localhost:12345\",\"legalName\":\"C=CH,L=Zurich,O=Notary Service\"},"
|
|
||||||
+ "\"p2pAddress\":\"localhost:10001\","
|
|
||||||
+ "\"rpcAddress\":\"localhost:40002\","
|
|
||||||
+ "\"rpcUsers\":["
|
|
||||||
+ "{\"password\":\"letmein\",\"permissions\":[\"ALL\"],\"username\":\"jenny\"}"
|
|
||||||
+ "],"
|
|
||||||
+ "\"useTestClock\":true,"
|
|
||||||
+ "\"webAddress\":\"localhost:20001\""
|
|
||||||
+ "}"), prettyPrint(config.toText()))
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `reading node configuration`() {
|
fun `reading node configuration`() {
|
||||||
val config = createConfig(
|
val config = createConfig(
|
||||||
@ -192,13 +32,13 @@ class NodeConfigTest {
|
|||||||
p2pPort = 10001,
|
p2pPort = 10001,
|
||||||
rpcPort = 40002,
|
rpcPort = 40002,
|
||||||
webPort = 20001,
|
webPort = 20001,
|
||||||
h2Port = 30001,
|
h2port = 30001,
|
||||||
services = mutableListOf("my.service"),
|
notary = NotaryService(validating = false),
|
||||||
|
networkMap = NetworkMapConfig(DUMMY_NOTARY.name, localPort(12345)),
|
||||||
users = listOf(user("jenny"))
|
users = listOf(user("jenny"))
|
||||||
)
|
)
|
||||||
config.networkMap = NetworkMapConfig(DUMMY_NOTARY.name, 12345)
|
|
||||||
|
|
||||||
val nodeConfig = config.toFileConfig()
|
val nodeConfig = config.toConfig()
|
||||||
.withValue("baseDirectory", ConfigValueFactory.fromAnyRef(baseDir.toString()))
|
.withValue("baseDirectory", ConfigValueFactory.fromAnyRef(baseDir.toString()))
|
||||||
.withFallback(ConfigFactory.parseResources("reference.conf"))
|
.withFallback(ConfigFactory.parseResources("reference.conf"))
|
||||||
.resolve()
|
.resolve()
|
||||||
@ -207,10 +47,9 @@ class NodeConfigTest {
|
|||||||
assertEquals(myLegalName, fullConfig.myLegalName)
|
assertEquals(myLegalName, fullConfig.myLegalName)
|
||||||
assertEquals(localPort(40002), fullConfig.rpcAddress)
|
assertEquals(localPort(40002), fullConfig.rpcAddress)
|
||||||
assertEquals(localPort(10001), fullConfig.p2pAddress)
|
assertEquals(localPort(10001), fullConfig.p2pAddress)
|
||||||
assertEquals(listOf("my.service"), fullConfig.extraAdvertisedServiceIds)
|
|
||||||
assertEquals(listOf(user("jenny")), fullConfig.rpcUsers)
|
assertEquals(listOf(user("jenny")), fullConfig.rpcUsers)
|
||||||
assertEquals(NetworkMapInfo(localPort(12345), DUMMY_NOTARY.name), fullConfig.networkMapService)
|
assertEquals(NetworkMapInfo(localPort(12345), DUMMY_NOTARY.name), fullConfig.networkMapService)
|
||||||
assertTrue((fullConfig.dataSourceProperties["dataSource.url"] as String).contains("AUTO_SERVER_PORT=30001"))
|
assertThat(fullConfig.dataSourceProperties["dataSource.url"] as String).contains("AUTO_SERVER_PORT=30001")
|
||||||
assertTrue(fullConfig.useTestClock)
|
assertTrue(fullConfig.useTestClock)
|
||||||
assertFalse(fullConfig.detectPublicIp)
|
assertFalse(fullConfig.detectPublicIp)
|
||||||
}
|
}
|
||||||
@ -222,13 +61,13 @@ class NodeConfigTest {
|
|||||||
p2pPort = 10001,
|
p2pPort = 10001,
|
||||||
rpcPort = 40002,
|
rpcPort = 40002,
|
||||||
webPort = 20001,
|
webPort = 20001,
|
||||||
h2Port = 30001,
|
h2port = 30001,
|
||||||
services = mutableListOf("my.service"),
|
notary = NotaryService(validating = false),
|
||||||
|
networkMap = NetworkMapConfig(DUMMY_NOTARY.name, localPort(12345)),
|
||||||
users = listOf(user("jenny"))
|
users = listOf(user("jenny"))
|
||||||
)
|
)
|
||||||
config.networkMap = NetworkMapConfig(DUMMY_NOTARY.name, 12345)
|
|
||||||
|
|
||||||
val nodeConfig = config.toFileConfig()
|
val nodeConfig = config.toConfig()
|
||||||
.withValue("baseDirectory", ConfigValueFactory.fromAnyRef(baseDir.toString()))
|
.withValue("baseDirectory", ConfigValueFactory.fromAnyRef(baseDir.toString()))
|
||||||
.withFallback(ConfigFactory.parseResources("web-reference.conf"))
|
.withFallback(ConfigFactory.parseResources("web-reference.conf"))
|
||||||
.resolve()
|
.resolve()
|
||||||
@ -240,35 +79,27 @@ class NodeConfigTest {
|
|||||||
assertEquals("cordacadevpass", webConfig.keyStorePassword)
|
assertEquals("cordacadevpass", webConfig.keyStorePassword)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `test moving`() {
|
|
||||||
val config = createConfig(legalName = myLegalName)
|
|
||||||
|
|
||||||
val elsewhere = baseDir / "elsewhere"
|
|
||||||
val moved = config.moveTo(elsewhere)
|
|
||||||
assertEquals(elsewhere / "myname", moved.nodeDir)
|
|
||||||
assertEquals(elsewhere / "myname-explorer", moved.explorerDir)
|
|
||||||
assertEquals(elsewhere / "myname" / "plugins", moved.pluginDir)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun createConfig(
|
private fun createConfig(
|
||||||
legalName: CordaX500Name = CordaX500Name(organisation = "Unknown", locality = "Nowhere", country = "GB"),
|
legalName: CordaX500Name = CordaX500Name(organisation = "Unknown", locality = "Nowhere", country = "GB"),
|
||||||
p2pPort: Int = -1,
|
p2pPort: Int = -1,
|
||||||
rpcPort: Int = -1,
|
rpcPort: Int = -1,
|
||||||
webPort: Int = -1,
|
webPort: Int = -1,
|
||||||
h2Port: Int = -1,
|
h2port: Int = -1,
|
||||||
services: MutableList<String> = mutableListOf("extra.service"),
|
notary: NotaryService?,
|
||||||
|
networkMap: NetworkMapConfig?,
|
||||||
users: List<User> = listOf(user("guest"))
|
users: List<User> = listOf(user("guest"))
|
||||||
) = NodeConfig(
|
): NodeConfig {
|
||||||
baseDir,
|
return NodeConfig(
|
||||||
legalName = legalName,
|
myLegalName = legalName,
|
||||||
p2pPort = p2pPort,
|
p2pAddress = localPort(p2pPort),
|
||||||
rpcPort = rpcPort,
|
rpcAddress = localPort(rpcPort),
|
||||||
webPort = webPort,
|
webAddress = localPort(webPort),
|
||||||
h2Port = h2Port,
|
h2port = h2port,
|
||||||
extraServices = services,
|
notary = notary,
|
||||||
users = users
|
networkMapService = networkMap,
|
||||||
|
rpcUsers = users
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
private fun localPort(port: Int) = NetworkHostAndPort("localhost", port)
|
private fun localPort(port: Int) = NetworkHostAndPort("localhost", port)
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package net.corda.demobench.model
|
package net.corda.demobench.model
|
||||||
|
|
||||||
import net.corda.core.identity.CordaX500Name
|
import net.corda.core.identity.CordaX500Name
|
||||||
|
import net.corda.core.utilities.NetworkHostAndPort
|
||||||
import net.corda.nodeapi.User
|
import net.corda.nodeapi.User
|
||||||
import net.corda.testing.DUMMY_NOTARY
|
import net.corda.testing.DUMMY_NOTARY
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
@ -51,7 +52,7 @@ class NodeControllerTest {
|
|||||||
fun `test first validated node becomes network map`() {
|
fun `test first validated node becomes network map`() {
|
||||||
val data = NodeData()
|
val data = NodeData()
|
||||||
data.legalName.value = node1Name
|
data.legalName.value = node1Name
|
||||||
data.p2pPort.value = 100000
|
data.p2pPort.value = 10000
|
||||||
|
|
||||||
assertFalse(controller.hasNetworkMap())
|
assertFalse(controller.hasNetworkMap())
|
||||||
controller.validate(data)
|
controller.validate(data)
|
||||||
@ -90,7 +91,7 @@ class NodeControllerTest {
|
|||||||
@Test
|
@Test
|
||||||
fun `test register network map node`() {
|
fun `test register network map node`() {
|
||||||
val config = createConfig(commonName = "Organisation is Network Map")
|
val config = createConfig(commonName = "Organisation is Network Map")
|
||||||
assertTrue(config.isNetworkMap())
|
assertTrue(config.nodeConfig.isNetworkMap)
|
||||||
|
|
||||||
assertFalse(controller.hasNetworkMap())
|
assertFalse(controller.hasNetworkMap())
|
||||||
controller.register(config)
|
controller.register(config)
|
||||||
@ -99,9 +100,10 @@ class NodeControllerTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `test register non-network-map node`() {
|
fun `test register non-network-map node`() {
|
||||||
val config = createConfig(commonName = "Organisation is not Network Map")
|
val config = createConfig(
|
||||||
config.networkMap = NetworkMapConfig(DUMMY_NOTARY.name, 10000)
|
commonName = "Organisation is not Network Map",
|
||||||
assertFalse(config.isNetworkMap())
|
networkMap = NetworkMapConfig(DUMMY_NOTARY.name, localPort(10000)))
|
||||||
|
assertFalse(config.nodeConfig.isNetworkMap)
|
||||||
|
|
||||||
assertFalse(controller.hasNetworkMap())
|
assertFalse(controller.hasNetworkMap())
|
||||||
controller.register(config)
|
controller.register(config)
|
||||||
@ -146,7 +148,7 @@ class NodeControllerTest {
|
|||||||
@Test
|
@Test
|
||||||
fun `test H2 port is max`() {
|
fun `test H2 port is max`() {
|
||||||
val portNumber = NodeController.firstPort + 3478
|
val portNumber = NodeController.firstPort + 3478
|
||||||
val config = createConfig(h2Port = portNumber)
|
val config = createConfig(h2port = portNumber)
|
||||||
assertEquals(NodeController.firstPort, controller.nextPort)
|
assertEquals(NodeController.firstPort, controller.nextPort)
|
||||||
controller.register(config)
|
controller.register(config)
|
||||||
assertEquals(portNumber + 1, controller.nextPort)
|
assertEquals(portNumber + 1, controller.nextPort)
|
||||||
@ -166,25 +168,30 @@ class NodeControllerTest {
|
|||||||
|
|
||||||
private fun createConfig(
|
private fun createConfig(
|
||||||
commonName: String = "Unknown",
|
commonName: String = "Unknown",
|
||||||
p2pPort: Int = -1,
|
p2pPort: Int = 0,
|
||||||
rpcPort: Int = -1,
|
rpcPort: Int = 0,
|
||||||
webPort: Int = -1,
|
webPort: Int = 0,
|
||||||
h2Port: Int = -1,
|
h2port: Int = 0,
|
||||||
services: MutableList<String> = mutableListOf("extra.service"),
|
notary: NotaryService? = null,
|
||||||
|
networkMap: NetworkMapConfig? = null,
|
||||||
users: List<User> = listOf(user("guest"))
|
users: List<User> = listOf(user("guest"))
|
||||||
) = NodeConfig(
|
): NodeConfigWrapper {
|
||||||
baseDir,
|
val nodeConfig = NodeConfig(
|
||||||
legalName = CordaX500Name(
|
myLegalName = CordaX500Name(
|
||||||
organisation = commonName,
|
organisation = commonName,
|
||||||
locality = "New York",
|
locality = "New York",
|
||||||
country = "US"
|
country = "US"
|
||||||
),
|
),
|
||||||
p2pPort = p2pPort,
|
p2pAddress = localPort(p2pPort),
|
||||||
rpcPort = rpcPort,
|
rpcAddress = localPort(rpcPort),
|
||||||
webPort = webPort,
|
webAddress = localPort(webPort),
|
||||||
h2Port = h2Port,
|
h2port = h2port,
|
||||||
extraServices = services,
|
notary = notary,
|
||||||
users = users
|
networkMapService = networkMap,
|
||||||
|
rpcUsers = users
|
||||||
)
|
)
|
||||||
|
return NodeConfigWrapper(baseDir, nodeConfig)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun localPort(port: Int) = NetworkHostAndPort("localhost", port)
|
||||||
}
|
}
|
||||||
|
@ -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.finance.flows.CashIssueAndPaymentFlow.IssueAndPaymentRequest
|
||||||
import net.corda.node.services.FlowPermissions.Companion.startFlowPermission
|
import net.corda.node.services.FlowPermissions.Companion.startFlowPermission
|
||||||
import net.corda.nodeapi.User
|
import net.corda.nodeapi.User
|
||||||
import net.corda.nodeapi.internal.ServiceInfo
|
|
||||||
import net.corda.nodeapi.internal.ServiceType
|
|
||||||
import net.corda.testing.ALICE
|
import net.corda.testing.ALICE
|
||||||
import net.corda.testing.BOB
|
import net.corda.testing.BOB
|
||||||
import net.corda.testing.DUMMY_NOTARY
|
import net.corda.testing.DUMMY_NOTARY
|
||||||
@ -71,19 +69,19 @@ class ExplorerSimulation(val options: OptionSet) {
|
|||||||
// TODO : Supported flow should be exposed somehow from the node instead of set of ServiceInfo.
|
// TODO : Supported flow should be exposed somehow from the node instead of set of ServiceInfo.
|
||||||
val notary = startNotaryNode(DUMMY_NOTARY.name, customOverrides = mapOf("nearestCity" to "Zurich"), validating = false)
|
val notary = startNotaryNode(DUMMY_NOTARY.name, customOverrides = mapOf("nearestCity" to "Zurich"), validating = false)
|
||||||
val alice = startNode(providedName = ALICE.name, rpcUsers = arrayListOf(user),
|
val alice = startNode(providedName = ALICE.name, rpcUsers = arrayListOf(user),
|
||||||
advertisedServices = setOf(ServiceInfo(ServiceType.corda.getSubType("cash"))),
|
|
||||||
customOverrides = mapOf("nearestCity" to "Milan"))
|
customOverrides = mapOf("nearestCity" to "Milan"))
|
||||||
val bob = startNode(providedName = BOB.name, rpcUsers = arrayListOf(user),
|
val bob = startNode(providedName = BOB.name, rpcUsers = arrayListOf(user),
|
||||||
advertisedServices = setOf(ServiceInfo(ServiceType.corda.getSubType("cash"))),
|
|
||||||
customOverrides = mapOf("nearestCity" to "Madrid"))
|
customOverrides = mapOf("nearestCity" to "Madrid"))
|
||||||
val ukBankName = CordaX500Name(organisation = "UK Bank Plc", locality = "London", country = "GB")
|
val ukBankName = CordaX500Name(organisation = "UK Bank Plc", locality = "London", country = "GB")
|
||||||
val usaBankName = CordaX500Name(organisation = "USA Bank Corp", locality = "New York", country = "US")
|
val usaBankName = CordaX500Name(organisation = "USA Bank Corp", locality = "New York", country = "US")
|
||||||
val issuerGBP = startNode(providedName = ukBankName, rpcUsers = arrayListOf(manager),
|
val issuerGBP = startNode(providedName = ukBankName, rpcUsers = arrayListOf(manager),
|
||||||
advertisedServices = setOf(ServiceInfo(ServiceType.corda.getSubType("issuer.GBP"))),
|
customOverrides = mapOf(
|
||||||
customOverrides = mapOf("nearestCity" to "London"))
|
"issuableCurrencies" to listOf("GBP"),
|
||||||
|
"nearestCity" to "London"))
|
||||||
val issuerUSD = startNode(providedName = usaBankName, rpcUsers = arrayListOf(manager),
|
val issuerUSD = startNode(providedName = usaBankName, rpcUsers = arrayListOf(manager),
|
||||||
advertisedServices = setOf(ServiceInfo(ServiceType.corda.getSubType("issuer.USD"))),
|
customOverrides = mapOf(
|
||||||
customOverrides = mapOf("nearestCity" to "New York"))
|
"issuableCurrencies" to listOf("USD"),
|
||||||
|
"nearestCity" to "New York"))
|
||||||
|
|
||||||
notaryNode = notary.get()
|
notaryNode = notary.get()
|
||||||
aliceNode = alice.get()
|
aliceNode = alice.get()
|
||||||
|
@ -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("."))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
Reference in New Issue
Block a user