Added bits of versioning info to the node

This commit is contained in:
Shams Asari 2017-03-06 14:19:04 +00:00
parent 570b871524
commit e9d63b2662
23 changed files with 211 additions and 83 deletions

View File

@ -47,6 +47,7 @@ buildscript {
classpath 'com.github.ben-manes:gradle-versions-plugin:0.13.0' classpath 'com.github.ben-manes:gradle-versions-plugin:0.13.0'
classpath "org.jetbrains.kotlin:kotlin-noarg:$kotlin_version" classpath "org.jetbrains.kotlin:kotlin-noarg:$kotlin_version"
classpath "org.jetbrains.dokka:dokka-gradle-plugin:${dokka_version}" classpath "org.jetbrains.dokka:dokka-gradle-plugin:${dokka_version}"
classpath "org.ajoberstar:grgit:1.1.0"
} }
} }
@ -56,6 +57,10 @@ plugins {
id "us.kirchmeier.capsule" version "1.0.2" id "us.kirchmeier.capsule" version "1.0.2"
} }
ext {
corda_revision = org.ajoberstar.grgit.Grgit.open(file('.')).head().id
}
apply plugin: 'kotlin' apply plugin: 'kotlin'
apply plugin: 'project-report' apply plugin: 'project-report'
apply plugin: 'com.github.ben-manes.versions' apply plugin: 'com.github.ben-manes.versions'

View File

@ -146,6 +146,7 @@ inline fun Path.write(createDirs: Boolean = false, vararg options: OpenOption =
} }
inline fun <R> Path.readLines(charset: Charset = UTF_8, block: (Stream<String>) -> R): R = Files.lines(this, charset).use(block) inline fun <R> Path.readLines(charset: Charset = UTF_8, block: (Stream<String>) -> R): R = Files.lines(this, charset).use(block)
fun Path.readAllLines(charset: Charset = UTF_8): List<String> = Files.readAllLines(this, charset)
fun Path.writeLines(lines: Iterable<CharSequence>, charset: Charset = UTF_8, vararg options: OpenOption): Path = Files.write(this, lines, charset, *options) fun Path.writeLines(lines: Iterable<CharSequence>, charset: Charset = UTF_8, vararg options: OpenOption): Path = Files.write(this, lines, charset, *options)
fun InputStream.copyTo(target: Path, vararg options: CopyOption): Long = Files.copy(this, target, *options) fun InputStream.copyTo(target: Path, vararg options: CopyOption): Long = Files.copy(this, target, *options)

View File

@ -19,6 +19,7 @@ data class ServiceEntry(val info: ServiceInfo, val identity: Party)
@CordaSerializable @CordaSerializable
data class NodeInfo(val address: SingleMessageRecipient, data class NodeInfo(val address: SingleMessageRecipient,
val legalIdentity: Party, val legalIdentity: Party,
val version: Version,
var advertisedServices: List<ServiceEntry> = emptyList(), var advertisedServices: List<ServiceEntry> = emptyList(),
val physicalLocation: PhysicalLocation? = null) { val physicalLocation: PhysicalLocation? = null) {
init { init {

View File

@ -0,0 +1,28 @@
package net.corda.core.node
import net.corda.core.serialization.CordaSerializable
import java.util.regex.Pattern
/**
* Versions of the same [major] version but with different [minor] versions are considered compatible with each other. One
* exception to this is when the major version is 0 - each different minor version should be considered incompatible.
*
* If two [Version]s are equal (i.e. [equals] returns true) but they are both [snapshot] then they may refer to different
* builds of the node. [NodeVersionInfo.revision] would be required to differentiate the two.
*/
@CordaSerializable
data class Version(val major: Int, val minor: Int, val snapshot: Boolean) {
companion object {
private val pattern = Pattern.compile("""(\d+)\.(\d+)(-SNAPSHOT)?""")
fun parse(string: String): Version {
val matcher = pattern.matcher(string)
require(matcher.matches())
return Version(matcher.group(1).toInt(), matcher.group(2).toInt(), matcher.group(3) != null)
}
}
override fun toString(): String = if (snapshot) "$major.$minor-SNAPSHOT" else "$major.$minor"
}
data class NodeVersionInfo(val version: Version, val revision: String, val vendor: String)

View File

@ -0,0 +1,31 @@
package net.corda.core.node
import org.assertj.core.api.Assertions.assertThat
import org.assertj.core.api.Assertions.assertThatThrownBy
import org.junit.Test
class VersionTest {
@Test
fun `parse valid non-SNAPSHOT string`() {
assertThat(Version.parse("1.2")).isEqualTo(Version(1, 2, false))
}
@Test
fun `parse valid SNAPSHOT string`() {
assertThat(Version.parse("2.23-SNAPSHOT")).isEqualTo(Version(2, 23, true))
}
@Test
fun `parse string with just major number`() {
assertThatThrownBy {
Version.parse("2")
}.isInstanceOf(IllegalArgumentException::class.java)
}
@Test
fun `parse string with unknown qualifier`() {
assertThatThrownBy {
Version.parse("2.3-TEST")
}.isInstanceOf(IllegalArgumentException::class.java)
}
}

View File

@ -87,6 +87,9 @@ dependencies {
// JAnsi: for drawing things to the terminal in nicely coloured ways. // JAnsi: for drawing things to the terminal in nicely coloured ways.
compile "org.fusesource.jansi:jansi:$jansi_version" compile "org.fusesource.jansi:jansi:$jansi_version"
// Manifests: for reading stuff from the manifest file
compile "com.jcabi:jcabi-manifests:1.1"
// GraphStream: For visualisation // GraphStream: For visualisation
testCompile "org.graphstream:gs-core:1.3" testCompile "org.graphstream:gs-core:1.3"
testCompile("org.graphstream:gs-ui:1.3") { testCompile("org.graphstream:gs-ui:1.3") {

View File

@ -59,7 +59,6 @@ task buildCordaJAR(type: FatCapsule) {
appClassPath = ["jolokia-agent-war-${project.rootProject.ext.jolokia_version}.war"] appClassPath = ["jolokia-agent-war-${project.rootProject.ext.jolokia_version}.war"]
javaAgents = ["quasar-core-${quasar_version}-jdk8.jar"] javaAgents = ["quasar-core-${quasar_version}-jdk8.jar"]
systemProperties['visualvm.display.name'] = 'Corda' systemProperties['visualvm.display.name'] = 'Corda'
systemProperties['corda.version'] = corda_version
minJavaVersion = '1.8.0' minJavaVersion = '1.8.0'
// This version is known to work and avoids earlier 8u versions that have bugs. // This version is known to work and avoids earlier 8u versions that have bugs.
minUpdateVersion['1.8'] = '102' minUpdateVersion['1.8'] = '102'
@ -80,6 +79,8 @@ task buildCordaJAR(type: FatCapsule) {
manifest { manifest {
attributes('Corda-Version': corda_version) attributes('Corda-Version': corda_version)
attributes('Corda-Revision': corda_revision)
attributes('Corda-Vendor': 'Corda Open Source')
} }
} }

View File

@ -15,6 +15,7 @@ import net.corda.node.services.network.NetworkMapService
import net.corda.node.services.network.NetworkMapService.RegistrationRequest import net.corda.node.services.network.NetworkMapService.RegistrationRequest
import net.corda.node.services.network.NodeRegistration import net.corda.node.services.network.NodeRegistration
import net.corda.node.utilities.AddOrRemove import net.corda.node.utilities.AddOrRemove
import net.corda.testing.MOCK_VERSION
import net.corda.testing.TestNodeConfiguration import net.corda.testing.TestNodeConfiguration
import net.corda.testing.node.NodeBasedTest import net.corda.testing.node.NodeBasedTest
import net.corda.testing.node.SimpleNode import net.corda.testing.node.SimpleNode
@ -62,7 +63,7 @@ class P2PSecurityTest : NodeBasedTest() {
} }
private fun SimpleNode.registerWithNetworkMap(registrationName: String): ListenableFuture<NetworkMapService.RegistrationResponse> { private fun SimpleNode.registerWithNetworkMap(registrationName: String): ListenableFuture<NetworkMapService.RegistrationResponse> {
val nodeInfo = NodeInfo(net.myAddress, Party(registrationName, identity.public)) val nodeInfo = NodeInfo(net.myAddress, Party(registrationName, identity.public), MOCK_VERSION)
val registration = NodeRegistration(nodeInfo, System.currentTimeMillis(), AddOrRemove.ADD, Instant.MAX) val registration = NodeRegistration(nodeInfo, System.currentTimeMillis(), AddOrRemove.ADD, Instant.MAX)
val request = RegistrationRequest(registration.toWire(identity.private), net.myAddress) val request = RegistrationRequest(registration.toWire(identity.private), net.myAddress)
return net.sendRequest<NetworkMapService.RegistrationResponse>(NetworkMapService.REGISTER_TOPIC, request, networkMapNode.net.myAddress) return net.sendRequest<NetworkMapService.RegistrationResponse>(NetworkMapService.REGISTER_TOPIC, request, networkMapNode.net.myAddress)

View File

@ -29,6 +29,7 @@ class ArgsParser {
.defaultsTo(Level.INFO) .defaultsTo(Level.INFO)
private val logToConsoleArg = optionParser.accepts("log-to-console", "If set, prints logging to the console as well as to a file.") private val logToConsoleArg = optionParser.accepts("log-to-console", "If set, prints logging to the console as well as to a file.")
private val isRegistrationArg = optionParser.accepts("initial-registration", "Start initial node registration with Corda network to obtain certificate from the permissioning server.") private val isRegistrationArg = optionParser.accepts("initial-registration", "Start initial node registration with Corda network to obtain certificate from the permissioning server.")
private val isVersionArg = optionParser.accepts("version", "Print the version and exit")
private val helpArg = optionParser.accepts("help").forHelp() private val helpArg = optionParser.accepts("help").forHelp()
fun parse(vararg args: String): CmdLineOptions { fun parse(vararg args: String): CmdLineOptions {
@ -42,7 +43,8 @@ class ArgsParser {
val loggingLevel = optionSet.valueOf(loggerLevel) val loggingLevel = optionSet.valueOf(loggerLevel)
val logToConsole = optionSet.has(logToConsoleArg) val logToConsole = optionSet.has(logToConsoleArg)
val isRegistration = optionSet.has(isRegistrationArg) val isRegistration = optionSet.has(isRegistrationArg)
return CmdLineOptions(baseDirectory, configFile, help, loggingLevel, logToConsole, isRegistration) val isVersion = optionSet.has(isVersionArg)
return CmdLineOptions(baseDirectory, configFile, help, loggingLevel, logToConsole, isRegistration, isVersion)
} }
fun printHelp(sink: PrintStream) = optionParser.printHelpOn(sink) fun printHelp(sink: PrintStream) = optionParser.printHelpOn(sink)
@ -53,7 +55,8 @@ data class CmdLineOptions(val baseDirectory: Path,
val help: Boolean, val help: Boolean,
val loggingLevel: Level, val loggingLevel: Level,
val logToConsole: Boolean, val logToConsole: Boolean,
val isRegistration: Boolean) { val isRegistration: Boolean,
val isVersion: Boolean) {
fun loadConfig(allowMissingConfig: Boolean = false, configOverrides: Map<String, Any?> = emptyMap()): Config { fun loadConfig(allowMissingConfig: Boolean = false, configOverrides: Map<String, Any?> = emptyMap()): Config {
return ConfigHelper.loadConfig(baseDirectory, configFile, allowMissingConfig, configOverrides) return ConfigHelper.loadConfig(baseDirectory, configFile, allowMissingConfig, configOverrides)
} }

View File

@ -1,9 +1,12 @@
@file:JvmName("Corda") @file:JvmName("Corda")
package net.corda.node package net.corda.node
import com.jcabi.manifests.Manifests
import com.typesafe.config.ConfigException import com.typesafe.config.ConfigException
import joptsimple.OptionException import joptsimple.OptionException
import net.corda.core.* import net.corda.core.*
import net.corda.core.node.NodeVersionInfo
import net.corda.core.node.Version
import net.corda.core.utilities.Emoji import net.corda.core.utilities.Emoji
import net.corda.node.internal.Node import net.corda.node.internal.Node
import net.corda.node.services.config.FullNodeConfiguration import net.corda.node.services.config.FullNodeConfiguration
@ -35,6 +38,16 @@ fun main(args: Array<String>) {
val startTime = System.currentTimeMillis() val startTime = System.currentTimeMillis()
checkJavaVersion() checkJavaVersion()
val nodeVersionInfo = if (Manifests.exists("Corda-Version")) {
NodeVersionInfo(
Version.parse(Manifests.read("Corda-Version")),
Manifests.read("Corda-Revision"),
Manifests.read("Corda-Vendor"))
} else {
// If the manifest properties aren't available then we're running from within an IDE
NodeVersionInfo(Version(0, 0, false), "~Git revision unavailable~", "Unknown vendor")
}
val argsParser = ArgsParser() val argsParser = ArgsParser()
val cmdlineOptions = try { val cmdlineOptions = try {
@ -45,6 +58,12 @@ fun main(args: Array<String>) {
exitProcess(1) exitProcess(1)
} }
if (cmdlineOptions.isVersion) {
println("${nodeVersionInfo.vendor} ${nodeVersionInfo.version}")
println("Revision ${nodeVersionInfo.revision}")
exitProcess(0)
}
// Maybe render command line help. // Maybe render command line help.
if (cmdlineOptions.help) { if (cmdlineOptions.help) {
argsParser.printHelp(System.out) argsParser.printHelp(System.out)
@ -58,7 +77,7 @@ fun main(args: Array<String>) {
renderBasicInfoToConsole = false renderBasicInfoToConsole = false
} }
drawBanner() drawBanner(nodeVersionInfo)
System.setProperty("log-path", (cmdlineOptions.baseDirectory / "logs").toString()) System.setProperty("log-path", (cmdlineOptions.baseDirectory / "logs").toString())
@ -97,7 +116,7 @@ fun main(args: Array<String>) {
try { try {
cmdlineOptions.baseDirectory.createDirectories() cmdlineOptions.baseDirectory.createDirectories()
val node = conf.createNode() val node = conf.createNode(nodeVersionInfo)
node.start() node.start()
printPluginsAndServices(node) printPluginsAndServices(node)
@ -116,6 +135,7 @@ fun main(args: Array<String>) {
log.error("Exception during node startup", e) log.error("Exception during node startup", e)
exitProcess(1) exitProcess(1)
} }
exitProcess(0) exitProcess(0)
} }
@ -154,13 +174,12 @@ private fun messageOfTheDay(): Pair<String, String> {
"Computer science and finance together.\nYou should see our crazy Christmas parties!" "Computer science and finance together.\nYou should see our crazy Christmas parties!"
) )
if (Emoji.hasEmojiTerminal) if (Emoji.hasEmojiTerminal)
messages += messages += "Kind of like a regular database but\nwith emojis, colours and ascii art. ${Emoji.coolGuy}"
"Kind of like a regular database but\nwith emojis, colours and ascii art. ${Emoji.coolGuy}"
val (a, b) = messages.randomOrNull()!!.split('\n') val (a, b) = messages.randomOrNull()!!.split('\n')
return Pair(a, b) return Pair(a, b)
} }
private fun drawBanner() { private fun drawBanner(nodeVersionInfo: NodeVersionInfo) {
// This line makes sure ANSI escapes work on Windows, where they aren't supported out of the box. // This line makes sure ANSI escapes work on Windows, where they aren't supported out of the box.
AnsiConsole.systemInstall() AnsiConsole.systemInstall()
@ -174,7 +193,7 @@ private fun drawBanner() {
/ / __ / ___/ __ / __ `/ """).fgBrightBlue().a(msg1).newline().fgBrightRed().a( / / __ / ___/ __ / __ `/ """).fgBrightBlue().a(msg1).newline().fgBrightRed().a(
"/ /___ /_/ / / / /_/ / /_/ / ").fgBrightBlue().a(msg2).newline().fgBrightRed().a( "/ /___ /_/ / / / /_/ / /_/ / ").fgBrightBlue().a(msg2).newline().fgBrightRed().a(
"""\____/ /_/ \__,_/\__,_/""").reset().newline().newline().fgBrightDefault().bold(). """\____/ /_/ \__,_/\__,_/""").reset().newline().newline().fgBrightDefault().bold().
a("--- MILESTONE 9 -------------------------------------------------------------------"). a("--- ${nodeVersionInfo.vendor} ${nodeVersionInfo.version} (${nodeVersionInfo.revision.take(6)}) -----------------------------------------------").
newline(). newline().
newline(). newline().
a("${Emoji.books}New! ").reset().a("Training now available worldwide, see https://corda.net/corda-training/"). a("${Emoji.books}New! ").reset().a("Training now available worldwide, see https://corda.net/corda-training/").

View File

@ -97,6 +97,7 @@ abstract class AbstractNode(open val configuration: NodeConfiguration,
protected abstract val log: Logger protected abstract val log: Logger
protected abstract val networkMapAddress: SingleMessageRecipient? protected abstract val networkMapAddress: SingleMessageRecipient?
protected abstract val version: Version
// We will run as much stuff in this single thread as possible to keep the risk of thread safety bugs low during the // We will run as much stuff in this single thread as possible to keep the risk of thread safety bugs low during the
// low-performance prototyping period. // low-performance prototyping period.
@ -282,7 +283,7 @@ abstract class AbstractNode(open val configuration: NodeConfiguration,
private fun makeInfo(): NodeInfo { private fun makeInfo(): NodeInfo {
val advertisedServiceEntries = makeServiceEntries() val advertisedServiceEntries = makeServiceEntries()
val legalIdentity = obtainLegalIdentity() val legalIdentity = obtainLegalIdentity()
return NodeInfo(net.myAddress, legalIdentity, advertisedServiceEntries, findMyLocation()) return NodeInfo(net.myAddress, legalIdentity, version, advertisedServiceEntries, findMyLocation())
} }
/** /**

View File

@ -4,11 +4,11 @@ import com.codahale.metrics.JmxReporter
import com.google.common.net.HostAndPort import com.google.common.net.HostAndPort
import com.google.common.util.concurrent.Futures import com.google.common.util.concurrent.Futures
import com.google.common.util.concurrent.ListenableFuture import com.google.common.util.concurrent.ListenableFuture
import net.corda.core.div import net.corda.core.*
import net.corda.core.flatMap
import net.corda.core.getOrThrow
import net.corda.core.messaging.RPCOps import net.corda.core.messaging.RPCOps
import net.corda.core.node.NodeVersionInfo
import net.corda.core.node.ServiceHub import net.corda.core.node.ServiceHub
import net.corda.core.node.Version
import net.corda.core.node.services.ServiceInfo import net.corda.core.node.services.ServiceInfo
import net.corda.core.node.services.ServiceType import net.corda.core.node.services.ServiceType
import net.corda.core.node.services.UniquenessProvider import net.corda.core.node.services.UniquenessProvider
@ -22,14 +22,15 @@ import net.corda.node.services.config.FullNodeConfiguration
import net.corda.node.services.messaging.ArtemisMessagingComponent.NetworkMapAddress import net.corda.node.services.messaging.ArtemisMessagingComponent.NetworkMapAddress
import net.corda.node.services.messaging.ArtemisMessagingServer import net.corda.node.services.messaging.ArtemisMessagingServer
import net.corda.node.services.messaging.NodeMessagingClient import net.corda.node.services.messaging.NodeMessagingClient
import net.corda.node.services.transactions.* import net.corda.node.services.transactions.PersistentUniquenessProvider
import net.corda.node.services.transactions.RaftUniquenessProvider
import net.corda.node.services.transactions.RaftValidatingNotaryService
import net.corda.node.utilities.AddressUtils import net.corda.node.utilities.AddressUtils
import net.corda.node.utilities.AffinityExecutor import net.corda.node.utilities.AffinityExecutor
import org.slf4j.Logger
import java.io.RandomAccessFile import java.io.RandomAccessFile
import java.lang.management.ManagementFactory import java.lang.management.ManagementFactory
import java.nio.channels.FileLock import java.nio.channels.FileLock
import java.nio.file.Files
import java.nio.file.Paths
import java.time.Clock import java.time.Clock
import javax.management.ObjectName import javax.management.ObjectName
import kotlin.concurrent.thread import kotlin.concurrent.thread
@ -45,8 +46,14 @@ import kotlin.concurrent.thread
*/ */
class Node(override val configuration: FullNodeConfiguration, class Node(override val configuration: FullNodeConfiguration,
advertisedServices: Set<ServiceInfo>, advertisedServices: Set<ServiceInfo>,
val nodeVersionInfo: NodeVersionInfo,
clock: Clock = NodeClock()) : AbstractNode(configuration, advertisedServices, clock) { clock: Clock = NodeClock()) : AbstractNode(configuration, advertisedServices, clock) {
override val log = loggerFor<Node>() companion object {
private val logger = loggerFor<Node>()
}
override val log: Logger get() = logger
override val version: Version get() = nodeVersionInfo.version
override val networkMapAddress: NetworkMapAddress? get() = configuration.networkMapService?.address?.let(::NetworkMapAddress) override val networkMapAddress: NetworkMapAddress? get() = configuration.networkMapService?.address?.let(::NetworkMapAddress)
// DISCUSSION // DISCUSSION
@ -103,35 +110,32 @@ class Node(override val configuration: FullNodeConfiguration,
} }
/** /**
* Abort starting the node if an existing deployment with a different version is detected in the current directory. * Abort starting the node if an existing deployment with a different version is detected in the base directory.
* The current version is expected to be specified as a system property. If not provided, the check will be ignored.
*/ */
private fun checkVersionUnchanged() { private fun checkVersionUnchanged() {
val currentVersion = System.getProperty("corda.version") ?: return val versionFile = configuration.baseDirectory / "version"
val versionFile = Paths.get("version") if (versionFile.exists()) {
if (Files.exists(versionFile)) { val previousVersion = Version.parse(versionFile.readAllLines()[0])
val existingVersion = Files.readAllLines(versionFile)[0] check(nodeVersionInfo.version.major == previousVersion.major) {
check(existingVersion == currentVersion) { "Major version change detected - current: ${nodeVersionInfo.version}, previous: $previousVersion. " +
"Version change detected - current: $currentVersion, existing: $existingVersion. Node upgrades are not yet supported." "Node upgrades across major versions are not yet supported."
} }
} else {
Files.write(versionFile, currentVersion.toByteArray())
} }
versionFile.writeLines(listOf(nodeVersionInfo.version.toString()))
} }
override fun makeMessagingService(): MessagingServiceInternal { override fun makeMessagingService(): MessagingServiceInternal {
userService = RPCUserServiceImpl(configuration) userService = RPCUserServiceImpl(configuration)
val serverAddress = configuration.messagingServerAddress ?: makeLocalMessageBroker() val serverAddress = configuration.messagingServerAddress ?: makeLocalMessageBroker()
val myIdentityOrNullIfNetworkMapService = if (networkMapAddress != null) obtainLegalIdentity().owningKey else null val myIdentityOrNullIfNetworkMapService = if (networkMapAddress != null) obtainLegalIdentity().owningKey else null
return NodeMessagingClient( return NodeMessagingClient(
configuration, configuration,
nodeVersionInfo,
serverAddress, serverAddress,
myIdentityOrNullIfNetworkMapService, myIdentityOrNullIfNetworkMapService,
serverThread, serverThread,
database, database,
networkMapRegistrationFuture networkMapRegistrationFuture)
)
} }
private fun makeLocalMessageBroker(): HostAndPort { private fun makeLocalMessageBroker(): HostAndPort {

View File

@ -3,6 +3,7 @@ package net.corda.node.services.config
import com.google.common.net.HostAndPort import com.google.common.net.HostAndPort
import com.typesafe.config.Config import com.typesafe.config.Config
import net.corda.core.div import net.corda.core.div
import net.corda.core.node.NodeVersionInfo
import net.corda.core.node.services.ServiceInfo import net.corda.core.node.services.ServiceInfo
import net.corda.node.internal.NetworkMapInfo import net.corda.node.internal.NetworkMapInfo
import net.corda.node.internal.Node import net.corda.node.internal.Node
@ -77,7 +78,7 @@ class FullNodeConfiguration(override val baseDirectory: Path, val config: Config
.getListOrElse<String>("notaryClusterAddresses") { emptyList() } .getListOrElse<String>("notaryClusterAddresses") { emptyList() }
.map { HostAndPort.fromString(it) } .map { HostAndPort.fromString(it) }
fun createNode(): Node { fun createNode(nodeVersionInfo: NodeVersionInfo): Node {
// This is a sanity feature do not remove. // This is a sanity feature do not remove.
require(!useTestClock || devMode) { "Cannot use test clock outside of dev mode" } require(!useTestClock || devMode) { "Cannot use test clock outside of dev mode" }
@ -87,7 +88,7 @@ class FullNodeConfiguration(override val baseDirectory: Path, val config: Config
.toMutableSet() .toMutableSet()
if (networkMapService == null) advertisedServices.add(ServiceInfo(NetworkMapService.type)) if (networkMapService == null) advertisedServices.add(ServiceInfo(NetworkMapService.type))
return Node(this, advertisedServices, if (useTestClock) TestClock() else NodeClock()) return Node(this, advertisedServices, nodeVersionInfo, if (useTestClock) TestClock() else NodeClock())
} }
} }

View File

@ -5,6 +5,7 @@ import com.google.common.util.concurrent.ListenableFuture
import net.corda.core.ThreadBox import net.corda.core.ThreadBox
import net.corda.core.crypto.CompositeKey import net.corda.core.crypto.CompositeKey
import net.corda.core.messaging.* import net.corda.core.messaging.*
import net.corda.core.node.NodeVersionInfo
import net.corda.core.node.services.PartyInfo import net.corda.core.node.services.PartyInfo
import net.corda.core.serialization.SerializedBytes import net.corda.core.serialization.SerializedBytes
import net.corda.core.serialization.opaque import net.corda.core.serialization.opaque
@ -21,6 +22,7 @@ import org.apache.activemq.artemis.api.core.ActiveMQObjectClosedException
import org.apache.activemq.artemis.api.core.Message.* import org.apache.activemq.artemis.api.core.Message.*
import org.apache.activemq.artemis.api.core.SimpleString import org.apache.activemq.artemis.api.core.SimpleString
import org.apache.activemq.artemis.api.core.client.* import org.apache.activemq.artemis.api.core.client.*
import org.apache.activemq.artemis.api.core.client.ActiveMQClient.DEFAULT_ACK_BATCH_SIZE
import org.bouncycastle.asn1.x500.X500Name import org.bouncycastle.asn1.x500.X500Name
import org.jetbrains.exposed.sql.Database import org.jetbrains.exposed.sql.Database
import org.jetbrains.exposed.sql.ResultRow import org.jetbrains.exposed.sql.ResultRow
@ -53,6 +55,7 @@ import javax.annotation.concurrent.ThreadSafe
*/ */
@ThreadSafe @ThreadSafe
class NodeMessagingClient(override val config: NodeConfiguration, class NodeMessagingClient(override val config: NodeConfiguration,
nodeVersionInfo: NodeVersionInfo,
val serverHostPort: HostAndPort, val serverHostPort: HostAndPort,
val myIdentity: CompositeKey?, val myIdentity: CompositeKey?,
val nodeExecutor: AffinityExecutor, val nodeExecutor: AffinityExecutor,
@ -65,9 +68,11 @@ class NodeMessagingClient(override val config: NodeConfiguration,
// We should probably try to unify our notion of "topic" (really, just a string that identifies an endpoint // We should probably try to unify our notion of "topic" (really, just a string that identifies an endpoint
// that will handle messages, like a URL) with the terminology used by underlying MQ libraries, to avoid // that will handle messages, like a URL) with the terminology used by underlying MQ libraries, to avoid
// confusion. // confusion.
const val TOPIC_PROPERTY = "platform-topic" private val topicProperty = SimpleString("platform-topic")
const val SESSION_ID_PROPERTY = "session-id" private val sessionIdProperty = SimpleString("session-id")
private val AMQ_DELAY: Int = Integer.valueOf(System.getProperty("amq.delivery.delay.ms", "0")) private val nodeVersionProperty = SimpleString("node-version")
private val nodeVendorProperty = SimpleString("node-vendor")
private val amqDelay: Int = Integer.valueOf(System.getProperty("amq.delivery.delay.ms", "0"))
} }
private class InnerState { private class InnerState {
@ -87,6 +92,8 @@ class NodeMessagingClient(override val config: NodeConfiguration,
data class Handler(val topicSession: TopicSession, data class Handler(val topicSession: TopicSession,
val callback: (ReceivedMessage, MessageHandlerRegistration) -> Unit) : MessageHandlerRegistration val callback: (ReceivedMessage, MessageHandlerRegistration) -> Unit) : MessageHandlerRegistration
private val nodeVendor = SimpleString(nodeVersionInfo.vendor)
private val version = SimpleString(nodeVersionInfo.version.toString())
/** An executor for sending messages */ /** An executor for sending messages */
private val messagingExecutor = AffinityExecutor.ServiceAffinityExecutor("Messaging", 1) private val messagingExecutor = AffinityExecutor.ServiceAffinityExecutor("Messaging", 1)
@ -130,7 +137,7 @@ class NodeMessagingClient(override val config: NodeConfiguration,
// using our TLS certificate. // using our TLS certificate.
// Note that the acknowledgement of messages is not flushed to the Artermis journal until the default buffer // Note that the acknowledgement of messages is not flushed to the Artermis journal until the default buffer
// size of 1MB is acknowledged. // size of 1MB is acknowledged.
val session = clientFactory!!.createSession(NODE_USER, NODE_USER, false, true, true, locator.isPreAcknowledge, ActiveMQClient.DEFAULT_ACK_BATCH_SIZE) val session = clientFactory!!.createSession(NODE_USER, NODE_USER, false, true, true, locator.isPreAcknowledge, DEFAULT_ACK_BATCH_SIZE)
this.session = session this.session = session
session.start() session.start()
@ -165,7 +172,7 @@ class NodeMessagingClient(override val config: NodeConfiguration,
private fun makeP2PConsumer(session: ClientSession, networkMapOnly: Boolean): ClientConsumer { private fun makeP2PConsumer(session: ClientSession, networkMapOnly: Boolean): ClientConsumer {
return if (networkMapOnly) { return if (networkMapOnly) {
// Filter for just the network map messages. // Filter for just the network map messages.
val messageFilter = "hyphenated_props:$TOPIC_PROPERTY like 'platform.network_map.%'" val messageFilter = "hyphenated_props:$topicProperty like 'platform.network_map.%'"
session.createConsumer(P2P_QUEUE, messageFilter) session.createConsumer(P2P_QUEUE, messageFilter)
} else } else
session.createConsumer(P2P_QUEUE) session.createConsumer(P2P_QUEUE)
@ -249,18 +256,10 @@ class NodeMessagingClient(override val config: NodeConfiguration,
private fun artemisToCordaMessage(message: ClientMessage): ReceivedMessage? { private fun artemisToCordaMessage(message: ClientMessage): ReceivedMessage? {
try { try {
if (!message.containsProperty(TOPIC_PROPERTY)) { val topic = message.required(topicProperty) { getStringProperty(it) }
log.warn("Received message without a $TOPIC_PROPERTY property, ignoring") val sessionID = message.required(sessionIdProperty) { getLongProperty(it) }
return null
}
if (!message.containsProperty(SESSION_ID_PROPERTY)) {
log.warn("Received message without a $SESSION_ID_PROPERTY property, ignoring")
return null
}
val topic = message.getStringProperty(TOPIC_PROPERTY)
val sessionID = message.getLongProperty(SESSION_ID_PROPERTY)
// Use the magic deduplication property built into Artemis as our message identity too // Use the magic deduplication property built into Artemis as our message identity too
val uuid = UUID.fromString(message.getStringProperty(HDR_DUPLICATE_DETECTION_ID)) val uuid = message.required(HDR_DUPLICATE_DETECTION_ID) { UUID.fromString(message.getStringProperty(it)) }
val user = requireNotNull(message.getStringProperty(HDR_VALIDATED_USER)) { "Message is not authenticated" } val user = requireNotNull(message.getStringProperty(HDR_VALIDATED_USER)) { "Message is not authenticated" }
log.trace { "Received message from: ${message.address} user: $user topic: $topic sessionID: $sessionID uuid: $uuid" } log.trace { "Received message from: ${message.address} user: $user topic: $topic sessionID: $sessionID uuid: $uuid" }
@ -277,11 +276,16 @@ class NodeMessagingClient(override val config: NodeConfiguration,
return msg return msg
} catch (e: Exception) { } catch (e: Exception) {
log.error("Internal error whilst reading MQ message", e) log.error("Unable to process message, ignoring it: $message", e)
return null return null
} }
} }
private inline fun <T> ClientMessage.required(key: SimpleString, extractor: ClientMessage.(SimpleString) -> T): T {
require(containsProperty(key)) { "Missing $key" }
return extractor(key)
}
private fun deliver(msg: ReceivedMessage): Boolean { private fun deliver(msg: ReceivedMessage): Boolean {
state.checkNotLocked() state.checkNotLocked()
// Because handlers is a COW list, the loop inside filter will operate on a snapshot. Handlers being added // Because handlers is a COW list, the loop inside filter will operate on a snapshot. Handlers being added
@ -368,16 +372,17 @@ class NodeMessagingClient(override val config: NodeConfiguration,
state.locked { state.locked {
val mqAddress = getMQAddress(target) val mqAddress = getMQAddress(target)
val artemisMessage = session!!.createMessage(true).apply { val artemisMessage = session!!.createMessage(true).apply {
val sessionID = message.topicSession.sessionID putStringProperty(nodeVendorProperty, nodeVendor)
putStringProperty(TOPIC_PROPERTY, message.topicSession.topic) putStringProperty(nodeVersionProperty, version)
putLongProperty(SESSION_ID_PROPERTY, sessionID) putStringProperty(topicProperty, SimpleString(message.topicSession.topic))
putLongProperty(sessionIdProperty, message.topicSession.sessionID)
writeBodyBufferBytes(message.data) writeBodyBufferBytes(message.data)
// Use the magic deduplication property built into Artemis as our message identity too // Use the magic deduplication property built into Artemis as our message identity too
putStringProperty(HDR_DUPLICATE_DETECTION_ID, SimpleString(message.uniqueMessageId.toString())) putStringProperty(HDR_DUPLICATE_DETECTION_ID, SimpleString(message.uniqueMessageId.toString()))
// For demo purposes - if set then add a delay to messages in order to demonstrate that the flows are doing as intended // For demo purposes - if set then add a delay to messages in order to demonstrate that the flows are doing as intended
if (AMQ_DELAY > 0 && message.topicSession.topic == StateMachineManager.sessionTopic.topic) { if (amqDelay > 0 && message.topicSession.topic == StateMachineManager.sessionTopic.topic) {
putLongProperty(HDR_SCHEDULED_DELIVERY_TIME, System.currentTimeMillis() + AMQ_DELAY) putLongProperty(HDR_SCHEDULED_DELIVERY_TIME, System.currentTimeMillis() + amqDelay)
} }
} }
log.trace { "Send to: $mqAddress topic: ${message.topicSession.topic} " + log.trace { "Send to: $mqAddress topic: ${message.topicSession.topic} " +
@ -387,7 +392,6 @@ class NodeMessagingClient(override val config: NodeConfiguration,
} }
} }
private fun getMQAddress(target: MessageRecipients): String { private fun getMQAddress(target: MessageRecipients): String {
return if (target == myAddress) { return if (target == myAddress) {
// If we are sending to ourselves then route the message directly to our P2P queue. // If we are sending to ourselves then route the message directly to our P2P queue.

View File

@ -215,7 +215,7 @@ class FlowStateMachineImpl<R>(override val id: StateMachineRunId,
state = FlowSessionState.Initiated(peerParty, sessionInitResponse.initiatedSessionId) state = FlowSessionState.Initiated(peerParty, sessionInitResponse.initiatedSessionId)
} else { } else {
sessionInitResponse as SessionReject sessionInitResponse as SessionReject
throw FlowException("Party ${state.sendToParty} rejected session request: ${sessionInitResponse.errorMessage}") throw FlowSessionException("Party ${state.sendToParty} rejected session request: ${sessionInitResponse.errorMessage}")
} }
} }

View File

@ -20,7 +20,8 @@ class ArgsParserTest {
help = false, help = false,
logToConsole = false, logToConsole = false,
loggingLevel = Level.INFO, loggingLevel = Level.INFO,
isRegistration = false)) isRegistration = false,
isVersion = false))
} }
@Test @Test
@ -54,20 +55,6 @@ class ArgsParserTest {
assertThat(cmdLineOptions.configFile).isEqualTo(configFile) assertThat(cmdLineOptions.configFile).isEqualTo(configFile)
} }
@Test
fun `log-to-console`() {
val cmdLineOptions = parser.parse("--log-to-console")
assertThat(cmdLineOptions.logToConsole).isTrue()
}
@Test
fun `logging-level`() {
for (level in Level.values()) {
val cmdLineOptions = parser.parse("--logging-level", level.name)
assertThat(cmdLineOptions.loggingLevel).isEqualTo(level)
}
}
@Test @Test
fun `both base-directory and config-file`() { fun `both base-directory and config-file`() {
assertThatExceptionOfType(IllegalArgumentException::class.java).isThrownBy { assertThatExceptionOfType(IllegalArgumentException::class.java).isThrownBy {
@ -89,6 +76,20 @@ class ArgsParserTest {
}.withMessageContaining("config-file") }.withMessageContaining("config-file")
} }
@Test
fun `log-to-console`() {
val cmdLineOptions = parser.parse("--log-to-console")
assertThat(cmdLineOptions.logToConsole).isTrue()
}
@Test
fun `logging-level`() {
for (level in Level.values()) {
val cmdLineOptions = parser.parse("--logging-level", level.name)
assertThat(cmdLineOptions.loggingLevel).isEqualTo(level)
}
}
@Test @Test
fun `logging-level without argument`() { fun `logging-level without argument`() {
assertThatExceptionOfType(OptionException::class.java).isThrownBy { assertThatExceptionOfType(OptionException::class.java).isThrownBy {
@ -102,4 +103,16 @@ class ArgsParserTest {
parser.parse("--logging-level", "not-a-level") parser.parse("--logging-level", "not-a-level")
}.withMessageContaining("logging-level") }.withMessageContaining("logging-level")
} }
@Test
fun `initial-registration`() {
val cmdLineOptions = parser.parse("--initial-registration")
assertThat(cmdLineOptions.isRegistration).isTrue()
}
@Test
fun version() {
val cmdLineOptions = parser.parse("--version")
assertThat(cmdLineOptions.isVersion).isTrue()
}
} }

View File

@ -23,6 +23,7 @@ import net.corda.node.services.transactions.PersistentUniquenessProvider
import net.corda.node.utilities.AffinityExecutor.ServiceAffinityExecutor import net.corda.node.utilities.AffinityExecutor.ServiceAffinityExecutor
import net.corda.node.utilities.configureDatabase import net.corda.node.utilities.configureDatabase
import net.corda.node.utilities.databaseTransaction import net.corda.node.utilities.databaseTransaction
import net.corda.testing.MOCK_NODE_VERSION_INFO
import net.corda.testing.TestNodeConfiguration import net.corda.testing.TestNodeConfiguration
import net.corda.testing.freeLocalHostAndPort import net.corda.testing.freeLocalHostAndPort
import net.corda.testing.node.makeTestDataSourceProperties import net.corda.testing.node.makeTestDataSourceProperties
@ -219,6 +220,7 @@ class ArtemisMessagingTests {
return databaseTransaction(database) { return databaseTransaction(database) {
NodeMessagingClient( NodeMessagingClient(
config, config,
MOCK_NODE_VERSION_INFO,
server, server,
identity.public.composite, identity.public.composite,
ServiceAffinityExecutor("ArtemisMessagingTests", 1), ServiceAffinityExecutor("ArtemisMessagingTests", 1),

View File

@ -9,7 +9,9 @@ import com.typesafe.config.Config
import net.corda.core.contracts.StateRef import net.corda.core.contracts.StateRef
import net.corda.core.crypto.* import net.corda.core.crypto.*
import net.corda.core.flows.FlowLogic import net.corda.core.flows.FlowLogic
import net.corda.core.node.NodeVersionInfo
import net.corda.core.node.ServiceHub import net.corda.core.node.ServiceHub
import net.corda.core.node.Version
import net.corda.core.serialization.OpaqueBytes import net.corda.core.serialization.OpaqueBytes
import net.corda.core.toFuture import net.corda.core.toFuture
import net.corda.core.transactions.TransactionBuilder import net.corda.core.transactions.TransactionBuilder
@ -82,6 +84,9 @@ val ALL_TEST_KEYS: List<KeyPair> get() = listOf(MEGA_CORP_KEY, MINI_CORP_KEY, AL
val MOCK_IDENTITY_SERVICE: MockIdentityService get() = MockIdentityService(listOf(MEGA_CORP, MINI_CORP, DUMMY_NOTARY)) val MOCK_IDENTITY_SERVICE: MockIdentityService get() = MockIdentityService(listOf(MEGA_CORP, MINI_CORP, DUMMY_NOTARY))
val MOCK_VERSION = Version(0, 0, false)
val MOCK_NODE_VERSION_INFO = NodeVersionInfo(MOCK_VERSION, "Mock revision", "Mock Vendor")
fun generateStateRef() = StateRef(SecureHash.randomSHA256(), 0) fun generateStateRef() = StateRef(SecureHash.randomSHA256(), 0)
/** /**

View File

@ -7,20 +7,21 @@ import net.corda.core.messaging.SingleMessageRecipient
import net.corda.core.node.NodeInfo import net.corda.core.node.NodeInfo
import net.corda.core.node.services.NetworkMapCache import net.corda.core.node.services.NetworkMapCache
import net.corda.node.services.network.InMemoryNetworkMapCache import net.corda.node.services.network.InMemoryNetworkMapCache
import net.corda.testing.MOCK_VERSION
import rx.Observable import rx.Observable
import rx.subjects.PublishSubject import rx.subjects.PublishSubject
/** /**
* Network map cache with no backing map service. * Network map cache with no backing map service.
*/ */
class MockNetworkMapCache() : InMemoryNetworkMapCache() { class MockNetworkMapCache : InMemoryNetworkMapCache() {
override val changed: Observable<NetworkMapCache.MapChange> = PublishSubject.create<NetworkMapCache.MapChange>() override val changed: Observable<NetworkMapCache.MapChange> = PublishSubject.create<NetworkMapCache.MapChange>()
data class MockAddress(val id: String): SingleMessageRecipient data class MockAddress(val id: String): SingleMessageRecipient
init { init {
val mockNodeA = NodeInfo(MockAddress("bankC:8080"), Party("Bank C", DummyPublicKey("Bank C"))) val mockNodeA = NodeInfo(MockAddress("bankC:8080"), Party("Bank C", DummyPublicKey("Bank C")), MOCK_VERSION)
val mockNodeB = NodeInfo(MockAddress("bankD:8080"), Party("Bank D", DummyPublicKey("Bank D"))) val mockNodeB = NodeInfo(MockAddress("bankD:8080"), Party("Bank D", DummyPublicKey("Bank D")), MOCK_VERSION)
registeredNodes[mockNodeA.legalIdentity.owningKey] = mockNodeA registeredNodes[mockNodeA.legalIdentity.owningKey] = mockNodeA
registeredNodes[mockNodeB.legalIdentity.owningKey] = mockNodeB registeredNodes[mockNodeB.legalIdentity.owningKey] = mockNodeB
runWithoutMapService() runWithoutMapService()

View File

@ -12,6 +12,7 @@ import net.corda.core.messaging.SingleMessageRecipient
import net.corda.core.node.CordaPluginRegistry import net.corda.core.node.CordaPluginRegistry
import net.corda.core.node.PhysicalLocation import net.corda.core.node.PhysicalLocation
import net.corda.core.node.ServiceEntry import net.corda.core.node.ServiceEntry
import net.corda.core.node.Version
import net.corda.core.node.services.* import net.corda.core.node.services.*
import net.corda.core.utilities.DUMMY_NOTARY_KEY import net.corda.core.utilities.DUMMY_NOTARY_KEY
import net.corda.core.utilities.loggerFor import net.corda.core.utilities.loggerFor
@ -27,6 +28,7 @@ import net.corda.node.services.transactions.ValidatingNotaryService
import net.corda.node.services.vault.NodeVaultService import net.corda.node.services.vault.NodeVaultService
import net.corda.node.utilities.AffinityExecutor import net.corda.node.utilities.AffinityExecutor
import net.corda.node.utilities.AffinityExecutor.ServiceAffinityExecutor import net.corda.node.utilities.AffinityExecutor.ServiceAffinityExecutor
import net.corda.testing.MOCK_VERSION
import net.corda.testing.TestNodeConfiguration import net.corda.testing.TestNodeConfiguration
import org.apache.activemq.artemis.utils.ReusableLatch import org.apache.activemq.artemis.utils.ReusableLatch
import org.slf4j.Logger import org.slf4j.Logger
@ -129,9 +131,11 @@ class MockNetwork(private val networkSendManuallyPumped: Boolean = false,
advertisedServices: Set<ServiceInfo>, advertisedServices: Set<ServiceInfo>,
val id: Int, val id: Int,
val overrideServices: Map<ServiceInfo, KeyPair>?, val overrideServices: Map<ServiceInfo, KeyPair>?,
val entropyRoot: BigInteger = BigInteger.valueOf(random63BitValue())) : AbstractNode(config, advertisedServices, TestClock(), mockNet.busyLatch) { val entropyRoot: BigInteger = BigInteger.valueOf(random63BitValue())) :
AbstractNode(config, advertisedServices, TestClock(), mockNet.busyLatch) {
var counter = entropyRoot var counter = entropyRoot
override val log: Logger = loggerFor<MockNode>() override val log: Logger = loggerFor<MockNode>()
override val version: Version get() = MOCK_VERSION
override val serverThread: AffinityExecutor = override val serverThread: AffinityExecutor =
if (mockNet.threadPerNode) if (mockNet.threadPerNode)
ServiceAffinityExecutor("Mock node $id thread", 1) ServiceAffinityExecutor("Mock node $id thread", 1)

View File

@ -17,6 +17,7 @@ import net.corda.core.utilities.DUMMY_NOTARY
import net.corda.node.services.persistence.InMemoryStateMachineRecordedTransactionMappingStorage import net.corda.node.services.persistence.InMemoryStateMachineRecordedTransactionMappingStorage
import net.corda.testing.MEGA_CORP import net.corda.testing.MEGA_CORP
import net.corda.testing.MINI_CORP import net.corda.testing.MINI_CORP
import net.corda.testing.MOCK_VERSION
import rx.Observable import rx.Observable
import rx.subjects.PublishSubject import rx.subjects.PublishSubject
import java.io.ByteArrayInputStream import java.io.ByteArrayInputStream
@ -63,7 +64,7 @@ open class MockServices(val key: KeyPair = generateKeyPair()) : ServiceHub {
override val networkMapCache: NetworkMapCache get() = throw UnsupportedOperationException() override val networkMapCache: NetworkMapCache get() = throw UnsupportedOperationException()
override val clock: Clock get() = Clock.systemUTC() override val clock: Clock get() = Clock.systemUTC()
override val schedulerService: SchedulerService get() = throw UnsupportedOperationException() override val schedulerService: SchedulerService get() = throw UnsupportedOperationException()
override val myInfo: NodeInfo get() = NodeInfo(object : SingleMessageRecipient {}, Party("MegaCorp", key.public.composite)) override val myInfo: NodeInfo get() = NodeInfo(object : SingleMessageRecipient {}, Party("MegaCorp", key.public.composite), MOCK_VERSION)
} }
@ThreadSafe @ThreadSafe
@ -83,11 +84,7 @@ class MockIdentityService(val identities: List<Party>) : IdentityService, Single
class MockKeyManagementService(vararg initialKeys: KeyPair) : SingletonSerializeAsToken(), KeyManagementService { class MockKeyManagementService(vararg initialKeys: KeyPair) : SingletonSerializeAsToken(), KeyManagementService {
override val keys: MutableMap<PublicKey, PrivateKey> override val keys: MutableMap<PublicKey, PrivateKey> = initialKeys.associateByTo(HashMap(), { it.public }, { it.private })
init {
keys = initialKeys.map { it.public to it.private }.toMap(HashMap())
}
val nextKeys = LinkedList<KeyPair>() val nextKeys = LinkedList<KeyPair>()

View File

@ -14,6 +14,7 @@ import net.corda.node.services.config.ConfigHelper
import net.corda.node.services.config.FullNodeConfiguration import net.corda.node.services.config.FullNodeConfiguration
import net.corda.node.services.transactions.RaftValidatingNotaryService import net.corda.node.services.transactions.RaftValidatingNotaryService
import net.corda.node.utilities.ServiceIdentityGenerator import net.corda.node.utilities.ServiceIdentityGenerator
import net.corda.testing.MOCK_NODE_VERSION_INFO
import net.corda.testing.freeLocalHostAndPort import net.corda.testing.freeLocalHostAndPort
import net.corda.testing.getFreeLocalPorts import net.corda.testing.getFreeLocalPorts
import org.junit.After import org.junit.After
@ -132,7 +133,7 @@ abstract class NodeBasedTest {
) + configOverrides ) + configOverrides
) )
val node = FullNodeConfiguration(baseDirectory, config).createNode() val node = FullNodeConfiguration(baseDirectory, config).createNode(MOCK_NODE_VERSION_INFO)
node.start() node.start()
nodes += node nodes += node
thread(name = legalName) { thread(name = legalName) {

View File

@ -5,6 +5,7 @@ import com.google.common.util.concurrent.SettableFuture
import net.corda.core.crypto.composite import net.corda.core.crypto.composite
import net.corda.core.crypto.generateKeyPair import net.corda.core.crypto.generateKeyPair
import net.corda.core.messaging.RPCOps import net.corda.core.messaging.RPCOps
import net.corda.testing.MOCK_NODE_VERSION_INFO
import net.corda.node.services.RPCUserServiceImpl import net.corda.node.services.RPCUserServiceImpl
import net.corda.node.services.config.NodeConfiguration import net.corda.node.services.config.NodeConfiguration
import net.corda.node.services.messaging.ArtemisMessagingServer import net.corda.node.services.messaging.ArtemisMessagingServer
@ -35,6 +36,7 @@ class SimpleNode(val config: NodeConfiguration, val address: HostAndPort = freeL
val net = databaseTransaction(database) { val net = databaseTransaction(database) {
NodeMessagingClient( NodeMessagingClient(
config, config,
MOCK_NODE_VERSION_INFO,
address, address,
identity.public.composite, identity.public.composite,
executor, executor,