mirror of
https://github.com/corda/corda.git
synced 2024-12-22 06:17:55 +00:00
Merge pull request #552 from corda/shams-nms-version-check
Introducing Platform Version and its use by the NMS for min version r…
This commit is contained in:
commit
af7f5ef0d7
16
build.gradle
16
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()
|
||||
|
@ -1,4 +1,4 @@
|
||||
gradlePluginsVersion=0.11.0
|
||||
gradlePluginsVersion=0.11.1
|
||||
kotlinVersion=1.1.1
|
||||
guavaVersion=21.0
|
||||
typesafeConfigVersion=1.3.1
|
||||
|
@ -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.
|
||||
*/
|
||||
|
@ -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<ServiceEntry> = emptyList(),
|
||||
val physicalLocation: PhysicalLocation? = null) {
|
||||
init {
|
||||
|
@ -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)
|
17
core/src/main/kotlin/net/corda/core/node/VersionInfo.kt
Normal file
17
core/src/main/kotlin/net/corda/core/node/VersionInfo.kt
Normal file
@ -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)
|
@ -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))
|
||||
}
|
||||
}
|
@ -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
|
||||
-------------
|
||||
|
@ -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 ``<workspace>/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.
|
||||
|
@ -128,8 +128,8 @@ the following segments to the relevant part of your build.gradle.
|
||||
.. code-block:: groovy
|
||||
|
||||
buildscript {
|
||||
ext.corda_version = '<enter the corda version you build against here>'
|
||||
ext.corda_gradle_plugins_version = '<enter the gradle plugins version here>' // This is usually the same as corda_version.
|
||||
ext.corda_release_version = '<enter the corda version you build against here>'
|
||||
ext.corda_gradle_plugins_version = '<enter the gradle plugins version here>' // 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 ...
|
||||
}
|
||||
|
||||
|
@ -72,6 +72,7 @@ Documentation Contents:
|
||||
:maxdepth: 2
|
||||
:caption: The Corda node
|
||||
|
||||
versioning
|
||||
shell
|
||||
serialization
|
||||
clientrpc
|
||||
|
@ -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 <https://github.com/fracting>`_, `Marek Skocovsky <https://github.com/marekdapps>`_, `Karel Hajek <https://github.com/polybioz>`_, and `Jonny Chiu <https://github.com/johnnyychiu>`_ for their contributions to Corda in M10.
|
||||
Special thank you to `Qian Hong <https://github.com/fracting>`_, `Marek Skocovsky <https://github.com/marekdapps>`_,
|
||||
`Karel Hajek <https://github.com/polybioz>`_, and `Jonny Chiu <https://github.com/johnnyychiu>`_ 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 <http://www.cra
|
||||
be easily added to the node by simply dropping Groovy or Java files into the node's ``shell-commands`` directory.
|
||||
We have many enhancements planned over time including SSH access, more commands and better tab completion.
|
||||
|
||||
The new "DemoBench" makes it easy to configure and launch local Corda nodes. It is a standalone desktop app that can be bundled with its own JRE and packaged as either EXE (Windows), DMG (MacOS) or RPM (Linux-based). It has the following features:
|
||||
#. New nodes can be added at the click of a button. Clicking "Add node" creates a new tab that lets you edit the most important configuration properties of the node before launch, such as its legal name and which CorDapps will be loaded.
|
||||
The new "DemoBench" makes it easy to configure and launch local Corda nodes. It is a standalone desktop app that can be
|
||||
bundled with its own JRE and packaged as either EXE (Windows), DMG (MacOS) or RPM (Linux-based). It has the following
|
||||
features:
|
||||
#. New nodes can be added at the click of a button. Clicking "Add node" creates a new tab that lets you edit the most
|
||||
important configuration properties of the node before launch, such as its legal name and which CorDapps will be loaded.
|
||||
#. Each tab contains a terminal emulator, attached to the pseudoterminal of the node. This lets you see console output.
|
||||
#. You can launch an Corda Explorer instance for each node at the click of a button. Credentials are handed to the Corda Explorer so it starts out logged in already.
|
||||
#. You can launch an Corda Explorer instance for each node at the click of a button. Credentials are handed to the Corda
|
||||
Explorer so it starts out logged in already.
|
||||
#. Some basic statistics are shown about each node, informed via the RPC connection.
|
||||
#. Another button launches a database viewer in the system browser.
|
||||
#. The configurations of all running nodes can be saved into a single ``.profile`` file that can be reloaded later.
|
||||
|
||||
Soft Locking is a new feature implemented in the vault to prevent a node constructing transactions that attempt to use the same input(s) simultaneously.
|
||||
Such transactions would result in naturally wasted effort when the notary rejects them as double spend attempts.
|
||||
Soft locks are automatically applied to coin selection (eg. cash spending) to ensure that no two transactions attempt to spend the same fungible states.
|
||||
Soft Locking is a new feature implemented in the vault to prevent a node constructing transactions that attempt to use the
|
||||
same input(s) simultaneously. Such transactions would result in naturally wasted effort when the notary rejects them as
|
||||
double spend attempts. Soft locks are automatically applied to coin selection (eg. cash spending) to ensure that no two
|
||||
transactions attempt to spend the same fungible states.
|
||||
|
||||
The basic Amount API has been upgraded to have support for advanced financial use cases and to better integrate with currency reference data.
|
||||
The basic Amount API has been upgraded to have support for advanced financial use cases and to better integrate with
|
||||
currency reference data.
|
||||
|
||||
We have added optional out-of-process transaction verification. Any number of external verifier processes may be attached to the node which can handle loadbalanced verification requests.
|
||||
We have added optional out-of-process transaction verification. Any number of external verifier processes may be attached
|
||||
to the node which can handle loadbalanced verification requests.
|
||||
|
||||
We have also delivered the long waited Kotlin 1.1 upgrade in M10! The new features in Kotlin allow us to write even more clean and easy to manage code, which greatly increases our productivity.
|
||||
We have also delivered the long waited Kotlin 1.1 upgrade in M10! The new features in Kotlin allow us to write even more
|
||||
clean and easy to manage code, which greatly increases our productivity.
|
||||
|
||||
This release contains a large number of improvements, new features, library upgrades and bug fixes. For a full list of changes please see :doc:`changelog`.
|
||||
This release contains a large number of improvements, new features, library upgrades and bug fixes. For a full list of
|
||||
changes please see :doc:`changelog`.
|
||||
|
||||
Milestone 9
|
||||
-----------
|
||||
|
@ -53,8 +53,8 @@ for you from our `public Maven repository <https://bintray.com/r3/corda>`_.
|
||||
**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 <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"
|
||||
|
||||
...
|
||||
|
||||
|
29
docs/source/versioning.rst
Normal file
29
docs/source/versioning.rst
Normal file
@ -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 <https://developer.android.com/guide/topics/manifest/uses-sdk-element.html>`_.
|
||||
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.
|
@ -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())
|
||||
|
@ -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 {
|
||||
|
@ -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<NetworkMapService.RegistrationResponse> {
|
||||
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.RegistrationResponse>(NetworkMapService.REGISTER_TOPIC, request, networkMapNode.net.myAddress)
|
||||
|
@ -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<String>) {
|
||||
// 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<String>) {
|
||||
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<String>) {
|
||||
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<String>) {
|
||||
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<String, String> {
|
||||
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/").
|
||||
|
@ -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<Any>): NotaryService {
|
||||
|
@ -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<ServiceInfo>,
|
||||
val nodeVersionInfo: NodeVersionInfo,
|
||||
val versionInfo: VersionInfo,
|
||||
clock: Clock = NodeClock()) : AbstractNode(configuration, advertisedServices, clock) {
|
||||
companion object {
|
||||
private val logger = loggerFor<Node>()
|
||||
}
|
||||
|
||||
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,
|
||||
|
@ -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<User>,
|
||||
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())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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<Party, NodeRegistrationInfo> = ConcurrentHashMap()
|
||||
override val subscribers = ThreadBox(mutableMapOf<SingleMessageRecipient, LastAcknowledgeInfo>())
|
||||
@ -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<MessageHandlerRegistration>()
|
||||
|
||||
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,7 +228,9 @@ 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()
|
||||
@ -229,11 +240,15 @@ abstract class AbstractNetworkMapService(services: ServiceHubInternal) : Network
|
||||
|
||||
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) }
|
||||
}
|
||||
|
@ -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")
|
||||
|
@ -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),
|
||||
|
@ -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<Persistent
|
||||
* state within it is correctly restored.
|
||||
*/
|
||||
private class SwizzleNetworkMapService(val services: ServiceHubInternal) : NetworkMapService {
|
||||
var delegate: PersistentNetworkMapService = PersistentNetworkMapService(services)
|
||||
var delegate: PersistentNetworkMapService = PersistentNetworkMapService(services, MOCK_VERSION_INFO.platformVersion)
|
||||
|
||||
fun swizzle() {
|
||||
delegate.unregisterNetworkHandlers()
|
||||
delegate = PersistentNetworkMapService(services)
|
||||
delegate = PersistentNetworkMapService(services, MOCK_VERSION_INFO.platformVersion)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6,11 +6,12 @@ package net.corda.testing
|
||||
import com.google.common.net.HostAndPort
|
||||
import com.google.common.util.concurrent.ListenableFuture
|
||||
import net.corda.core.contracts.StateRef
|
||||
import net.corda.core.crypto.*
|
||||
import net.corda.core.crypto.Party
|
||||
import net.corda.core.crypto.SecureHash
|
||||
import net.corda.core.crypto.generateKeyPair
|
||||
import net.corda.core.flows.FlowLogic
|
||||
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.serialization.OpaqueBytes
|
||||
import net.corda.core.toFuture
|
||||
import net.corda.core.transactions.TransactionBuilder
|
||||
@ -83,8 +84,7 @@ 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_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<User> = emptyList(),
|
||||
@ -166,7 +167,8 @@ data class TestNodeConfiguration(
|
||||
override val devMode: Boolean = true,
|
||||
override val certificateSigningService: URL = URL("http://localhost"),
|
||||
override val certificateChainCheckPolicies: List<CertChainPolicyConfig> = 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(
|
||||
|
@ -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()
|
||||
|
@ -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<MockNode>()
|
||||
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<ServiceEntry> {
|
||||
|
@ -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 {
|
||||
|
@ -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<FullNodeConfiguration>().createNode(MOCK_NODE_VERSION_INFO)
|
||||
val node = config.parseAs<FullNodeConfiguration>().createNode(MOCK_VERSION_INFO)
|
||||
node.start()
|
||||
nodes += node
|
||||
thread(name = legalName) {
|
||||
|
@ -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,
|
||||
|
@ -80,7 +80,6 @@ dependencies {
|
||||
jar {
|
||||
manifest {
|
||||
attributes(
|
||||
'Corda-Version': corda_version,
|
||||
'Main-Class': mainClassName,
|
||||
'Class-Path': configurations.compile.collect { it.getName() }.join(' ')
|
||||
)
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
Loading…
Reference in New Issue
Block a user