From b5e022f350ef8a7b5d08ddcc0277020e0709cd8c Mon Sep 17 00:00:00 2001 From: Shams Asari Date: Wed, 19 Apr 2017 11:05:27 +0100 Subject: [PATCH] Introducing Platform Version and its use by the NMS for min version requirements for the network --- build.gradle | 16 ++++++- constants.properties | 2 +- .../net/corda/core/messaging/CordaRPCOps.kt | 6 +++ .../kotlin/net/corda/core/node/NodeInfo.kt | 2 +- .../kotlin/net/corda/core/node/Version.kt | 35 ---------------- .../kotlin/net/corda/core/node/VersionInfo.kt | 17 ++++++++ .../kotlin/net/corda/core/node/VersionTest.kt | 41 ------------------ docs/source/clientrpc.rst | 10 ++--- docs/source/corda-configuration-file.rst | 4 ++ docs/source/creating-a-cordapp.rst | 12 +++--- docs/source/index.rst | 1 + docs/source/release-notes.rst | 42 +++++++++++++------ docs/source/tutorial-cordapp.rst | 32 +++++++------- docs/source/versioning.rst | 29 +++++++++++++ .../main/groovy/net/corda/plugins/Node.groovy | 8 ++-- node/capsule/build.gradle | 10 +---- .../services/messaging/P2PSecurityTest.kt | 4 +- node/src/main/kotlin/net/corda/node/Corda.kt | 28 +++++++------ .../net/corda/node/internal/AbstractNode.kt | 6 +-- .../kotlin/net/corda/node/internal/Node.kt | 32 ++++---------- .../node/services/config/NodeConfiguration.kt | 8 ++-- .../services/messaging/NodeMessagingClient.kt | 24 ++++++----- .../services/network/NetworkMapService.kt | 31 ++++++++++---- .../network/PersistentNetworkMapService.kt | 4 +- .../messaging/ArtemisMessagingTests.kt | 4 +- .../PersistentNetworkMapServiceTest.kt | 5 ++- .../kotlin/net/corda/testing/CoreTestUtils.kt | 14 ++++--- .../corda/testing/node/MockNetworkMapCache.kt | 6 +-- .../kotlin/net/corda/testing/node/MockNode.kt | 17 +++++--- .../net/corda/testing/node/MockServices.kt | 4 +- .../net/corda/testing/node/NodeBasedTest.kt | 4 +- .../net/corda/testing/node/SimpleNode.kt | 4 +- tools/demobench/build.gradle | 5 +-- tools/explorer/capsule/build.gradle | 8 +--- webserver/webcapsule/build.gradle | 9 +--- 35 files changed, 247 insertions(+), 237 deletions(-) delete mode 100644 core/src/main/kotlin/net/corda/core/node/Version.kt create mode 100644 core/src/main/kotlin/net/corda/core/node/VersionInfo.kt delete mode 100644 core/src/test/kotlin/net/corda/core/node/VersionTest.kt create mode 100644 docs/source/versioning.rst diff --git a/build.gradle b/build.gradle index 2fa275185e..7a2b4c069d 100644 --- a/build.gradle +++ b/build.gradle @@ -4,7 +4,10 @@ buildscript { file("$projectDir/constants.properties").withInputStream { constants.load(it) } // Our version: bump this on release. - ext.corda_version = "0.11-SNAPSHOT" + ext.corda_release_version = "0.11-SNAPSHOT" + // Increment this on any release that changes public APIs anywhere in the Corda platform + // TODO This is going to be difficult until we have a clear separation throughout the code of what is public and what is internal + ext.corda_platform_version = 1 ext.gradle_plugins_version = constants.getProperty("gradlePluginsVersion") // Dependency versions. Can run 'gradle dependencyUpdates' to find new versions of things. @@ -105,8 +108,17 @@ allprojects { } } + tasks.withType(Jar) { // Includes War and Ear + manifest { + attributes('Corda-Release-Version': corda_release_version) + attributes('Corda-Platform-Version': corda_platform_version) + attributes('Corda-Revision': corda_revision) + attributes('Corda-Vendor': 'Corda Open Source') + } + } + group 'net.corda' - version "$corda_version" + version "$corda_release_version" repositories { mavenLocal() diff --git a/constants.properties b/constants.properties index df48624952..43a322729d 100644 --- a/constants.properties +++ b/constants.properties @@ -1,4 +1,4 @@ -gradlePluginsVersion=0.11.0 +gradlePluginsVersion=0.11.1 kotlinVersion=1.1.1 guavaVersion=21.0 typesafeConfigVersion=1.3.1 diff --git a/core/src/main/kotlin/net/corda/core/messaging/CordaRPCOps.kt b/core/src/main/kotlin/net/corda/core/messaging/CordaRPCOps.kt index 1e2c76297a..325c781c4c 100644 --- a/core/src/main/kotlin/net/corda/core/messaging/CordaRPCOps.kt +++ b/core/src/main/kotlin/net/corda/core/messaging/CordaRPCOps.kt @@ -50,6 +50,12 @@ sealed class StateMachineUpdate { // TODO: The use of Pairs throughout is unfriendly for Java interop. interface CordaRPCOps : RPCOps { + /** + * Returns the RPC protocol version, which is the same the node's Platform Version. Exists since version 1 so guaranteed + * to be present. + */ + override val protocolVersion: Int get() = nodeIdentity().platformVersion + /** * Returns a pair of currently in-progress state machine infos and an observable of future state machine adds/removes. */ diff --git a/core/src/main/kotlin/net/corda/core/node/NodeInfo.kt b/core/src/main/kotlin/net/corda/core/node/NodeInfo.kt index c86cf30bd4..7c54fc8647 100644 --- a/core/src/main/kotlin/net/corda/core/node/NodeInfo.kt +++ b/core/src/main/kotlin/net/corda/core/node/NodeInfo.kt @@ -19,7 +19,7 @@ data class ServiceEntry(val info: ServiceInfo, val identity: Party) @CordaSerializable data class NodeInfo(val address: SingleMessageRecipient, val legalIdentity: Party, - val version: Version, + val platformVersion: Int, var advertisedServices: List = emptyList(), val physicalLocation: PhysicalLocation? = null) { init { diff --git a/core/src/main/kotlin/net/corda/core/node/Version.kt b/core/src/main/kotlin/net/corda/core/node/Version.kt deleted file mode 100644 index 52e483bac2..0000000000 --- a/core/src/main/kotlin/net/corda/core/node/Version.kt +++ /dev/null @@ -1,35 +0,0 @@ -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 patch: Int?, val snapshot: Boolean) { - companion object { - private val pattern = Pattern.compile("""(\d+)\.(\d+)(.(\d+))?(-SNAPSHOT)?""") - - fun parse(string: String): Version { - val matcher = pattern.matcher(string) - require(matcher.matches()) - val patch = matcher.group(4)?.toInt() - return Version(matcher.group(1).toInt(), matcher.group(2).toInt(), patch, matcher.group(5) != null) - } - } - - override fun toString(): String { - val sb = StringBuilder() - sb.append(major, ".", minor) - if(patch != null) sb.append(".", patch) - if(snapshot) sb.append("-SNAPSHOT") - return sb.toString() - } -} - -data class NodeVersionInfo(val version: Version, val revision: String, val vendor: String) \ No newline at end of file diff --git a/core/src/main/kotlin/net/corda/core/node/VersionInfo.kt b/core/src/main/kotlin/net/corda/core/node/VersionInfo.kt new file mode 100644 index 0000000000..f072eafe04 --- /dev/null +++ b/core/src/main/kotlin/net/corda/core/node/VersionInfo.kt @@ -0,0 +1,17 @@ +package net.corda.core.node + +/** + * Encapsulates various pieces of version information of the node. + */ +data class VersionInfo( + /** + * Platform version of the node which is an integer value which increments on any release where any of the public + * API of the entire Corda platform changes. This includes messaging, serialisation, node APIs, etc. + */ + val platformVersion: Int, + /** Release version string of the node. */ + val releaseVersion: String, + /** The exact version control commit ID of the node build. */ + val revision: String, + /** The node vendor */ + val vendor: String) \ No newline at end of file diff --git a/core/src/test/kotlin/net/corda/core/node/VersionTest.kt b/core/src/test/kotlin/net/corda/core/node/VersionTest.kt deleted file mode 100644 index 8d5a77b338..0000000000 --- a/core/src/test/kotlin/net/corda/core/node/VersionTest.kt +++ /dev/null @@ -1,41 +0,0 @@ -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, null, false)) - } - - @Test - fun `parse valid SNAPSHOT string`() { - assertThat(Version.parse("2.23-SNAPSHOT")).isEqualTo(Version(2, 23, null, 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) - } - - @Test - fun `parses patch version`() { - assertThat(Version.parse("0.1.2")).isEqualTo(Version(0, 1, 2, false)) - } - - @Test - fun `parses snapshot patch version`() { - assertThat(Version.parse("0.1.2-SNAPSHOT")).isEqualTo(Version(0, 1, 2, true)) - } -} \ No newline at end of file diff --git a/docs/source/clientrpc.rst b/docs/source/clientrpc.rst index 0ab04505e6..1b1a44803e 100644 --- a/docs/source/clientrpc.rst +++ b/docs/source/clientrpc.rst @@ -57,11 +57,11 @@ the ``cancel`` method on the future will unsubscribe it from any future value an Versioning ---------- -The client RPC protocol is versioned with a simple incrementing integer. When a proxy is created the server is -queried for its protocol version, and you can specify your minimum requirement. Methods added in later versions -are tagged with the ``@RPCSinceVersion`` annotation. If you try to use a method that the server isn't advertising -support of, an ``UnsupportedOperationException`` is thrown. If you want to know the version of the server, just -use the ``protocolVersion`` property (i.e. ``getProtocolVersion`` in Java). +The client RPC protocol is versioned using the node's Platform Version (see :doc:`versioning`). When a proxy is created +the server is queried for its version, and you can specify your minimum requirement. Methods added in later versions +are tagged with the ``@RPCSinceVersion`` annotation. If you try to use a method that the server isn't advertising support +of, an ``UnsupportedOperationException`` is thrown. If you want to know the version of the server, just use the +``protocolVersion`` property (i.e. ``getProtocolVersion`` in Java). Thread safety ------------- diff --git a/docs/source/corda-configuration-file.rst b/docs/source/corda-configuration-file.rst index 846258f481..290e687bc4 100644 --- a/docs/source/corda-configuration-file.rst +++ b/docs/source/corda-configuration-file.rst @@ -116,6 +116,10 @@ path to the node's base directory. :legalName: Legal name of the node. This is required as part of the TLS host verification process. The node will reject the connection to the network map service if it provides a TLS common name which doesn't match with this value. +:minimumPlatformVersion: Used by the node if it's running the network map service to enforce a minimum version requirement + on registrations - any node on a Platform Version lower than this value will have their registration rejected. + Defaults to 1 if absent. + :useHTTPS: If false the node's web server will be plain HTTP. If true the node will use the same certificate and private key from the ``/certificates/sslkeystore.jks`` file as the ArtemisMQ port for HTTPS. If HTTPS is enabled then unencrypted HTTP traffic to the node's **webAddress** port is not supported. diff --git a/docs/source/creating-a-cordapp.rst b/docs/source/creating-a-cordapp.rst index 4e075db65a..439e00d91a 100644 --- a/docs/source/creating-a-cordapp.rst +++ b/docs/source/creating-a-cordapp.rst @@ -128,8 +128,8 @@ the following segments to the relevant part of your build.gradle. .. code-block:: groovy buildscript { - ext.corda_version = '' - ext.corda_gradle_plugins_version = '' // This is usually the same as corda_version. + ext.corda_release_version = '' + ext.corda_gradle_plugins_version = '' // This is usually the same as corda_release_version. ... your buildscript ... repositories { @@ -155,10 +155,10 @@ the following segments to the relevant part of your build.gradle. } dependencies { - compile "net.corda.core:$corda_version" - compile "net.corda.finance:$corda_version" - compile "net.corda.node:$corda_version" - compile "net.corda.corda:$corda_version" + compile "net.corda.core:$corda_release_version" + compile "net.corda.finance:$corda_release_version" + compile "net.corda.node:$corda_release_version" + compile "net.corda.corda:$corda_release_version" ... other dependencies here ... } diff --git a/docs/source/index.rst b/docs/source/index.rst index ec4f2bd5b5..dedf8b4f8f 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -72,6 +72,7 @@ Documentation Contents: :maxdepth: 2 :caption: The Corda node + versioning shell serialization clientrpc diff --git a/docs/source/release-notes.rst b/docs/source/release-notes.rst index 50377ff2bc..39cc682010 100644 --- a/docs/source/release-notes.rst +++ b/docs/source/release-notes.rst @@ -22,12 +22,21 @@ X.500 distinguished names (see RFC 1779 for details on the construction of disti * If you are using mock parties for testing, try to standardise on the ``DUMMY_NOTARY``, ``DUMMY_BANK_A``, etc. provided in order to ensure consistency. -We have updated DemoBench so that it is installed as "Corda DemoBench" for both Windows and MacOSX. The original version was installed as just "DemoBench", and so will not be overwritten automatically by the new version. +We have updated DemoBench so that it is installed as "Corda DemoBench" for both Windows and MacOSX. The original version +was installed as just "DemoBench", and so will not be overwritten automatically by the new version. + +We've introduced the concept of platform version which is a single integer value which increments by 1 if a release changes +any of the public APIs of the entire Corda platform. This includes the node's public APIs, the messaging protocol, +serialisation, etc. The node exposes the platform version it's on and we envision CorDapps will use this to be able to +run on older versions of the platform to the one they were compiled against. Platform version borrows heavily from Android's +API Level. Milestone 10 ------------ -Special thank you to `Qian Hong `_, `Marek Skocovsky `_, `Karel Hajek `_, and `Jonny Chiu `_ for their contributions to Corda in M10. +Special thank you to `Qian Hong `_, `Marek Skocovsky `_, +`Karel Hajek `_, and `Jonny Chiu `_ for their contributions +to Corda in M10. A new interactive **Corda Shell** has been added to the node. The shell lets developers and node administrators easily command the node by running flows, RPCs and SQL queries. It also provides a variety of commands to monitor @@ -35,25 +44,34 @@ the node. The Corda Shell is based on the popular `CRaSH project `_. **Using a Corda SNAPSHOT build.** Alternatively, if you wish to work from the master branch of the Corda repo which contains the most up-to-date Corda feature set then you will need to clone the ``corda`` repository and publish the latest master build (or previously tagged releases) to your local Maven repository. You will then need to ensure that Gradle -grabs the correct dependencies for you from Maven local by changing the ``corda_version`` in ``build.gradle``. This will be -covered below in `Using a SNAPSHOT release`_. +grabs the correct dependencies for you from Maven local by changing the ``corda_release_version`` in ``build.gradle``. +This will be covered below in `Using a SNAPSHOT release`_. Firstly, follow the :doc:`getting set up ` page to download the JDK, IntelliJ and git if you didn't already have it. @@ -145,10 +145,10 @@ if you are trying out new features, in this case you can change ``version`` for .. note:: **A quick point on corda version numbers used by Gradle.** - In the ``build.gradle`` file for your CorDapp, you can specify the ``corda_version`` to use. It is important that when - developing your CorDapp that you use the correct version number. For example, when wanting to work from a SNAPSHOT + In the ``build.gradle`` file for your CorDapp, you can specify the ``corda_release_version`` to use. It is important + that when developing your CorDapp that you use the correct version number. For example, when wanting to work from a SNAPSHOT release, the release numbers are suffixed with 'SNAPSHOT', e.g. if the latest milestone release is M6 then the - SNAPSHOT release will be 0.7-SNAPSHOT, and so on. As such, you will set your ``corda_version`` to ``'0.7-SNAPSHOT'`` + SNAPSHOT release will be 0.7-SNAPSHOT, and so on. As such, you will set your ``corda_release_version`` to ``'0.7-SNAPSHOT'`` in the ``build.gradle`` file in your CorDapp. Gradle will automatically grab the SNAPSHOT dependencies from your local Maven repository. Alternatively, if working from a milestone release, you will use the version number only, for example ``0.6`` or ``0.7``. @@ -224,7 +224,7 @@ see all available Gradle tasks. * For the Corda repo there will be many project listed, the root project ``corda`` and associated sub-projects: ``core``, ``finance``, ``node``, etc. -.. note:: It's worth noting that when you change branch in the example CorDapp, the ``corda_version`` will change to +.. note:: It's worth noting that when you change branch in the example CorDapp, the ``corda_release_version`` will change to reflect the version of the branch you are working from. To execute a task, double click it. The output will be shown in a console window. @@ -712,15 +712,15 @@ are available for use in the rest of the build script. It also specifies version things. If you are working from a Corda SNAPSHOT release which you have publish to Maven local then ensure that -``corda_version`` is the same as the version of the Corda core modules you published to Maven local. If not then change the -``kotlin_version`` property. Also, if you are working from a previous cordapp-tutorial milestone release, then be sure to ``git checkout`` -the correct version of the example CorDapp from the ``cordapp-tutorial`` repo. +``corda_release_version`` is the same as the version of the Corda core modules you published to Maven local. If not then +change the ``kotlin_version`` property. Also, if you are working from a previous cordapp-tutorial milestone release, then +be sure to ``git checkout`` the correct version of the example CorDapp from the ``cordapp-tutorial`` repo. .. sourcecode:: groovy buildscript { ext.kotlin_version = '1.0.4' - ext.corda_version = '0.5-SNAPSHOT' // Ensure this version is the same as the corda core modules you are using. + ext.corda_release_version = '0.5-SNAPSHOT' // Ensure this version is the same as the corda core modules you are using. ext.quasar_version = '0.7.6' ext.jersey_version = '2.23.1' @@ -747,12 +747,12 @@ code snippet.package. Use the standard format: testCompile group: 'junit', name: 'junit', version: '4.11' // Corda integration dependencies - compile "net.corda:client:$corda_version" - compile "net.corda:core:$corda_version" - compile "net.corda:contracts:$corda_version" - compile "net.corda:node:$corda_version" - compile "net.corda:corda:$corda_version" - compile "net.corda:test-utils:$corda_version" + compile "net.corda:client:$corda_release_version" + compile "net.corda:core:$corda_release_version" + compile "net.corda:contracts:$corda_release_version" + compile "net.corda:node:$corda_release_version" + compile "net.corda:corda:$corda_release_version" + compile "net.corda:test-utils:$corda_release_version" ... diff --git a/docs/source/versioning.rst b/docs/source/versioning.rst new file mode 100644 index 0000000000..ff502c0d97 --- /dev/null +++ b/docs/source/versioning.rst @@ -0,0 +1,29 @@ +Versioning +========== + +As the Corda platform evolves and new features are added it becomes important to have a versioning system which allows +its users to easily compare versions and know what feature are available to them. Each Corda release uses the standard +semantic versioning scheme of ``major.minor.patch``. This is useful when making releases in the public domain but is not +friendly for a developer working on the platform. It first has to be parsed and then they have three separate segments on +which to determine API differences. The release version is still useful and every MQ message the node sends attaches it +to the ``release-version`` header property for debugging purposes. + +Platform Version +---------------- + +It is much easier to use a single incrementing integer value to represent the API version of the Corda platform, which +is called the Platform Version. It is similar to Android's `API Level `_. +It starts at 1 and will increment by exactly 1 for each release which changes any of the publicly exposed APIs in the +entire platform. This includes public APIs on the node itself, the RPC system, messaging, serialisation, etc. API backwards +compatibility will always be maintained, with the use of deprecation to migrate away from old APIs. In rare situations +APIs may have to be removed, for example due to security issues. There is no relationship between the Platform Version +and the release version - a change in the major, minor or patch values may or may not increase the Platform Version. + +The Platform Version is part of the node's ``NodeInfo`` object, which is available from the ``ServiceHub``. This enables +a CorDapp to find out which version it's running on and determine whether a desired feature is available. When a node +registers with the Network Map Service it will use the node's Platform Version to enforce a minimum version requirement +for the network. + +.. note:: A future release may introduce the concept of a target platform version, which would be similar to Android's + ``targetSdkVersion``, and would provide a means of maintaining behavioural compatibility for the cases where the + platform's behaviour has changed. diff --git a/gradle-plugins/cordformation/src/main/groovy/net/corda/plugins/Node.groovy b/gradle-plugins/cordformation/src/main/groovy/net/corda/plugins/Node.groovy index 6f23f88ee0..4bbe02fabd 100644 --- a/gradle-plugins/cordformation/src/main/groovy/net/corda/plugins/Node.groovy +++ b/gradle-plugins/cordformation/src/main/groovy/net/corda/plugins/Node.groovy @@ -274,10 +274,10 @@ class Node { */ private File verifyAndGetCordaJar() { def maybeCordaJAR = project.configurations.runtime.filter { - it.toString().contains("corda-${project.corda_version}.jar") + it.toString().contains("corda-${project.corda_release_version}.jar") } if (maybeCordaJAR.size() == 0) { - throw new RuntimeException("No Corda Capsule JAR found. Have you deployed the Corda project to Maven? Looked for \"corda-${project.corda_version}.jar\"") + throw new RuntimeException("No Corda Capsule JAR found. Have you deployed the Corda project to Maven? Looked for \"corda-${project.corda_release_version}.jar\"") } else { def cordaJar = maybeCordaJAR.getSingleFile() assert(cordaJar.isFile()) @@ -292,10 +292,10 @@ class Node { */ private File verifyAndGetWebserverJar() { def maybeJar = project.configurations.runtime.filter { - it.toString().contains("corda-webserver-${project.corda_version}.jar") + it.toString().contains("corda-webserver-${project.corda_release_version}.jar") } if (maybeJar.size() == 0) { - throw new RuntimeException("No Corda Webserver JAR found. Have you deployed the Corda project to Maven? Looked for \"corda-webserver-${project.corda_version}.jar\"") + throw new RuntimeException("No Corda Webserver JAR found. Have you deployed the Corda project to Maven? Looked for \"corda-webserver-${project.corda_release_version}.jar\"") } else { def jar = maybeJar.getSingleFile() assert(jar.isFile()) diff --git a/node/capsule/build.gradle b/node/capsule/build.gradle index 62ea52efc8..f3d9a019ff 100644 --- a/node/capsule/build.gradle +++ b/node/capsule/build.gradle @@ -38,12 +38,12 @@ dependencies { task buildCordaJAR(type: FatCapsule) { applicationClass 'net.corda.node.Corda' - archiveName "corda-${corda_version}.jar" + archiveName "corda-${corda_release_version}.jar" applicationSource = files(project.tasks.findByName('jar'), '../build/classes/main/CordaCaplet.class', '../build/classes/main/CordaCaplet$1.class', 'config/dev/log4j2.xml') from 'NOTICE' // Copy CDDL notice capsuleManifest { - applicationVersion = corda_version + applicationVersion = corda_release_version appClassPath = ["jolokia-agent-war-${project.rootProject.ext.jolokia_version}.war"] javaAgents = ["quasar-core-${quasar_version}-jdk8.jar"] systemProperties['visualvm.display.name'] = 'Corda' @@ -63,12 +63,6 @@ task buildCordaJAR(type: FatCapsule) { // This lets you run the file like so: ./corda.jar // Other than being slightly less typing, this has one big advantage: Ctrl-C works properly in the terminal. reallyExecutable { trampolining() } - - manifest { - attributes('Corda-Version': corda_version) - attributes('Corda-Revision': corda_revision) - attributes('Corda-Vendor': 'Corda Open Source') - } } artifacts { diff --git a/node/src/integration-test/kotlin/net/corda/services/messaging/P2PSecurityTest.kt b/node/src/integration-test/kotlin/net/corda/services/messaging/P2PSecurityTest.kt index 93d83f4495..c44e697caf 100644 --- a/node/src/integration-test/kotlin/net/corda/services/messaging/P2PSecurityTest.kt +++ b/node/src/integration-test/kotlin/net/corda/services/messaging/P2PSecurityTest.kt @@ -15,7 +15,7 @@ import net.corda.node.services.network.NetworkMapService import net.corda.node.services.network.NetworkMapService.RegistrationRequest import net.corda.node.services.network.NodeRegistration import net.corda.node.utilities.AddOrRemove -import net.corda.testing.MOCK_VERSION +import net.corda.testing.MOCK_VERSION_INFO import net.corda.testing.TestNodeConfiguration import net.corda.testing.node.NodeBasedTest import net.corda.testing.node.SimpleNode @@ -63,7 +63,7 @@ class P2PSecurityTest : NodeBasedTest() { } private fun SimpleNode.registerWithNetworkMap(registrationName: String): ListenableFuture { - val nodeInfo = NodeInfo(net.myAddress, Party(registrationName, identity.public), MOCK_VERSION) + val nodeInfo = NodeInfo(net.myAddress, Party(registrationName, identity.public), MOCK_VERSION_INFO.platformVersion) val registration = NodeRegistration(nodeInfo, System.currentTimeMillis(), AddOrRemove.ADD, Instant.MAX) val request = RegistrationRequest(registration.toWire(identity.private), net.myAddress) return net.sendRequest(NetworkMapService.REGISTER_TOPIC, request, networkMapNode.net.myAddress) diff --git a/node/src/main/kotlin/net/corda/node/Corda.kt b/node/src/main/kotlin/net/corda/node/Corda.kt index 516037d46e..0cae920196 100644 --- a/node/src/main/kotlin/net/corda/node/Corda.kt +++ b/node/src/main/kotlin/net/corda/node/Corda.kt @@ -6,8 +6,7 @@ import com.jcabi.manifests.Manifests import com.typesafe.config.ConfigException import joptsimple.OptionException import net.corda.core.* -import net.corda.core.node.NodeVersionInfo -import net.corda.core.node.Version +import net.corda.core.node.VersionInfo import net.corda.core.utilities.Emoji import net.corda.core.utilities.LogHelper.withLevel import net.corda.node.internal.Node @@ -69,15 +68,17 @@ fun main(args: Array) { // Manifest properties are only available if running from the corda jar fun manifestValue(name: String): String? = if (Manifests.exists(name)) Manifests.read(name) else null - val nodeVersionInfo = NodeVersionInfo( - manifestValue("Corda-Version")?.let { Version.parse(it) } ?: Version(0, 0, 0, false), + val versionInfo = VersionInfo( + manifestValue("Corda-Platform-Version")?.toInt() ?: 1, + manifestValue("Corda-Release-Version") ?: "Unknown", manifestValue("Corda-Revision") ?: "Unknown", manifestValue("Corda-Vendor") ?: "Unknown" ) if (cmdlineOptions.isVersion) { - println("${nodeVersionInfo.vendor} ${nodeVersionInfo.version}") - println("Revision ${nodeVersionInfo.revision}") + println("${versionInfo.vendor} ${versionInfo.releaseVersion}") + println("Revision ${versionInfo.revision}") + println("Platform Version ${versionInfo.platformVersion}") exitProcess(0) } @@ -87,7 +88,7 @@ fun main(args: Array) { exitProcess(0) } - drawBanner(nodeVersionInfo) + drawBanner(versionInfo) val log = LoggerFactory.getLogger("Main") printBasicNodeInfo("Logs can be found in", System.getProperty("log-path")) @@ -110,9 +111,10 @@ fun main(args: Array) { exitProcess(0) } - log.info("Version: ${nodeVersionInfo.version}") - log.info("Vendor: ${nodeVersionInfo.vendor}") - log.info("Revision: ${nodeVersionInfo.revision}") + log.info("Vendor: ${versionInfo.vendor}") + log.info("Release: ${versionInfo.releaseVersion}") + log.info("Platform Version: ${versionInfo.platformVersion}") + log.info("Revision: ${versionInfo.revision}") val info = ManagementFactory.getRuntimeMXBean() log.info("PID: ${info.name.split("@").firstOrNull()}") // TODO Java 9 has better support for this log.info("Main class: ${FullNodeConfiguration::class.java.protectionDomain.codeSource.location.toURI().path}") @@ -132,7 +134,7 @@ fun main(args: Array) { try { cmdlineOptions.baseDirectory.createDirectories() - val node = conf.createNode(nodeVersionInfo) + val node = conf.createNode(versionInfo) node.start() printPluginsAndServices(node) @@ -236,7 +238,7 @@ private fun messageOfTheDay(): Pair { return Pair(a, b) } -private fun drawBanner(nodeVersionInfo: NodeVersionInfo) { +private fun drawBanner(versionInfo: VersionInfo) { // This line makes sure ANSI escapes work on Windows, where they aren't supported out of the box. AnsiConsole.systemInstall() @@ -249,7 +251,7 @@ private fun drawBanner(nodeVersionInfo: NodeVersionInfo) { / / __ / ___/ __ / __ `/ """).fgBrightBlue().a(msg1).newline().fgBrightRed().a( "/ /___ /_/ / / / /_/ / /_/ / ").fgBrightBlue().a(msg2).newline().fgBrightRed().a( """\____/ /_/ \__,_/\__,_/""").reset().newline().newline().fgBrightDefault().bold(). - a("--- ${nodeVersionInfo.vendor} ${nodeVersionInfo.version} (${nodeVersionInfo.revision.take(7)}) -----------------------------------------------"). + a("--- ${versionInfo.vendor} ${versionInfo.releaseVersion} (${versionInfo.revision.take(7)}) -----------------------------------------------"). newline(). newline(). a("${Emoji.books}New! ").reset().a("Training now available worldwide, see https://corda.net/corda-training/"). diff --git a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt index 792ab34a68..21d4ff0982 100644 --- a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt +++ b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt @@ -101,7 +101,7 @@ abstract class AbstractNode(open val configuration: NodeConfiguration, protected abstract val log: Logger protected abstract val networkMapAddress: SingleMessageRecipient? - protected abstract val version: Version + protected abstract val platformVersion: Int // 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. @@ -296,7 +296,7 @@ abstract class AbstractNode(open val configuration: NodeConfiguration, private fun makeInfo(): NodeInfo { val advertisedServiceEntries = makeServiceEntries() val legalIdentity = obtainLegalIdentity() - return NodeInfo(net.myAddress, legalIdentity, version, advertisedServiceEntries, findMyLocation()) + return NodeInfo(net.myAddress, legalIdentity, platformVersion, advertisedServiceEntries, findMyLocation()) } /** @@ -445,7 +445,7 @@ abstract class AbstractNode(open val configuration: NodeConfiguration, protected open fun makeKeyManagementService(): KeyManagementService = PersistentKeyManagementService(partyKeys) open protected fun makeNetworkMapService() { - inNodeNetworkMapService = PersistentNetworkMapService(services) + inNodeNetworkMapService = PersistentNetworkMapService(services, configuration.minimumPlatformVersion) } open protected fun makeNotaryService(type: ServiceType, tokenizableServices: MutableList): NotaryService { diff --git a/node/src/main/kotlin/net/corda/node/internal/Node.kt b/node/src/main/kotlin/net/corda/node/internal/Node.kt index 05f3fdaa34..7242ce1f08 100644 --- a/node/src/main/kotlin/net/corda/node/internal/Node.kt +++ b/node/src/main/kotlin/net/corda/node/internal/Node.kt @@ -5,14 +5,15 @@ import com.google.common.net.HostAndPort import com.google.common.util.concurrent.Futures import com.google.common.util.concurrent.ListenableFuture import com.google.common.util.concurrent.SettableFuture -import net.corda.core.* +import net.corda.core.div +import net.corda.core.flatMap import net.corda.core.messaging.RPCOps -import net.corda.core.node.NodeVersionInfo import net.corda.core.node.ServiceHub -import net.corda.core.node.Version +import net.corda.core.node.VersionInfo import net.corda.core.node.services.ServiceInfo import net.corda.core.node.services.ServiceType import net.corda.core.node.services.UniquenessProvider +import net.corda.core.success import net.corda.core.utilities.loggerFor import net.corda.node.printBasicNodeInfo import net.corda.node.serialization.NodeClock @@ -48,14 +49,14 @@ import kotlin.concurrent.thread */ class Node(override val configuration: FullNodeConfiguration, advertisedServices: Set, - val nodeVersionInfo: NodeVersionInfo, + val versionInfo: VersionInfo, clock: Clock = NodeClock()) : AbstractNode(configuration, advertisedServices, clock) { companion object { private val logger = loggerFor() } override val log: Logger get() = logger - override val version: Version get() = nodeVersionInfo.version + override val platformVersion: Int get() = versionInfo.platformVersion override val networkMapAddress: NetworkMapAddress? get() = configuration.networkMapService?.address?.let(::NetworkMapAddress) override fun makeTransactionVerifierService() = (net as NodeMessagingClient).verifierService @@ -108,32 +109,13 @@ class Node(override val configuration: FullNodeConfiguration, private lateinit var userService: RPCUserService - init { - checkVersionUnchanged() - } - - /** - * Abort starting the node if an existing deployment with a different version is detected in the base directory. - */ - private fun checkVersionUnchanged() { - val versionFile = configuration.baseDirectory / "version" - if (versionFile.exists()) { - val previousVersion = Version.parse(versionFile.readAllLines()[0]) - check(nodeVersionInfo.version.major == previousVersion.major) { - "Major version change detected - current: ${nodeVersionInfo.version}, previous: $previousVersion. " + - "Node upgrades across major versions are not yet supported." - } - } - versionFile.writeLines(listOf(nodeVersionInfo.version.toString())) - } - override fun makeMessagingService(): MessagingServiceInternal { userService = RPCUserServiceImpl(configuration.rpcUsers) val serverAddress = configuration.messagingServerAddress ?: makeLocalMessageBroker() val myIdentityOrNullIfNetworkMapService = if (networkMapAddress != null) obtainLegalIdentity().owningKey else null return NodeMessagingClient( configuration, - nodeVersionInfo, + versionInfo, serverAddress, myIdentityOrNullIfNetworkMapService, serverThread, diff --git a/node/src/main/kotlin/net/corda/node/services/config/NodeConfiguration.kt b/node/src/main/kotlin/net/corda/node/services/config/NodeConfiguration.kt index 31ade8ea7a..dae48ff4ec 100644 --- a/node/src/main/kotlin/net/corda/node/services/config/NodeConfiguration.kt +++ b/node/src/main/kotlin/net/corda/node/services/config/NodeConfiguration.kt @@ -2,7 +2,7 @@ package net.corda.node.services.config import com.google.common.net.HostAndPort import net.corda.core.div -import net.corda.core.node.NodeVersionInfo +import net.corda.core.node.VersionInfo import net.corda.core.node.services.ServiceInfo import net.corda.node.internal.NetworkMapInfo import net.corda.node.internal.Node @@ -22,6 +22,7 @@ interface NodeConfiguration : SSLConfiguration { override val certificatesDirectory: Path get() = baseDirectory / "certificates" val myLegalName: String val networkMapService: NetworkMapInfo? + val minimumPlatformVersion: Int val nearestCity: String val emailAddress: String val exportJMXto: String @@ -47,6 +48,7 @@ data class FullNodeConfiguration( override val dataSourceProperties: Properties, override val certificateSigningService: URL, override val networkMapService: NetworkMapInfo?, + override val minimumPlatformVersion: Int = 1, override val rpcUsers: List, override val verifierType: VerifierType, val useHTTPS: Boolean, @@ -78,14 +80,14 @@ data class FullNodeConfiguration( } } - fun createNode(nodeVersionInfo: NodeVersionInfo): Node { + fun createNode(versionInfo: VersionInfo): Node { val advertisedServices = extraAdvertisedServiceIds .filter(String::isNotBlank) .map { ServiceInfo.parse(it) } .toMutableSet() if (networkMapService == null) advertisedServices += ServiceInfo(NetworkMapService.type) - return Node(this, advertisedServices, nodeVersionInfo, if (useTestClock) TestClock() else NodeClock()) + return Node(this, advertisedServices, versionInfo, if (useTestClock) TestClock() else NodeClock()) } } diff --git a/node/src/main/kotlin/net/corda/node/services/messaging/NodeMessagingClient.kt b/node/src/main/kotlin/net/corda/node/services/messaging/NodeMessagingClient.kt index a59cb484ed..514e13f451 100644 --- a/node/src/main/kotlin/net/corda/node/services/messaging/NodeMessagingClient.kt +++ b/node/src/main/kotlin/net/corda/node/services/messaging/NodeMessagingClient.kt @@ -4,7 +4,7 @@ import com.google.common.net.HostAndPort import com.google.common.util.concurrent.ListenableFuture import net.corda.core.ThreadBox import net.corda.core.messaging.* -import net.corda.core.node.NodeVersionInfo +import net.corda.core.node.VersionInfo import net.corda.core.node.services.PartyInfo import net.corda.core.node.services.TransactionVerifierService import net.corda.core.random63BitValue @@ -68,7 +68,7 @@ import java.security.PublicKey */ @ThreadSafe class NodeMessagingClient(override val config: NodeConfiguration, - nodeVersionInfo: NodeVersionInfo, + val versionInfo: VersionInfo, val serverHostPort: HostAndPort, val myIdentity: PublicKey?, val nodeExecutor: AffinityExecutor, @@ -85,9 +85,10 @@ class NodeMessagingClient(override val config: NodeConfiguration, // confusion. private val topicProperty = SimpleString("platform-topic") private val sessionIdProperty = SimpleString("session-id") - 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 val cordaVendorProperty = SimpleString("corda-vendor") + private val releaseVersionProperty = SimpleString("release-version") + private val platformVersionProperty = SimpleString("platform-version") + private val amqDelayMillis = System.getProperty("amq.delivery.delay.ms", "0").toInt() private val verifierResponseAddress = "$VERIFICATION_RESPONSES_QUEUE_NAME_PREFIX.${random63BitValue()}" } @@ -114,8 +115,8 @@ class NodeMessagingClient(override val config: NodeConfiguration, data class Handler(val topicSession: TopicSession, val callback: (ReceivedMessage, MessageHandlerRegistration) -> Unit) : MessageHandlerRegistration - private val nodeVendor = SimpleString(nodeVersionInfo.vendor) - private val version = SimpleString(nodeVersionInfo.version.toString()) + private val cordaVendor = SimpleString(versionInfo.vendor) + private val releaseVersion = SimpleString(versionInfo.releaseVersion) /** An executor for sending messages */ private val messagingExecutor = AffinityExecutor.ServiceAffinityExecutor("Messaging", 1) @@ -409,8 +410,9 @@ class NodeMessagingClient(override val config: NodeConfiguration, state.locked { val mqAddress = getMQAddress(target) val artemisMessage = session!!.createMessage(true).apply { - putStringProperty(nodeVendorProperty, nodeVendor) - putStringProperty(nodeVersionProperty, version) + putStringProperty(cordaVendorProperty, cordaVendor) + putStringProperty(releaseVersionProperty, releaseVersion) + putIntProperty(platformVersionProperty, versionInfo.platformVersion) putStringProperty(topicProperty, SimpleString(message.topicSession.topic)) putLongProperty(sessionIdProperty, message.topicSession.sessionID) writeBodyBufferBytes(message.data) @@ -418,8 +420,8 @@ class NodeMessagingClient(override val config: NodeConfiguration, 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 - if (amqDelay > 0 && message.topicSession.topic == StateMachineManager.sessionTopic.topic) { - putLongProperty(HDR_SCHEDULED_DELIVERY_TIME, System.currentTimeMillis() + amqDelay) + if (amqDelayMillis > 0 && message.topicSession.topic == StateMachineManager.sessionTopic.topic) { + putLongProperty(HDR_SCHEDULED_DELIVERY_TIME, System.currentTimeMillis() + amqDelayMillis) } } log.trace { diff --git a/node/src/main/kotlin/net/corda/node/services/network/NetworkMapService.kt b/node/src/main/kotlin/net/corda/node/services/network/NetworkMapService.kt index 041fd4a8b1..ed15e45ef4 100644 --- a/node/src/main/kotlin/net/corda/node/services/network/NetworkMapService.kt +++ b/node/src/main/kotlin/net/corda/node/services/network/NetworkMapService.kt @@ -109,7 +109,8 @@ interface NetworkMapService { } @ThreadSafe -class InMemoryNetworkMapService(services: ServiceHubInternal) : AbstractNetworkMapService(services) { +class InMemoryNetworkMapService(services: ServiceHubInternal, minimumPlatformVersion: Int) + : AbstractNetworkMapService(services, minimumPlatformVersion) { override val nodeRegistrations: MutableMap = ConcurrentHashMap() override val subscribers = ThreadBox(mutableMapOf()) @@ -126,7 +127,8 @@ class InMemoryNetworkMapService(services: ServiceHubInternal) : AbstractNetworkM * subscriber clean up and is simpler to persist than the previous implementation based on a set of missing messages acks. */ @ThreadSafe -abstract class AbstractNetworkMapService(services: ServiceHubInternal) : NetworkMapService, AbstractNodeService(services) { +abstract class AbstractNetworkMapService(services: ServiceHubInternal, + val minimumPlatformVersion: Int) : NetworkMapService, AbstractNodeService(services) { companion object { /** * Maximum credible size for a registration request. Generally requests are around 500-600 bytes, so this gives a @@ -152,6 +154,13 @@ abstract class AbstractNetworkMapService(services: ServiceHubInternal) : Network private val handlers = ArrayList() + init { + require(minimumPlatformVersion >= 1) { "minimumPlatformVersion cannot be less than 1" } + require(minimumPlatformVersion <= services.myInfo.platformVersion) { + "minimumPlatformVersion cannot be greater than the node's own version" + } + } + protected fun setup() { // Register message handlers handlers += addMessageHandler(FETCH_TOPIC) { req: FetchMapRequest -> processFetchAllRequest(req) } @@ -219,21 +228,27 @@ abstract class AbstractNetworkMapService(services: ServiceHubInternal) : Network } private fun processRegistrationRequest(request: RegistrationRequest): RegistrationResponse { - if (request.wireReg.raw.size > MAX_SIZE_REGISTRATION_REQUEST_BYTES) return RegistrationResponse("Request is too big") + if (request.wireReg.raw.size > MAX_SIZE_REGISTRATION_REQUEST_BYTES) { + return RegistrationResponse("Request is too big") + } val change = try { request.wireReg.verified() - } catch(e: SignatureException) { + } catch (e: SignatureException) { return RegistrationResponse("Invalid signature on request") } val node = change.node + if (node.platformVersion < minimumPlatformVersion) { + return RegistrationResponse("Minimum platform version requirement not met: $minimumPlatformVersion") + } + // Update the current value atomically, so that if multiple updates come // in on different threads, there is no risk of a race condition while checking // sequence numbers. val registrationInfo = try { - nodeRegistrations.compute(node.legalIdentity) { _: Party, existing: NodeRegistrationInfo? -> + nodeRegistrations.compute(node.legalIdentity) { _, existing: NodeRegistrationInfo? -> require(!((existing == null || existing.reg.type == REMOVE) && change.type == REMOVE)) { "Attempting to de-register unknown node" } @@ -263,16 +278,16 @@ abstract class AbstractNetworkMapService(services: ServiceHubInternal) : Network return RegistrationResponse(null) } - private fun notifySubscribers(wireReg: WireNodeRegistration, mapVersion: Int) { + private fun notifySubscribers(wireReg: WireNodeRegistration, newMapVersion: Int) { // TODO: Once we have a better established messaging system, we can probably send // to a MessageRecipientGroup that nodes join/leave, rather than the network map // service itself managing the group - val update = NetworkMapService.Update(wireReg, mapVersion, net.myAddress).serialize().bytes + val update = NetworkMapService.Update(wireReg, newMapVersion, net.myAddress).serialize().bytes val message = net.createMessage(PUSH_TOPIC, DEFAULT_SESSION_ID, update) subscribers.locked { // Remove any stale subscribers - values.removeIf { lastAckInfo -> mapVersion - lastAckInfo.mapVersion > maxUnacknowledgedUpdates } + values.removeIf { (mapVersion) -> newMapVersion - mapVersion > maxUnacknowledgedUpdates } // TODO: introduce some concept of time in the condition to avoid unsubscribes when there's a message burst. keys.forEach { recipient -> net.send(message, recipient) } } diff --git a/node/src/main/kotlin/net/corda/node/services/network/PersistentNetworkMapService.kt b/node/src/main/kotlin/net/corda/node/services/network/PersistentNetworkMapService.kt index ccdd5dbebf..cf5f834b6e 100644 --- a/node/src/main/kotlin/net/corda/node/services/network/PersistentNetworkMapService.kt +++ b/node/src/main/kotlin/net/corda/node/services/network/PersistentNetworkMapService.kt @@ -17,7 +17,9 @@ import java.util.Collections.synchronizedMap * This class needs database transactions to be in-flight during method calls and init, otherwise it will throw * exceptions. */ -class PersistentNetworkMapService(services: ServiceHubInternal) : AbstractNetworkMapService(services) { +class PersistentNetworkMapService(services: ServiceHubInternal, minimumPlatformVersion: Int) + : AbstractNetworkMapService(services, minimumPlatformVersion) { + private object Table : JDBCHashedTable("${NODE_DATABASE_PREFIX}network_map_nodes") { val nodeParty = party("node_party_name", "node_party_key") val registrationInfo = blob("node_registration_info") diff --git a/node/src/test/kotlin/net/corda/node/services/messaging/ArtemisMessagingTests.kt b/node/src/test/kotlin/net/corda/node/services/messaging/ArtemisMessagingTests.kt index b4c24d7894..0e95ba7337 100644 --- a/node/src/test/kotlin/net/corda/node/services/messaging/ArtemisMessagingTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/messaging/ArtemisMessagingTests.kt @@ -25,7 +25,7 @@ import net.corda.node.services.transactions.PersistentUniquenessProvider import net.corda.node.utilities.AffinityExecutor.ServiceAffinityExecutor import net.corda.node.utilities.configureDatabase import net.corda.node.utilities.transaction -import net.corda.testing.MOCK_NODE_VERSION_INFO +import net.corda.testing.MOCK_VERSION_INFO import net.corda.testing.TestNodeConfiguration import net.corda.testing.freeLocalHostAndPort import net.corda.testing.node.makeTestDataSourceProperties @@ -225,7 +225,7 @@ class ArtemisMessagingTests { return database.transaction { NodeMessagingClient( config, - MOCK_NODE_VERSION_INFO, + MOCK_VERSION_INFO, server, identity.public, ServiceAffinityExecutor("ArtemisMessagingTests", 1), diff --git a/node/src/test/kotlin/net/corda/node/services/network/PersistentNetworkMapServiceTest.kt b/node/src/test/kotlin/net/corda/node/services/network/PersistentNetworkMapServiceTest.kt index 246d199ee2..2a3a8fd3b2 100644 --- a/node/src/test/kotlin/net/corda/node/services/network/PersistentNetworkMapServiceTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/network/PersistentNetworkMapServiceTest.kt @@ -5,6 +5,7 @@ import net.corda.core.node.services.ServiceInfo import net.corda.node.services.api.ServiceHubInternal import net.corda.node.services.config.NodeConfiguration import net.corda.node.utilities.transaction +import net.corda.testing.MOCK_VERSION_INFO import net.corda.testing.node.MockNetwork import net.corda.testing.node.MockNetwork.MockNode import java.math.BigInteger @@ -48,11 +49,11 @@ class PersistentNetworkMapServiceTest : AbstractNetworkMapServiceTest 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_VERSION = Version(0, 0, 0, false) -val MOCK_NODE_VERSION_INFO = NodeVersionInfo(MOCK_VERSION, "Mock revision", "Mock Vendor") +val MOCK_VERSION_INFO = VersionInfo(1, "Mock release", "Mock revision", "Mock Vendor") fun generateStateRef() = StateRef(SecureHash.randomSHA256(), 0) @@ -156,6 +156,7 @@ data class TestNodeConfiguration( override val baseDirectory: Path, override val myLegalName: String, override val networkMapService: NetworkMapInfo?, + override val minimumPlatformVersion: Int = 1, override val keyStorePassword: String = "cordacadevpass", override val trustStorePassword: String = "trustpass", override val rpcUsers: List = emptyList(), @@ -166,7 +167,8 @@ data class TestNodeConfiguration( override val devMode: Boolean = true, override val certificateSigningService: URL = URL("http://localhost"), override val certificateChainCheckPolicies: List = emptyList(), - override val verifierType: VerifierType = VerifierType.InMemory) : NodeConfiguration + override val verifierType: VerifierType = VerifierType.InMemory) : NodeConfiguration { +} fun testConfiguration(baseDirectory: Path, legalName: String, basePort: Int): FullNodeConfiguration { return FullNodeConfiguration( diff --git a/test-utils/src/main/kotlin/net/corda/testing/node/MockNetworkMapCache.kt b/test-utils/src/main/kotlin/net/corda/testing/node/MockNetworkMapCache.kt index a46e3b052d..c23e491eba 100644 --- a/test-utils/src/main/kotlin/net/corda/testing/node/MockNetworkMapCache.kt +++ b/test-utils/src/main/kotlin/net/corda/testing/node/MockNetworkMapCache.kt @@ -7,7 +7,7 @@ import net.corda.core.messaging.SingleMessageRecipient import net.corda.core.node.NodeInfo import net.corda.core.node.services.NetworkMapCache import net.corda.node.services.network.InMemoryNetworkMapCache -import net.corda.testing.MOCK_VERSION +import net.corda.testing.MOCK_VERSION_INFO import rx.Observable import rx.subjects.PublishSubject @@ -20,8 +20,8 @@ class MockNetworkMapCache : InMemoryNetworkMapCache() { data class MockAddress(val id: String) : SingleMessageRecipient init { - 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")), MOCK_VERSION) + val mockNodeA = NodeInfo(MockAddress("bankC:8080"), Party("Bank C", DummyPublicKey("Bank C")), MOCK_VERSION_INFO.platformVersion) + val mockNodeB = NodeInfo(MockAddress("bankD:8080"), Party("Bank D", DummyPublicKey("Bank D")), MOCK_VERSION_INFO.platformVersion) registeredNodes[mockNodeA.legalIdentity.owningKey] = mockNodeA registeredNodes[mockNodeB.legalIdentity.owningKey] = mockNodeB runWithoutMapService() diff --git a/test-utils/src/main/kotlin/net/corda/testing/node/MockNode.kt b/test-utils/src/main/kotlin/net/corda/testing/node/MockNode.kt index 11676456b1..75e1dff548 100644 --- a/test-utils/src/main/kotlin/net/corda/testing/node/MockNode.kt +++ b/test-utils/src/main/kotlin/net/corda/testing/node/MockNode.kt @@ -12,7 +12,6 @@ import net.corda.core.messaging.SingleMessageRecipient import net.corda.core.node.CordaPluginRegistry import net.corda.core.node.PhysicalLocation import net.corda.core.node.ServiceEntry -import net.corda.core.node.Version import net.corda.core.node.services.* import net.corda.core.utilities.DUMMY_NOTARY_KEY import net.corda.core.utilities.loggerFor @@ -29,7 +28,7 @@ import net.corda.node.services.transactions.ValidatingNotaryService import net.corda.node.services.vault.NodeVaultService import net.corda.node.utilities.AffinityExecutor import net.corda.node.utilities.AffinityExecutor.ServiceAffinityExecutor -import net.corda.testing.MOCK_VERSION +import net.corda.testing.MOCK_VERSION_INFO import net.corda.testing.TestNodeConfiguration import org.apache.activemq.artemis.utils.ReusableLatch import org.slf4j.Logger @@ -136,7 +135,7 @@ class MockNetwork(private val networkSendManuallyPumped: Boolean = false, AbstractNode(config, advertisedServices, TestClock(), mockNet.busyLatch) { var counter = entropyRoot override val log: Logger = loggerFor() - override val version: Version get() = MOCK_VERSION + override val platformVersion: Int get() = MOCK_VERSION_INFO.platformVersion override val serverThread: AffinityExecutor = if (mockNet.threadPerNode) ServiceAffinityExecutor("Mock node $id thread", 1) @@ -149,7 +148,15 @@ class MockNetwork(private val networkSendManuallyPumped: Boolean = false, // through the java.nio API which we are already mocking via Jimfs. override fun makeMessagingService(): MessagingServiceInternal { require(id >= 0) { "Node ID must be zero or positive, was passed: " + id } - return mockNet.messagingNetwork.createNodeWithID(!mockNet.threadPerNode, id, serverThread, makeServiceEntries(), configuration.myLegalName, database).start().getOrThrow() + return mockNet.messagingNetwork.createNodeWithID( + !mockNet.threadPerNode, + id, + serverThread, + makeServiceEntries(), + configuration.myLegalName, + database) + .start() + .getOrThrow() } override fun makeIdentityService() = MockIdentityService(mockNet.identities) @@ -165,7 +172,7 @@ class MockNetwork(private val networkSendManuallyPumped: Boolean = false, } override fun makeNetworkMapService() { - inNodeNetworkMapService = InMemoryNetworkMapService(services) + inNodeNetworkMapService = InMemoryNetworkMapService(services, platformVersion) } override fun makeServiceEntries(): List { diff --git a/test-utils/src/main/kotlin/net/corda/testing/node/MockServices.kt b/test-utils/src/main/kotlin/net/corda/testing/node/MockServices.kt index a33a205ca6..e7f84d35d0 100644 --- a/test-utils/src/main/kotlin/net/corda/testing/node/MockServices.kt +++ b/test-utils/src/main/kotlin/net/corda/testing/node/MockServices.kt @@ -21,7 +21,7 @@ import net.corda.node.services.transactions.InMemoryTransactionVerifierService import net.corda.node.services.vault.NodeVaultService import net.corda.testing.MEGA_CORP import net.corda.testing.MINI_CORP -import net.corda.testing.MOCK_VERSION +import net.corda.testing.MOCK_VERSION_INFO import org.bouncycastle.asn1.x500.X500Name import rx.Observable import rx.subjects.PublishSubject @@ -68,7 +68,7 @@ open class MockServices(val key: KeyPair = generateKeyPair()) : ServiceHub { override val networkMapCache: NetworkMapCache get() = throw UnsupportedOperationException() override val clock: Clock get() = Clock.systemUTC() override val schedulerService: SchedulerService get() = throw UnsupportedOperationException() - override val myInfo: NodeInfo get() = NodeInfo(object : SingleMessageRecipient {}, Party("MegaCorp", key.public), MOCK_VERSION) + override val myInfo: NodeInfo get() = NodeInfo(object : SingleMessageRecipient {}, Party("MegaCorp", key.public), MOCK_VERSION_INFO.platformVersion) override val transactionVerifierService: TransactionVerifierService get() = InMemoryTransactionVerifierService(2) fun makeVaultService(dataSourceProps: Properties): VaultService { diff --git a/test-utils/src/main/kotlin/net/corda/testing/node/NodeBasedTest.kt b/test-utils/src/main/kotlin/net/corda/testing/node/NodeBasedTest.kt index 4d1b4a0d2f..fdac833c6b 100644 --- a/test-utils/src/main/kotlin/net/corda/testing/node/NodeBasedTest.kt +++ b/test-utils/src/main/kotlin/net/corda/testing/node/NodeBasedTest.kt @@ -16,7 +16,7 @@ import net.corda.node.services.transactions.RaftValidatingNotaryService import net.corda.node.utilities.ServiceIdentityGenerator import net.corda.nodeapi.User import net.corda.nodeapi.config.parseAs -import net.corda.testing.MOCK_NODE_VERSION_INFO +import net.corda.testing.MOCK_VERSION_INFO import net.corda.testing.getFreeLocalPorts import org.apache.logging.log4j.Level import org.junit.After @@ -141,7 +141,7 @@ abstract class NodeBasedTest { ) + configOverrides ) - val node = config.parseAs().createNode(MOCK_NODE_VERSION_INFO) + val node = config.parseAs().createNode(MOCK_VERSION_INFO) node.start() nodes += node thread(name = legalName) { diff --git a/test-utils/src/main/kotlin/net/corda/testing/node/SimpleNode.kt b/test-utils/src/main/kotlin/net/corda/testing/node/SimpleNode.kt index 66a29d52fc..6e3fa38534 100644 --- a/test-utils/src/main/kotlin/net/corda/testing/node/SimpleNode.kt +++ b/test-utils/src/main/kotlin/net/corda/testing/node/SimpleNode.kt @@ -5,6 +5,7 @@ import com.google.common.net.HostAndPort import com.google.common.util.concurrent.SettableFuture import net.corda.core.crypto.generateKeyPair import net.corda.core.messaging.RPCOps +import net.corda.testing.MOCK_VERSION_INFO import net.corda.node.services.RPCUserServiceImpl import net.corda.node.services.api.MonitoringService import net.corda.node.services.config.NodeConfiguration @@ -13,7 +14,6 @@ import net.corda.node.services.messaging.NodeMessagingClient import net.corda.node.services.network.InMemoryNetworkMapCache import net.corda.node.utilities.AffinityExecutor.ServiceAffinityExecutor import net.corda.node.utilities.configureDatabase -import net.corda.testing.MOCK_NODE_VERSION_INFO import net.corda.node.utilities.transaction import net.corda.testing.freeLocalHostAndPort import org.jetbrains.exposed.sql.Database @@ -38,7 +38,7 @@ class SimpleNode(val config: NodeConfiguration, val address: HostAndPort = freeL val net = database.transaction { NodeMessagingClient( config, - MOCK_NODE_VERSION_INFO, + MOCK_VERSION_INFO, address, identity.public, executor, diff --git a/tools/demobench/build.gradle b/tools/demobench/build.gradle index f180b12b85..969d762e88 100644 --- a/tools/demobench/build.gradle +++ b/tools/demobench/build.gradle @@ -80,9 +80,8 @@ dependencies { jar { manifest { attributes( - 'Corda-Version': corda_version, - 'Main-Class': mainClassName, - 'Class-Path': configurations.compile.collect { it.getName() }.join(' ') + 'Main-Class': mainClassName, + 'Class-Path': configurations.compile.collect { it.getName() }.join(' ') ) } } diff --git a/tools/explorer/capsule/build.gradle b/tools/explorer/capsule/build.gradle index a855e9af5a..29e85a49c8 100644 --- a/tools/explorer/capsule/build.gradle +++ b/tools/explorer/capsule/build.gradle @@ -31,12 +31,12 @@ dependencies { task buildExplorerJAR(type: FatCapsule) { applicationClass 'net.corda.explorer.Main' - archiveName "node-explorer-${corda_version}.jar" + archiveName "node-explorer-${corda_release_version}.jar" applicationSource = files(project.tasks.findByName('jar'), '../build/classes/main/ExplorerCaplet.class') classifier 'fat' capsuleManifest { - applicationVersion = corda_version + applicationVersion = corda_release_version systemProperties['visualvm.display.name'] = 'Node Explorer' minJavaVersion = '1.8.0' minUpdateVersion['1.8'] = java8_minUpdateVersion @@ -49,10 +49,6 @@ task buildExplorerJAR(type: FatCapsule) { // If you change these flags, please also update Driver.kt jvmArgs = ['-Xmx200m', '-XX:+UseG1GC'] } - - manifest { - attributes('Corda-Version': corda_version) - } } build.dependsOn buildExplorerJAR diff --git a/webserver/webcapsule/build.gradle b/webserver/webcapsule/build.gradle index d046076a73..17d711b3a4 100644 --- a/webserver/webcapsule/build.gradle +++ b/webserver/webcapsule/build.gradle @@ -38,7 +38,7 @@ dependencies { task buildWebserverJar(type: FatCapsule) { applicationClass 'net.corda.webserver.WebServer' - archiveName "corda-webserver-${corda_version}.jar" + archiveName "corda-webserver-${corda_release_version}.jar" applicationSource = files( project.tasks.findByName('jar'), new File(project(':node').rootDir, 'node/build/classes/main/CordaCaplet.class'), @@ -48,10 +48,9 @@ task buildWebserverJar(type: FatCapsule) { from 'NOTICE' // Copy CDDL notice capsuleManifest { - applicationVersion = corda_version + applicationVersion = corda_release_version javaAgents = ["quasar-core-${quasar_version}-jdk8.jar"] systemProperties['visualvm.display.name'] = 'Corda Webserver' - systemProperties['corda.version'] = corda_version minJavaVersion = '1.8.0' minUpdateVersion['1.8'] = java8_minUpdateVersion caplets = ['CordaCaplet'] @@ -63,10 +62,6 @@ task buildWebserverJar(type: FatCapsule) { // If you change these flags, please also update Driver.kt jvmArgs = ['-Xmx200m', '-XX:+UseG1GC'] } - - manifest { - attributes('Corda-Version': corda_version) - } } artifacts {