Merge from R3 Corda 3.0 Dev Preview release branch to master

This commit is contained in:
josecoll 2018-01-13 11:00:01 +00:00
parent e7cc38cf1b
commit 729aa65e7f
63 changed files with 1247 additions and 1560 deletions

View File

@ -4,7 +4,9 @@ buildscript {
file("$projectDir/constants.properties").withInputStream { constants.load(it) }
// Our version: bump this on release.
ext.corda_release_version = "3.0-ENT-snapshot"
ext.corda_release_version = "R3.CORDA-3.0-SNAPSHOT" // "CORDA-3.0-SNAPSHOT" for Corda (Open Source)
ext.corda_release_distribution = "com.r3.corda" // "net.corda" for Corda (Open Source)
// Increment this on any release that changes public APIs anywhere in the Corda platform
ext.corda_platform_version = constants.getProperty("platformVersion")
ext.gradle_plugins_version = constants.getProperty("gradlePluginsVersion")
@ -154,7 +156,7 @@ allprojects {
attributes('Corda-Release-Version': corda_release_version)
attributes('Corda-Platform-Version': corda_platform_version)
attributes('Corda-Revision': corda_revision)
attributes('Corda-Vendor': 'Corda Enterprise Edition')
attributes('Corda-Vendor': 'R3 Corda Edition')
attributes('Automatic-Module-Name': "net.corda.${task.project.name.replaceAll('-', '.')}")
}
}
@ -191,7 +193,7 @@ allprojects {
}
}
group 'com.r3.corda.enterprise'
group 'com.r3.corda'
version "$corda_release_version"
repositories {
@ -301,7 +303,7 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) {
}
}
// User and key are commented out to prevent accidental pushes of Enterprise to public repos. DO NOT UNCOMMENT.
// User and key are commented out to prevent accidental pushes of R3 Corda to public repos. DO NOT UNCOMMENT.
bintrayConfig {
// user = System.getenv('CORDA_BINTRAY_USER')
// key = System.getenv('CORDA_BINTRAY_KEY')

View File

@ -1,6 +1,6 @@
gradlePluginsVersion=3.0.3
kotlinVersion=1.1.60
platformVersion=1
platformVersion=3
guavaVersion=21.0
bouncycastleVersion=1.57
typesafeConfigVersion=1.3.1

View File

@ -47,7 +47,8 @@ sealed class ByteSequence : Comparable<ByteSequence> {
fun open() = ByteArrayInputStream(_bytes, offset, size)
/**
* Create a sub-sequence backed by the same array.
* Create a sub-sequence of this sequence. A copy of the underlying array may be made, if a subclass overrides
* [bytes] to do so, as [OpaqueBytes] does.
*
* @param offset The offset within this sequence to start the new sequence. Note: not the offset within the backing array.
* @param size The size of the intended sub sequence.
@ -55,7 +56,8 @@ sealed class ByteSequence : Comparable<ByteSequence> {
fun subSequence(offset: Int, size: Int): ByteSequence {
require(offset >= 0)
require(offset + size <= this.size)
// Intentionally use bytes rather than _bytes, to mirror the copy-or-not behaviour of that property.
// Intentionally use bytes rather than _bytes, in case a subclass wants to prevent access to the original
// underlying array which could be revealed here (e.g. OpaqueBytes).
return if (offset == 0 && size == this.size) this else of(bytes, this.offset + offset, size)
}

View File

@ -1,14 +1,3 @@
{
"https://docs.corda.net/releases/release-M6.0": "M6.0",
"https://docs.corda.net/releases/release-M7.0": "M7.0",
"https://docs.corda.net/releases/release-M8.2": "M8.2",
"https://docs.corda.net/releases/release-M9.2": "M9.2",
"https://docs.corda.net/releases/release-M10.1": "M10.1",
"https://docs.corda.net/releases/release-M11.2": "M11.2",
"https://docs.corda.net/releases/release-M12.1": "M12.1",
"https://docs.corda.net/releases/release-M13.0": "M13.0",
"https://docs.corda.net/releases/release-M14.0": "M14.0",
"https://docs.corda.net/releases/release-V1.0": "V1.0",
"https://docs.corda.net/releases/release-V2.0": "V2.0",
"https://docs.corda.net/head/": "Master"
"https://docs.corda.r3.com/releases/release-V3.0": "V3.0"
}

View File

@ -18,7 +18,7 @@ API reference: <a href="api/kotlin/corda/index.html">Kotlin</a>/ <a href="api/ja
{% block footer %}
<script>
// A synchronous request to retrieve all the Corda versions.
$.getJSON("https://docs.corda.net/_static/versions", function(data) {
$.getJSON("https://docs.corda.r3.com/_static/versions", function(data) {
// Grab the current version.
var version = $("#version").html();

View File

@ -145,6 +145,8 @@ which is then referenced within a custom flow:
:start-after: DOCSTART TopupIssuer
:end-before: DOCEND TopupIssuer
.. _database_migration_ref:
Database Migration
==================
@ -232,7 +234,7 @@ Usage:
Configurations:
- To enable migration at startup, set:
- database.runMigration = true // false by default,
- ``database.runMigration = true`` // false by default,
Command line arguments:

File diff suppressed because it is too large Load Diff

View File

@ -18,6 +18,8 @@ an ongoing stream of updates from the node. More detail on how to use this is pr
For a brief tutorial on how one can use the RPC API see :doc:`tutorial-clientrpc-api`.
.. _rpc_security_mgmt_ref:
RPC permissions
---------------
If a node's owner needs to interact with their node via RPC (e.g. to read the contents of the node's storage), they
@ -68,6 +70,8 @@ Fine grained permissions allow a user to invoke a specific RPC operation, or to
- to invoke a RPC operation: ``InvokeRpc.<rpc method name>`` e.g., ``InvokeRpc.nodeInfo``.
.. note:: Permission ``InvokeRpc.startFlow`` allows a user to initiate all flows.
.. _authentication_ref:
RPC security management
-----------------------

View File

@ -54,11 +54,11 @@ author = u'R3 DLG'
# built documents.
#
# The short X.Y version.
version = 'Master'
version = '3.0'
# The full version, including alpha/beta/rc tags.
release = 'Master'
release = 'R3 Corda V3.0 Developer Preview'
# The version for use in the dropdown html.
html_context = {'version': 'Master'}
html_context = {'version': 'V3.0'}
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.

View File

@ -68,28 +68,24 @@ path to the node's base directory.
.. note:: Longer term these keys will be managed in secure hardware devices.
:database: Database configuration:
.. _database_properties_ref:
:transactionIsolationLevel: Transaction isolation level as defined by the ``TRANSACTION_`` constants in
:database: This section is used to configure JDBC and Hibernate related properties:
:transactionIsolationLevel: Transaction isolation level as defined by the ``TRANSACTION_`` constants in
``java.sql.Connection``, but without the "TRANSACTION_" prefix. Defaults to REPEATABLE_READ.
:exportHibernateJMXStatistics: Whether to export Hibernate JMX statistics (caution: expensive run-time overhead)
:exportHibernateJMXStatistics: Whether to export Hibernate JMX statistics (caution: expensive run-time overhead)
:runMigration: Boolean on whether to run the database migration scripts. Defaults to true.
:schema: (optional) some database providers require a schema name when generating DDL and SQL statements.
(the value is passed to Hibernate property 'hibernate.default_schema').
:dataSourceProperties: This section is used to configure the jdbc connection and database driver used for the nodes persistence.
Currently the defaults in ``/node/src/main/resources/reference.conf`` are as shown in the first example. This is currently
the only configuration that has been tested, although in the future full support for other storage layers will be validated.
:database: This section is used to configure JDBC and Hibernate related properties:
:transactionIsolationLevel: Transaction isolation level as defined by the ``TRANSACTION_`` constants in
``java.sql.Connection``, but without the "TRANSACTION_" prefix. Defaults to REPEATABLE_READ.
:exportHibernateJMXStatistics: Whether to export Hibernate JMX statistics (caution: expensive run-time overhead)
:runMigration: Boolean on whether to run the database migration scripts. Defaults to true.
:schema: (optional) some database providers require a schema name when generating DDL and SQL statements.
(the value is passed to Hibernate property 'hibernate.hbm2ddl.auto').
:messagingServerAddress: The address of the ArtemisMQ broker instance. If not provided the node will run one locally.
:p2pAddress: The host and port on which the node is available for protocol operations over ArtemisMQ.
@ -196,5 +192,7 @@ path to the node's base directory.
:exportJMXTo: If set to ``http``, will enable JMX metrics reporting via the Jolokia HTTP/JSON agent.
Default Jolokia access url is http://127.0.0.1:7005/jolokia/
.. _config_amqp_bridge:
:useAMQPBridges: Optionally can be set to ``false`` to use Artemis CORE Bridges for peer-to-peer communications.
Otherwise, defaults to ``true`` and the AMQP 1.0 protocol will be used for message transfer between nodes.

View File

@ -28,14 +28,12 @@ Setting your dependencies
Choosing your Corda version
^^^^^^^^^^^^^^^^^^^^^^^^^^^
``ext.corda_release_version`` and ``ext.corda_gradle_plugins_version`` are used in the ``build.gradle`` to define the
versions of Corda and the Corda Gradle Plugins that are used to build your CorDapp.
For example, to use version 1.0 of Corda and version 1.0 of the Corda gradle plugins, you'd write:
The following three lines of the ``build.gradle`` file define the Corda version and distribution used to build your CorDapp:
.. sourcecode:: groovy
ext.corda_release_version = '1.0.0'
ext.corda_release_distribution = 'net.corda'
ext.corda_gradle_plugins_version = '1.0.0'
You can find the latest published version of both here: https://bintray.com/r3/corda.
@ -94,21 +92,21 @@ is already correctly configured and this is for reference only;
dependencies {
// Corda integration dependencies
cordaCompile "net.corda:corda-core:$corda_release_version"
cordaCompile "net.corda:corda-finance:$corda_release_version"
cordaCompile "net.corda:corda-jackson:$corda_release_version"
cordaCompile "net.corda:corda-rpc:$corda_release_version"
cordaCompile "net.corda:corda-node-api:$corda_release_version"
cordaCompile "net.corda:corda-webserver-impl:$corda_release_version"
cordaRuntime "net.corda:corda:$corda_release_version"
cordaRuntime "net.corda:corda-webserver:$corda_release_version"
testCompile "net.corda:corda-test-utils:$corda_release_version"
cordaCompile "$corda_release_distribution:corda-core:$corda_release_version"
cordaCompile "$corda_release_distribution:corda-finance:$corda_release_version"
cordaCompile "$corda_release_distribution:corda-jackson:$corda_release_version"
cordaCompile "$corda_release_distribution:corda-rpc:$corda_release_version"
cordaCompile "$corda_release_distribution:corda-node-api:$corda_release_version"
cordaCompile "$corda_release_distribution:corda-webserver-impl:$corda_release_version"
cordaRuntime "$corda_release_distribution:corda:$corda_release_version"
cordaRuntime "$corda_release_distribution:corda-webserver:$corda_release_version"
testCompile "$corda_release_distribution:corda-test-utils:$corda_release_version"
// Corda Plugins: dependent flows and services
// Identifying a CorDapp by its module in the same project.
cordapp project(":cordapp-contracts-states")
// Identifying a CorDapp by its fully-qualified name.
cordapp "net.corda:bank-of-corda-demo:1.0"
cordapp "$corda_release_distribution:bank-of-corda-demo:1.0"
// Some other dependencies
compile "org.jetbrains.kotlin:kotlin-stdlib-jre8:$kotlin_version"

View File

@ -88,7 +88,7 @@ nodes. Here is an example ``Cordform`` task called ``deployNodes`` that creates
// No webport property, so no webserver will be created.
h2Port 10004
// Includes the corda-finance CorDapp on our node.
cordapps = ["net.corda:corda-finance:$corda_release_version"]
cordapps = ["$corda_release_distribution:corda-finance:$corda_release_version"]
}
node {
name "O=PartyA,L=London,C=GB"
@ -96,7 +96,7 @@ nodes. Here is an example ``Cordform`` task called ``deployNodes`` that creates
rpcPort 10006
webPort 10007
h2Port 10008
cordapps = ["net.corda:corda-finance:$corda_release_version"]
cordapps = ["$corda_release_distribution:corda-finance:$corda_release_version"]
// Grants user1 all RPC permissions.
rpcUsers = [[ user: "user1", "password": "test", "permissions": ["ALL"]]]
}
@ -106,7 +106,7 @@ nodes. Here is an example ``Cordform`` task called ``deployNodes`` that creates
rpcPort 10010
webPort 10011
h2Port 10012
cordapps = ["net.corda:corda-finance:$corda_release_version"]
cordapps = ["$corda_release_distribution:corda-finance:$corda_release_version"]
// Grants user1 the ability to start the MyFlow flow.
rpcUsers = [[ user: "user1", "password": "test", "permissions": ["StartFlow.net.corda.flows.MyFlow"]]]
}

View File

@ -25,14 +25,14 @@ service.
notary = [validating : true]
p2pPort 10002
rpcPort 10003
cordapps = ["net.corda:corda-finance:$corda_release_version"]
cordapps = ["$corda_release_distribution:corda-finance:$corda_release_version"]
}
node {
name "O=PartyA,L=London,C=GB"
p2pPort 10005
rpcPort 10006
webPort 10007
cordapps = ["net.corda:corda-finance:$corda_release_version"]
cordapps = ["$corda_release_distribution:corda-finance:$corda_release_version"]
rpcUsers = [[ user: "user1", "password": "test", "permissions": ["ALL]]]
}
node {
@ -41,7 +41,7 @@ service.
rpcPort 10009
webPort 10010
sshdPort 10024
cordapps = ["net.corda:corda-finance:$corda_release_version"]
cordapps = ["$corda_release_distribution:corda-finance:$corda_release_version"]
rpcUsers = [[ user: "user1", "password": "test", "permissions": ["ALL"]]]
}
}

View File

@ -1,84 +1,78 @@
Network Map
===========
The network map stores a collection of ``NodeInfo`` objects, each representing another node with which the node can interact.
There are two sources from which a Corda node can retrieve ``NodeInfo`` objects:
The network map is a collection of signed ``NodeInfo`` objects (signed by the node it represents and thus tamper-proof)
forming the set of reachable nodes in a compatbility zone. A node can receive these objects from two sources:
1. the REST protocol with the network map service, which also provides a publishing API,
1. The HTTP network map service if the ``compatibilityZoneURL`` config key is specified.
2. The ``additional-node-infos`` directory within the node's directory.
2. the ``additional-node-infos`` directory.
HTTP network map service
------------------------
If the node is configured with the ``compatibilityZoneURL`` config then it first uploads its own signed ``NodeInfo``
to the server (and each time it changes on startup) and then proceeds to download the entire network map. The network map
consists of a list of ``NodeInfo`` hashes. The node periodically polls for the network map (based on the HTTP cache expiry
header) and any new hash entries are downloaded and cached. Entries which no longer exist are deleted from the node's cache.
Protocol Design
---------------
The node info publishing protocol:
The set of REST end-points for the network map service are as follows.
* Create a ``NodeInfo`` object, and sign it to create a ``SignedNodeInfo`` object.
* Serialise the signed data and POST the data to the network map server.
* The network map server validates the signature and acknowledges the registration with a HTTP 200 response, it will return HTTP 400 "Bad Request" if the data failed validation or if the public key wasn't registered with the network.
* The network map server will sign and distribute the new network map periodically.
Node side network map update protocol:
* The Corda node will query the network map service periodically according to the ``Expires`` attribute in the HTTP header.
* The network map service returns a signed ``NetworkMap`` object which looks as follows:
.. container:: codeset
.. sourcecode:: kotlin
data class NetworkMap {
val nodeInfoHashes: List<SecureHash>,
val networkParametersHash: SecureHash
}
The object contains list of node info hashes and hash of the network parameters data structure (without the signatures).
* The node updates its local copy of ``NodeInfos`` if it is different from the newly downloaded ``NetworkMap``.
Network Map service REST API:
+----------------+-----------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+
| Request method | Path | Description |
+================+===================================+========================================================================================================================================================+
| POST | /network-map/publish | Publish new ``NodeInfo`` to the network map service, the legal identity in ``NodeInfo`` must match with the identity registered with the doorman. |
+----------------+-----------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+
| GET | /network-map | Retrieve ``NetworkMap`` from the server, the ``NetworkMap`` object contains list of node info hashes and ``NetworkParameters`` hash. |
+----------------+-----------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+
| GET | /network-map/node-info/{hash} | Retrieve ``NodeInfo`` object with the same hash. |
+----------------+-----------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+
| GET | /network-map/parameters/{hash} | Retrieve ``NetworkParameters`` object with the same hash. |
+----------------+-----------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+
TODO: Access control of the network map will be added in the future.
+----------------+-----------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------+
| Request method | Path | Description |
+================+=========================================+==============================================================================================================================================+
| POST | /network-map/publish | For the node to upload its signed ``NodeInfo`` object to the network map. |
+----------------+-----------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------+
| GET | /network-map | Retrieve the current signed network map object. The entire object is signed with the network map certificate which is also attached. |
+----------------+-----------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------+
| GET | /network-map/node-info/{hash} | Retrieve a signed ``NodeInfo`` as specified in the network map object. |
+----------------+-----------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------+
| GET | /network-map/network-parameters/{hash} | Retrieve the signed network parameters (see below). The entire object is signed with the network map certificate which is also attached. |
+----------------+-----------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------+
The ``additional-node-infos`` directory
---------------------------------------
Each Corda node reads, and continuously polls, the files contained in a directory named ``additional-node-infos`` inside the node base directory.
Nodes expect to find a serialized ``SignedNodeInfo`` object, the same object which is sent to network map server.
Whenever a node starts it writes on disk a file containing its own ``NodeInfo``, this file is called ``nodeInfo-XXX`` where ``XXX`` is a long string.
Hence if an operator wants node A to see node B they can pick B's ``NodeInfo`` file from B base directory and drop it into A's ``additional-node-infos`` directory.
Alongside the HTTP network map service, or as a replacement if the node isn't connected to one, the node polls the
contents of the ``additional-node-infos`` directory located in its base directory. Each file is expected to be the same
signed ``NodeInfo`` object that the network map service vends. These are automtically added to the node's cache and can
be used to supplement or replace the HTTP network map. If the same node is advertised through both mechanisms then the
latest one is taken.
On startup the node generates its own signed node info file, filename of the format ``nodeInfo-${hash}``. To create a simple
network without the HTTP network map service then simply place this file in the ``additional-node-infos`` directory
of every node that's part of this network.
Network parameters
------------------
Network parameters are constants that every node participating in the network needs to agree on and use for interop purposes.
The structure is distributed as a file containing serialized ``SignedData<NetworkParameters>`` with a signature from
a sub-key of the compatibility zone root cert. Network map advertises the hash of currently used network parameters.
The ``NetworkParameters`` structure contains:
* ``minimumPlatformVersion`` - minimum version of Corda platform that is required for nodes in the network.
* ``notaries`` - list of well known and trusted notary identities with information on validation type.
* ``maxMessageSize`` - maximum P2P message size sent over the wire in bytes.
* ``maxTransactionSize`` - maximum permitted transaction size in bytes.
* ``modifiedTime`` - the time the network parameters were created by the CZ operator.
* ``epoch`` - version number of the network parameters. Starting from 1, this will always increment on each new set of parameters.
The set of parameters is still under development and we may find the need to add additional fields.
Network parameters are a set of values that every node participating in the network needs to agree on and use to
correctly interoperate with each other. If the node is using the HTTP network map service then on first startup it will
download the signed network parameters, cache it in a ``network-parameters`` file and apply them on the node.
.. warning:: If the ``network-parameters`` file is changed and no longer matches what the network map service is advertising
then the node will automatically shutdown. Resolution to this is to delete the incorrect file and restart the node so
that the parameters can be downloaded again.
.. note:: A future release will support the notion of network parameters changes.
If the node isn't using a HTTP network map service then it's expected the signed file is provided by some other means.
For such a scenario there is the network bootstrapper tool which in addition to generating the network parameters file
also distributes the node info files to the node directories. More information can be found in :doc:`setting-up-a-corda-network`.
The current set of network parameters:
:minimumPlatformVersion: The minimum platform version that the nodes must be running. Any node which is below this will
not start.
:notaries: List of identity and validation type (either validating or non-validating) of the notaries which are permitted
in the compatibility zone.
:maxMessageSize: Maximum allowed P2P message size sent over the wire in bytes. Any message larger than this will be
split up.
:maxTransactionSize: Maximum permitted transaction size in bytes.
:modifiedTime: The time when the network parameters were last modified by the compatibility zone operator.
:epoch: Version number of the network parameters. Starting from 1, this will always increment whenever any of the
parameters change.
.. note:: ``maxTransactionSize`` is currently not enforced in the node, but will be in a later release.
More parameters may be added in future releases.

View File

@ -73,6 +73,8 @@ Any database browsing tool that supports JDBC can be used, but if you have Intel
a tool integrated with your IDE. Just open the database window and add an H2 data source with the above details.
You will now be able to browse the tables and row data within them.
.. _jolokia_ref:
Monitoring your node
--------------------

View File

@ -34,12 +34,14 @@ Standalone database
-------------------
To run a node against a remote database modify node JDBC connection properties in `dataSourceProperties` entry
and Hibernate properties in `database` entry - see `:ref:`dataSourceProperties`.
and Hibernate properties in `database` entry - see :ref:`Node configuration <database_properties_ref>`.
.. _sql_server_ref:
SQL Azure and SQL Server
````````````````````````
Corda supports SQL Server 2017 (14.0.3006.16) and Azure SQL (12.0.2000.8).
The minimum transaction isolation level ``database.transactionIsolationLevel`` is 'readCommitted'.
The minimum transaction isolation level ``database.transactionIsolationLevel`` is 'READ_COMMITTED'.
The property ``database.schema`` is optional.
Corda ships with Microsoft JDBC Driver 6.2 for SQLServer out-of-the-box.
@ -47,36 +49,40 @@ Example node configuration for SQL Azure:
.. sourcecode:: none
dataSourceProperties {
dataSourceClassName = "com.microsoft.sqlserver.jdbc.SQLServerDataSource"
dataSourceProperties.dataSource.url = "jdbc:sqlserver://[DATABASE_SERVER].database.windows.net:1433;databaseName=[DATABASE];
encrypt=true;trustServerCertificate=false;hostNameInCertificate=*.database.windows.net;loginTimeout=30"
dataSourceProperties.dataSource.user = [USER]
dataSourceProperties.dataSource.password = [PASSWORD]
}
database {
transactionIsolationLevel = "readCommitted"
schema = [SCHEMA]
}
dataSourceProperties {
dataSourceClassName = "com.microsoft.sqlserver.jdbc.SQLServerDataSource"
dataSourceProperties.dataSource.url = "jdbc:sqlserver://[DATABASE_SERVER].database.windows.net:1433;databaseName=[DATABASE];
encrypt=true;trustServerCertificate=false;hostNameInCertificate=*.database.windows.net;loginTimeout=30"
dataSourceProperties.dataSource.user = [USER]
dataSourceProperties.dataSource.password = [PASSWORD]
}
database {
transactionIsolationLevel = READ_COMMITTED
schema = [SCHEMA]
}
.. _postgres_ref:
PostgreSQL
````````````````````````
Corda supports PostgreSQL 9.6 database.
The property ``database.schema`` is optional. The value of ``database.schema`` is automatically wrapped in double quotes
to preserve case-sensitivity (e.g. `AliceCorp` becomes `"AliceCorp"`, without quotes PostgresSQL would treat the value as `alicecorp`).
Corda supports PostgreSQL 9.6 database preliminarily.
The property ``database.schema`` is optional. Currently only lowercase value is supported,
please ensure your database setup uses lowercase schema names (Corda sends an unquoted schema name
and PostgresSQL interprets the value as a lowercase one e.g. `AliceCorp` becomes `alicecorp`).
Corda ships with PostgreSQL JDBC Driver 42.1.4 out-of-the-box.
Example node configuration for PostgreSQL:
.. sourcecode:: none
dataSourceProperties = {
dataSourceClassName = "org.postgresql.ds.PGSimpleDataSource"
dataSource.url = "jdbc:postgresql://[HOST]:[PORT]/postgres"
dataSource.user = [USER]
dataSource.password = [PASSWORD]
}
database = {
transactionIsolationLevel = READ_COMMITTED
schema = [SCHEMA]
}
dataSourceProperties = {
dataSourceClassName = "org.postgresql.ds.PGSimpleDataSource"
dataSource.url = "jdbc:postgresql://[HOST]:[PORT]/postgres"
dataSource.user = [USER]
dataSource.password = [PASSWORD]
}
database = {
transactionIsolationLevel = READ_COMMITTED
schema = [SCHEMA]
}

View File

@ -29,7 +29,7 @@ parties on the network or to exit cash (for itself only).
When connected to a *Participant* node a user can only execute cash transaction commands to move cash to other parties on the network.
The Demo Nodes can be started in one of two modes:
The Demo Nodes can be started in one of three modes:
1. Normal
@ -60,14 +60,30 @@ The Demo Nodes can be started in one of two modes:
./gradlew tools:explorer:runSimulationNodes
.. _flow_triage:
3. Flow triage
Once again, building on the demonstration Corda network topology described above, this scenario mode triggers
an exception within a flow which can then be visualized using the "Flow Triage" panel within the Explorer.
The "Flow Triage" panel will be enhanced in the future to enable operators to take corrective actions upon flow failures
(eg. retry, terminate, amend and replay)
**Windows:**
gradlew.bat tools:explorer:runFlowTriageNodes
**Other:**
./gradlew tools:explorer:runFlowTriageNodes
.. note:: 5 Corda nodes will be created on the following port on localhost by default.
* Notary -> 20003 (Does not accept logins)
* Alice -> 20006
* Bob -> 20009
* UK Bank Plc -> 20012 (*Issuer node*)
* USA Bank Corp -> 20015 (*Issuer node*)
* Notary -> 20001 (Does not accept logins)
* Alice -> 20004
* Bob -> 20007
* UK Bank Plc -> 20010 (*Issuer node*)
* USA Bank Corp -> 20013 (*Issuer node*)
Explorer login credentials to the Issuer nodes are defaulted to ``manager`` and ``test``.
Explorer login credentials to the Participants nodes are defaulted to ``user1`` and ``test``.

View File

@ -1,37 +1,164 @@
Release notes
=============
Here are release notes for each snapshot release from M9 onwards.
Unreleased
----------
R3 Corda 3.0 Developer Preview
------------------------------
This Developer Preview takes us towards the launch of R3 Corda, R3's commercially supported enterprise blockchain platform.
Whilst the recent major releases of **Corda** ("Open Source") - V1.0 and V2.0 - have focused on providing API stability and
functionality, **R3 Corda** has been primarily focused on non-functional aspects of the platform: performance, scalability,
robustness, security, configurability, manageability, supportability and upgradeability.
Here is a summary of some of the key new features in this preview, many of which will also appear in the next open
source release:
- support for :ref:`Azure SQL and SQL Server 2017 <sql_server_ref>` databases.
- integrated :ref:`database schema management <database_migration_ref>` tooling using `Liquibase <http://www.liquibase.org/>`_
- completely re-designed :doc:`network-map` Service.
- enabled :ref:`AMQP serialization <amqp_ref>` for peer to peer messaging, and vault transaction storage.
- pluggable :ref:`user authentication <authentication_ref>` and :ref:`fine-grained access control <rpc_security_mgmt_ref>`.
- re-designed Flow Framework manager in preparation for fully multi-threaded implementation.
- improvements to the Doorman certificate issuance process (including integration with HSMs).
- additional JMX metrics exported via :ref:`Jolokia for monitoring <jolokia_ref>` and pro-active alert management.
- a secure, remotely accessible, :ref:`SSH server <ssh_server>` in the node with built-in authorization and permissioning to enable remote
node administration without requiring console access to the underlying operating system.
- re-architected and re-designed Corda bridge management for secure P2P connectivity between participants.
- enhanced Explorer tool with new :ref:`flow triage <flow_triage>` user interface panel to visualize all currently running flows.
- preliminary implementation of Business Networks concept (a private sub group within a Corda Compatibility Zone).
We also continue to make improvements in usability of our Test Framework APIs in preparation for declaring the test
framework API stable (in addition to the already-stabilised core APIs).
Significant changes implemented in reaching this Developer Preview include:
* **AMQP**:
AMQP Serialization is now enabled for both peer to peer communication and the writing of states to the vault. This
change brings a serialisation format that will allow us to deliver enhanced security and wire stability. It is a key
prerequisite to enabling different Corda node versions to coexist on the same network and to enable easier upgrades.
Details on the AMQP serialization framework can be found :ref:`here <amqp_ref>`. This provides an introduction and
overview of the framework whilst more specific details on object evolution as it relates to serialization is similarly
found in :doc:`serialization-default-evolution` and :doc:`serialization-enum-evolution` respectively.
.. note:: This release delivers the bulk of our transition from Kryo serialisation to AMQP serialisation. This means that many of the restrictions
that were documented in previous versions of Corda are now enforced.
In particular, you are advised to review the section titled :ref:`Custom Types <amqp_custom_types_ref>`.
To aid with the transition, we have included support in this release for default construction and instantiation of
objects with inaccessible private fields, but it is not guaranteed that this support will continue into future versions;
the restrictions documented at the link above are the canonical source.
* **New Network Map Service**:
This release introduces the new network map architecture. The network map service has been completely redesigned and
implemented to enable future increased network scalability and redundancy, reduced runtime operational overhead,
support for multiple notaries, and administration of network compatibility zones (CZ) and business networks.
A Corda Compatibility Zone (CZ) is defined as a grouping of participants and services (notaries, oracles,
doorman, network map server) configured within an operational Corda network to be interoperable and compatible with
each other.
We introduce the concept of network parameters, which will be used in a future version of Corda to specify precisely
the set of constants (or ranges of constants) upon which a set of nodes need to agree in order to be assured of seamless
inter-operation. Additional security controls ensure that all network map data is now signed, thus reducing the power
of the network operator to tamper with the map.
This release also adds Hardware Security Module (HSM) support to the doorman service (certificate authority).
By integrating with external HSMs, we have further strengthened the security of issuing network certificates and
signing of network map related data.
Further information can be found in the :doc:`changelog` and :doc:`network-map` documentation.
* **Third party database support**:
R3 Corda has been tested against Azure SQL and SQL Server 2017 databases (in addition to the existing default support
of H2 for development mode). This preview adds preliminary support for :ref:`PostgreSQL 9.6 <postgres_ref>`.
Support for Oracle 11g RC02 and Oracle 12c is currently under development. All required database settings can be
specified in the node configuration file. For configuration details see :doc:`node-database`.
* **Integrated database migration tooling**:
We have adopted and integrated `Liquibase <http://www.liquibase.org/>`_ , an open source database-independent library
for tracking, managing and applying database schema changes in order to ease the evolution (creation and migration) of
CorDapp custom contract schemas and facilitate the operational administration of a Corda nodes database.
We provide tooling to export DDL and data (as SQL statements) to a file to be inspected and/or manually applied by a DBA.
Please see :ref:`database migration <database_migration_ref>` for further details.
* **Pluggable user authentication and fine-grained access control**:
All RPC functions are now subject to permission checks (previously these only applied when starting flows).
We have also included experimental support for external user credentials data source and password encryption using the
`Apache Shiro <https://shiro.apache.org>`_ framework. Please see :ref:`RPC security management <rpc_security_mgmt_ref>` for further details.
* **Preliminary preview of new bridge management functionality**:
The bridge manager component is responsible for dynamically establishing remote connectivity with participant nodes
in a Corda peer to peer network. A new Bridge manager has been designed and implemented to be used integrally
within a :ref:`Corda node <config_amqp_bridge>` or deployed (in the final R3 Corda 3.0 release) as a standalone component in DMZ operational deployments,
where security concerns require separation of infrastructure messaging subsystems.
* **Preliminary preview of flow triage functionality**:
The explorer GUI was extended with a panel similar to the ``flow watch`` CRaSH shell command. It provides users with a view of all
flows currently executed on the node, with information about success/failure. The "Flow Triage" panel will be enhanced in the future
to enable operators to take corrective actions upon flow failures (eg. retry, terminate, amend and replay).
* **Experimental preview of a new operational Corda network grouping concept: Business Networks**:
Business Networks are introduced as a way to partition the global population of nodes (a Compatibility Zone) into
independent, possibly overlapping, groups. A Business Network operator (BNO) will have control over which nodes will
be admitted into a Business Network. Some nodes may choose not to register themselves in the global Network Map, and
will therefore remain invisible to nodes outside of their Business Network. Further documentation will be forthcoming
by the final R3 Corda 3.0 release.
See the "Business Network reference implementation" prototype example in the Explorer tool (instructions in README.md).
In addition to enhancements focused on non-functional capabilities, this release encompasses a number of functional
improvements, including:
* Doorman Service
In order to automate a node's network joining process, a new Doorman service has been introduced with this release.
The Doorman's main purpose is to restrict network access only to those nodes whose identity has been confirmed and their network joining request approved.
It issues node-level certificates which are then used by other nodes in the network to confirm a nodes identity and network permissions.
More information on Doorman and how to run it can be found in :doc:`running-doorman`.
* Hardware Security Module (HSM) for Doorman
To allow for increased security, R3 Corda introduces HSM integration. Doorman certificates (together with their keys)
can now be stored on secured hardware constraining the way those certificates are accessed. Any usage of those certificates
(e.g. data signing or node-level certificate generation) falls into a restrictive process that is automatically audited
and can be configured to involve human-in-the-loop in order to prevent unauthorised access. The HSM integration is embodied
in our new Signing Service. More on this in :doc:`signing-service`.
* X.509 certificates now have an extension that specifies the Corda role the certificate is used for, and the role
hierarchy is now enforced in the validation code. This only has impact on those developing integrations with external
PKI solutions, in most cases it is managed transparently by Corda. A formal specification of the extension can be
PKI solutions. In most cases it is managed transparently by Corda. A formal specification of the extension can be
found at see :doc:`permissioning-certificate-specification`.
* **Enum Class Evolution**
With the addition of AMQP serialization Corda now supports enum constant evolution.
* Custom Serializers
To allow interop with third party libraries that cannot be recompiled we add functionality that allows custom serializers
to be written for those classes. If needed, a proxy object can be created as an interim step that allows Corda's internal
serializers to operate on those types. A good example of this is the SIMM valuation demo which has a number of such
serializers defined in the plugin/custom serializers package
That is the ability to alter an enum constant and, as long as certain rules are followed and the correct
annotations applied, have older and newer instances of that enumeration be understood.
Please refer to the :doc:`changelog` for detailed explanations of all new features.
* **AMQP Enabled**
Finally, please note that although this developer preview has not yet been security audited, it is currently being subjected
to a full external secure code review and penetration test.
AMQP Serialization is now enabled for both peer to peer communication and writing states to the vault. This change
brings a stable format Corda can support internally throughout it's lifetime that meets the needs of Corda and our
users.
As per previous major releases, we have provided a comprehensive upgrade notes (:doc:`upgrade-notes`) to ease the upgrade
of CorDapps to R3 Corda 3.0 Developer Preview. In line with our commitment to API stability, code level changes
are fairly minimal, and mostly related to improvements to our nearly API stable test framework.
* **Custom Serializers**
From a build perspective, switching CorDapps built using Corda (the "Open Source" code) to R3 Corda is mostly effortless,
and simply requires setting two gradle build file variables:
To allow interop with third party libraries that cannot be recompiled we add functionality that allows custom serializers
to be written for those classes. If needed, a proxy object can be created as an interim step that allows Corda's internal
serializers to operate on those types.
.. sourcecode:: shell
A good example of this is the SIMM valuation demo which has a number of such serializers defined in the plugin/customserializers package
ext.corda_release_version = 'R3.CORDA-3.0.0-DEV-PREVIEW'
ext.corda_release_distribution = 'com.r3.corda'
Release 2.0
----------
Please note this release is distributed under license and should not be used in a Production environment yet.
We look forward to hearing your feedback on this Developer Preview.
Corda 2.0
---------
Following quickly on the heels of the release of Corda 1.0, Corda version 2.0 consolidates
a number of security updates for our dependent libraries alongside the reintroduction of the Observer node functionality.
This was absent from version 1 but based on user feedback its re-introduction removes the need for complicated "isRelevant()" checks.
@ -46,11 +173,11 @@ and via the versioning APIs.
* **Observer Nodes**
Adds the facility for transparent forwarding of transactions to some third party observer, such as a regulator. By having
that entity simply run an Observer node they can simply recieve a stream of digitally signed, de-duplicated reports that
that entity simply run an Observer node they can simply receive a stream of digitally signed, de-duplicated reports that
can be used for reporting.
Release 1.0
-----------
Corda 1.0
---------
Corda 1.0 is finally here!
This critical step in the Corda journey enables the developer community, clients, and partners to build on Corda with confidence.
@ -154,253 +281,3 @@ We have provided a comprehensive :doc:`upgrade-notes` to ease the transition of
Upgrading to this release is strongly recommended, and you will be safe in the knowledge that core APIs will no longer break.
Thank you to all contributors for this release!
Milestone 14
------------
This release continues with the goal to improve API stability and developer friendliness. There have also been more
bug fixes and other improvements across the board.
The CorDapp template repository has been replaced with a specific repository for
`Java <https://github.com/corda/cordapp-template-java>`_ and `Kotlin <https://github.com/corda/cordapp-template-kotlin>`_
to improve the experience of starting a new project and to simplify the build system.
It is now possible to specify multiple IP addresses and legal identities for a single node, allowing node operators
more flexibility in setting up nodes.
A format has been introduced for CorDapp JARs that standardises the contents of CorDapps across nodes. This new format
now requires CorDapps to contain their own external dependencies. This paves the way for significantly improved
dependency management for CorDapps with the release of `Jigsaw (Java Modules) <http://openjdk.java.net/projects/jigsaw/>`_. For those using non-gradle build systems it is important
to read :doc:`cordapp-build-systems` to learn more. Those using our ``cordformation`` plugin simply need to update
to the latest version (``0.14.0``) to get the fixes.
We've now begun the process of demarcating which classes are part of our public API and which ones are internal.
Everything found in ``net.corda.core.internal`` and other packages in the ``net.corda`` namespace which has ``.internal`` in it are
considered internal and not for public use. In a future release any CorDapp using these packages will fail to load, and
when we migrate to Jigsaw these will not be exported.
The transaction finalisation flow (``FinalityFlow``) has had hooks added for alternative implementations, for example in
scenarios where no single participant in a transaction is aware of the well known identities of all parties.
DemoBench has a fix for a rare but inconvenient crash that can occur when sharing your display across multiple devices,
e.g. a projector while performing demonstrations in front of an audience.
Guava types are being removed because Guava does not have backwards compatibility across versions, which has serious
issues when multiple libraries depend on different versions of the library.
The identity service API has been tweaked, primarily so anonymous identity registration now takes in
AnonymousPartyAndPath rather than the individual components of the identity, as typically the caller will have
an AnonymousPartyAndPath instance. See change log for further detail.
Upgrading to this release is strongly recommended in order to keep up with the API changes, removal and additions.
Milestone 13
------------
Following our first public beta in M12, this release continues the work on API stability and user friendliness. Apart
from bug fixes and code refactoring, there are also significant improvements in the Vault Query and the
Identity Service (for more detailed information about what has changed, see :doc:`changelog`).
More specifically:
The long awaited new **Vault Query** service makes its debut in this release and provides advanced vault query
capabilities using criteria specifications (see ``QueryCriteria``), sorting, and pagination. Criteria specifications
enable selective filtering with and/or composition using multiple operator primitives on standard attributes stored in
Corda internal vault tables (eg. vault_states, vault_fungible_states, vault_linear_states), and also on custom contract
state schemas defined by CorDapp developers when modelling new contract types. Custom queries are specifiable using a
simple but sophisticated builder DSL (see ``QueryCriteriaUtils``). The new Vault Query service is usable by flows and by
RPC clients alike via two simple API functions: ``queryBy()`` and ``trackBy()``. The former provides point-in-time
snapshot queries whilst the later supplements the snapshot with dynamic streaming of updates.
See :doc:`api-vault-query` for full details.
We have written a comprehensive Hello, World! tutorial, showing developers how to build a CorDapp from start
to finish. The tutorial shows how the core elements of a CorDapp - states, contracts and flows - fit together
to allow your node to handle new business processes. It also explains how you can use our contract and
flow testing frameworks to massively reduce CorDapp development time.
Certificate checks have been enabled for much of the identity service. These are part of the confidential (anonymous)
identities work, and ensure that parties are actually who they claim to be by checking their certificate path back to
the network trust root (certificate authority).
To deal with anonymized keys, we've also implemented a deterministic key derivation function that combines logic
from the HMAC-based Extract-and-Expand Key Derivation Function (HKDF) protocol and the BIP32 hardened
parent-private-key -> child-private-key scheme. This function currently supports the following algorithms:
ECDSA secp256K1, ECDSA secpR1 (NIST P-256) and EdDSA ed25519. We are now very close to fully supporting anonymous
identities so as to increase privacy even against validating notaries.
We have further tightened the set of objects which Corda will attempt to serialise from the stack during flow
checkpointing. As flows are arbitrary code in which it is convenient to do many things, we ended up pulling in a lot of
objects that didn't make sense to put in a checkpoint, such as ``Thread`` and ``Connection``. To minimize serialization
cost and increase security by not allowing certain classes to be serialized, we now support class blacklisting
that will return an ``IllegalStateException`` if such a class is encountered during a checkpoint. Blacklisting supports
superclass and superinterface inheritance and always precedes ``@CordaSerializable`` annotation checking.
We've also started working on improving user experience when searching, by adding a new RPC to support fuzzy matching
of X.500 names.
Milestone 12 - First Public Beta
--------------------------------
One of our busiest releases, lots of changes that take us closer to API stability (for more detailed information about
what has changed, see :doc:`changelog`). In this release we focused mainly on making developers' lives easier. Taking
into account feedback from numerous training courses and meet-ups, we decided to add ``CollectSignaturesFlow`` which
factors out a lot of code which CorDapp developers needed to write to get their transactions signed.
The improvement is up to 150 fewer lines of code in each flow! To have your transaction signed by different parties, you
need only now call a subflow which collects the parties' signatures for you.
Additionally we introduced classpath scanning to wire-up flows automatically. Writing CorDapps has been made simpler by
removing boiler-plate code that was previously required when registering flows. Writing services such as oracles has also been simplified.
We made substantial RPC performance improvements (please note that this is separate to node performance, we are focusing
on that area in future milestones):
- 15-30k requests per second for a single client/server RPC connection.
* 1Kb requests, 1Kb responses, server and client on same machine, parallelism 8, measured on a Dell XPS 17(i7-6700HQ, 16Gb RAM)
- The framework is now multithreaded on both client and server side.
- All remaining bottlenecks are in the messaging layer.
Security of the key management service has been improved by removing support for extracting private keys, in order that
it can support use of a hardware security module (HSM) for key storage. Instead it exposes functionality for signing data
(typically transactions). The service now also supports multiple signature schemes (not just EdDSA).
We've added the beginnings of flow versioning. Nodes now reject flow requests if the initiating side is not using the same
flow version. In a future milestone release will add the ability to support backwards compatibility.
As with the previous few releases we have continued work extending identity support. There are major changes to the ``Party``
class as part of confidential identities, and how parties and keys are stored in transaction state objects.
See :doc:`changelog` for full details.
Added new Byzantine fault tolerant (BFT) decentralised notary demo, based on the `BFT-SMaRT protocol <https://bft-smart.github.io/library/>`_
For how to run the demo see: :ref:`notary-demo`
We continued to work on tools that enable diagnostics on the node. The newest addition to Corda Shell is ``flow watch`` command which
lets the administrator see all flows currently running with result or error information as well as who is the flow initiator.
Here is the view from DemoBench:
.. image:: resources/flowWatchCmd.png
We also started work on the strategic wire format (not integrated).
Milestone 11
------------
Special thank you to `Gary Rowe <https://github.com/gary-rowe>`_ for his contribution to Corda's Contracts DSL in M11.
Work has continued on confidential identities, introducing code to enable the Java standard libraries to work with
composite key signatures. This will form the underlying basis of future work to standardise the public key and signature
formats to enable interoperability with other systems, as well as enabling the use of composite signatures on X.509
certificates to prove association between transaction keys and identity keys.
The identity work will require changes to existing code and configurations, to replace party names with full X.500
distinguished names (see RFC 1779 for details on the construction of distinguished names). Currently this is not
enforced, however it will be in a later milestone.
* "myLegalName" in node configurations will need to be replaced, for example "Bank A" is replaced with
"CN=Bank A,O=Bank A,L=London,C=GB". Obviously organisation, location and country ("O", "L" and "C" respectively)
must be given values which are appropriate to the node, do not just use these example values.
* "networkMap" in node configurations must be updated to match any change to the legal name of the network map.
* 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 anticipate enforcing the use of distinguished names in node configurations from M12, and across the network from M13.
We have increased the maximum message size that we can send to Corda over RPC from 100 KB to 10 MB.
The Corda node now disables any use of ObjectInputStream to prevent Java deserialisation within flows. This is a security fix,
and prevents the node from deserialising arbitrary objects.
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.
We have revamped the DemoBench user interface. DemoBench will now also be 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.
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.
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
the node. The Corda Shell is based on the popular `CRaSH project <http://www.crashub.org/>`_ and new commands can
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.
#. 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.
#. 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.
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 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`.
Milestone 9
-----------
This release focuses on improvements to resiliency of the core infrastructure, with highlights including a Byzantine
fault tolerant (BFT) decentralised notary, based on the BFT-SMaRT protocol and isolating the web server from the
Corda node.
With thanks to open source contributor Thomas Schroeter for providing the BFT notary prototype, Corda can now resist
malicious attacks by members of a distributed notary service. If your notary service cluster has seven members, two can
become hacked or malicious simultaneously and the system continues unaffected! This work is still in development stage,
and more features are coming in the next snapshot!
The web server has been split out of the Corda node as part of our ongoing hardening of the node. We now provide a Jetty
servlet container pre-configured to contact a Corda node as a backend service out of the box, which means individual
webapps can have their REST APIs configured for the specific security environment of that app without affecting the
others, and without exposing the sensitive core of the node to malicious Javascript.
We have launched a global training programme, with two days of classes from the R3 team being hosted in London, New York
and Singapore. R3 members get 5 free places and seats are going fast, so sign up today.
We've started on support for confidential identities, based on the key randomisation techniques pioneered by the Bitcoin
and Ethereum communities. Identities may be either anonymous when a transaction is a part of a chain of custody, or fully
legally verified when a transaction is with a counterparty. Type safety is used to ensure the verification level of a
party is always clear and avoid mistakes. Future work will add support for generating new identity keys and providing a
certificate path to show ownership by the well known identity.
There are even more privacy improvements when a non-validating notary is used; the Merkle tree algorithm is used to hide
parts of the transaction that a non-validating notary doesn't need to see, whilst still allowing the decentralised
notary service to sign the entire transaction.
The serialisation API has been simplified and improved. Developers now only need to tag types that will be placed in
smart contracts or sent between parties with a single annotation... and sometimes even that isn't necessary!
Better permissioning in the cash CorDapp, to allow node users to be granted different permissions depending on whether
they manage the issuance, movement or ledger exit of cash tokens.
We've continued to improve error handling in flows, with information about errors being fed through to observing RPC
clients.
There have also been dozens of bug fixes, performance improvements and usability tweaks. Upgrading is definitely
worthwhile and will only take a few minutes for most apps.
For a full list of changes please see :doc:`changelog`.

View File

@ -64,3 +64,13 @@ To enable remote debugging of the node, run the following from the terminal wind
``java -Dcapsule.jvm.args="-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=5005" -jar corda.jar``
This command line will start the debugger on port 5005 and pause the process awaiting debugger attachment.
Starting a node with JMX monitoring enabled
-------------------------------------------
To enable export of JMX metrics over HTTP via `Jolokia <https://jolokia.org/>`_, run the following from the terminal window:
``java -Dcapsule.jvm.args="-javaagent:drivers/jolokia-jvm-1.3.7-agent.jar=port=7005" -jar corda.jar``
This command line will start the node with JMX metrics accessible via HTTP on port 7005.
See :ref:`Monitoring your node <jolokia_ref>` for further details.

View File

@ -37,7 +37,7 @@ Allowed parameters are:
:databaseProperties: database properties
:dataSourceProperties: datasoruce properties
:dataSourceProperties: datasource properties
:jiraConfig: The Jira configuration

View File

@ -1,6 +1,8 @@
Object serialization
====================
.. contents::
What is serialization (and deserialization)?
--------------------------------------------
@ -44,6 +46,8 @@ It's reproduced here as an example of both ways you can do this for a couple of
.. warning:: We will be replacing the use of Kryo in the serialization framework and so additional changes here are
likely.
.. _amqp_ref:
AMQP
====
@ -76,7 +80,7 @@ Finally, for the checkpointing of flows Corda will continue to use the existing
This separation of serialization schemes into different contexts allows us to use the most suitable framework for that context rather than
attempting to force a one size fits all approach. Where ``Kryo`` is more suited to the serialization of a programs stack frames, being more flexible
than our AMQP framework in what it can construct and serialize, that flexibility makes it exceptionally difficult to make secure. Conversly
than our AMQP framework in what it can construct and serialize, that flexibility makes it exceptionally difficult to make secure. Conversely
our AMQP framework allows us to concentrate on a robust a secure framework that can be reasoned about thus made safer with far fewer unforeseen
security holes.
@ -218,6 +222,8 @@ checked exceptions, or ``CordaRuntimeException``, for unchecked exceptions. Any
not conform to ``CordaThrowable`` will be converted to a ``CordaRuntimeException`` with the original exception type
and other properties retained within it.
.. _amqp_custom_types_ref:
Custom Types
------------
@ -282,22 +288,6 @@ serialised form
val e2 = e.serialize().deserialize() // e2.c will be 20, not 100!!!
.. warning:: Private properties in Kotlin classes render the class unserializable *unless* a public
getter is manually defined. For example:
.. container:: codeset
.. sourcecode:: kotlin
data class C(val a: Int, private val b: Int) {
// Without this C cannot be serialized
public fun getB() = b
}
.. note:: This is particularly relevant as IDE's can often point out where they believe a
property can be made private without knowing this can break Corda serialization. Should
this happen then a run time warning will be generated when the class fails to serialize
Setter Instantiation
''''''''''''''''''''
@ -330,6 +320,75 @@ For example:
public void setC(int c) { this.c = c; }
}
Inaccessible Private Properties
```````````````````````````````
Whilst the Corda AMQP serialization framework supports private object properties without publicly
accessible getter methods this development idiom is strongly discouraged.
For example.
.. container:: codeset
Kotlin:
.. sourcecode:: kotlin
data class C(val a: Int, private val b: Int)
Java:
.. sourcecode:: Java
class C {
public Integer a;
private Integer b;
C(Integer a, Integer b) {
this.a = a;
this.b = b;
}
}
When designing stateful objects is should be remembered that they are not, despite appearances, traditional
programmatic constructs. They are signed over, transformed, serialised, and relationally mapped. As such,
all elements should be publicly accessible by design
.. warning:: IDEs will indiciate erroneously that properties can be given something other than public
visibility. Ignore this as whilst it will work, as discussed above there are many reasons why this isn't
a good idea and those are beyond the scope of the IDEs inference rules
Providing a public getter, as per the following example, is acceptable
.. container:: codeset
Kotlin:
.. sourcecode:: kotlin
data class C(val a: Int, private val b: Int) {
public fun getB() = b
}
Java:
.. sourcecode:: Java
class C {
public Integer a;
private Integer b;
C(Integer a, Integer b) {
this.a = a;
this.b = b;
}
public Integer getB() {
return b;
}
}
Enums
`````

View File

@ -25,6 +25,8 @@ The shell via the local terminal
In development mode, the shell will display in the node's terminal window. It may be disabled by passing the
``--no-local-shell`` flag when running the node.
.. _ssh_server:
The shell via SSH
-----------------
The shell is also accessible via SSH.

View File

@ -109,15 +109,10 @@ See more on plugins in :doc:`running-a-node`.
Security
--------
RPC credentials associated with a Client must match the permission set configured on the server node.
This refers to both authentication (username and password) and role-based authorisation (a permissioned set of RPC operations an
This refers to both authentication (username and password) and authorisation (a permissioned set of RPC operations an
authenticated user is entitled to run).
.. note:: Permissions are represented as *String's* to allow RPC implementations to add their own permissioning. Currently
the only permission type defined is *StartFlow*, which defines a list of whitelisted flows an authenticated use may
execute. An administrator user (or a developer) may also be assigned the ``ALL`` permission, which grants access to
any flow.
In the instructions above the server node permissions are configured programmatically in the driver code:
In the instructions below the server node permissions are configured programmatically in the driver code:
.. code-block:: text
@ -125,7 +120,9 @@ In the instructions above the server node permissions are configured programmati
val user = User("user", "password", permissions = setOf(startFlow<CashFlow>()))
val node = startNode("CN=Alice Corp,O=Alice Corp,L=London,C=GB", rpcUsers = listOf(user)).get()
When starting a standalone node using a configuration file we must supply the RPC credentials as follows:
When starting a standalone node using a configuration file we must supply the RPC credentials in the node configuration,
like illustrated in the sample configuration below, or indicate an external database storing user accounts (see
:doc:`clientrpc` for more details):
.. code-block:: text
@ -133,6 +130,7 @@ When starting a standalone node using a configuration file we must supply the RP
{ username=user, password=password, permissions=[ StartFlow.net.corda.finance.flows.CashFlow ] }
]
When using the gradle Cordformation plugin to configure and deploy a node you must supply the RPC credentials in a similar
manner:

View File

@ -9,38 +9,369 @@ first public Beta (:ref:`Milestone 12 <changelog_m12>`), to :ref:`V1.0 <changelo
General rules
-------------
Always remember to update the version identifiers in your project gradle file:
Always remember to update the version identifiers in your project gradle file. For example, Corda V1.0 uses:
.. sourcecode:: shell
ext.corda_release_version = '1.0.0'
ext.corda_release_distribution = 'net.corda'
ext.corda_gradle_plugins_version = '1.0.0'
It may be necessary to update the version of major dependencies:
It may be necessary to update the version of major dependencies. Corda V1.0 uses:
.. sourcecode:: shell
ext.kotlin_version = '1.1.4'
ext.quasar_version = '0.7.9'
ext.junit_version = '4.12'
Please consult the relevant release notes of the release in question. If not specified, you may assume the
versions you are currently using are still in force.
We also strongly recommend cross referencing with the :doc:`changelog` to confirm changes.
UNRELEASED
----------
V1.0 and V2.0 to R3 Corda V3.0 Developer Preview
------------------------------------------------
Build
^^^^^
* Update the version identifiers in your project gradle file(s):
.. sourcecode:: shell
ext.corda_release_version = 'R3.CORDA-3.0.0-DEV-PREVIEW' // "CORDA-3.0.0-DEV-PREVIEW" (Open Source)
ext.corda_gradle_plugins_version = '3.0.3'
* Add a new release identifier to specify the R3 release of corda:
.. sourcecode:: shell
ext.corda_release_distribution = 'com.r3.corda' // "net.corda" for Corda (Open Source)
* Add an additional repository entry to point to the location of the R3 Corda distribution:
.. sourcecode:: shell
repositories {
maven {
credentials {
username "r3-corda-dev-preview"
password "XXXXX"
}
url 'https://ci-artifactory.corda.r3cev.com/artifactory/r3-corda-releases'
}
}
* Corda plugins have been modularised further so the following additional gradle entries are necessary:
.. sourcecode:: shell
dependencies {
classpath "net.corda.plugins:cordapp:$corda_gradle_plugins_version"
}
apply plugin: 'net.corda.plugins.cordapp'
The plugin needs to be applied in all gradle build files where there is a dependency on Corda using any of:
cordaCompile, cordaRuntime, cordapp
* Corda Gradle plugins require Gradle version 4.1 or above
* All gradle compile, test, and run-time dependencies (except gradle plugins) to Corda artifacts should now use the
``corda_release_distribution`` variable (was previously hardcoded to use ``net.corda``):
.. sourcecode:: shell
dependencies {
// Corda integration dependencies
cordaCompile "$corda_release_distribution:corda-core:$corda_release_version"
cordaCompile "$corda_release_distribution:corda-finance:$corda_release_version"
cordaCompile "$corda_release_distribution:corda-jackson:$corda_release_version"
cordaCompile "$corda_release_distribution:corda-rpc:$corda_release_version"
cordaCompile "$corda_release_distribution:corda-node-api:$corda_release_version"
cordaCompile "$corda_release_distribution:corda-webserver-impl:$corda_release_version"
cordaRuntime "$corda_release_distribution:corda:$corda_release_version"
cordaRuntime "$corda_release_distribution:corda-webserver:$corda_release_version"
testCompile "$corda_release_distribution:corda-node-driver:$corda_release_version"
}
* For existing contract ORM schemas that extend from `CommonSchemaV1.LinearState` or `CommonSchemaV1.FungibleState`,
you will need to explicitly map the `participants` collection to a database table. Previously this mapping was done in the
superclass, but that makes it impossible to properly configure the table name.
The required change is to add the ``override var participants: MutableSet<AbstractParty>? = null`` field to your class, and
add JPA mappings. For ex., see this example:
.. sourcecode:: kotlin
@Entity
@Table(name = "cash_states_v2",
indexes = arrayOf(Index(name = "ccy_code_idx2", columnList = "ccy_code")))
class PersistentCashState(
@ElementCollection
@Column(name = "participants")
@CollectionTable(name="cash_states_v2_participants", joinColumns = arrayOf(
JoinColumn(name = "output_index", referencedColumnName = "output_index"),
JoinColumn(name = "transaction_id", referencedColumnName = "transaction_id")))
override var participants: MutableSet<AbstractParty>? = null,
Configuration
^^^^^^^^^^^^^
Applies to both gradle deployNodes tasks and/or corda node configuration (node.conf).
* Remove any references to ``networkMap``.
.. sourcecode:: shell
networkMap "O=Agent,L=Dallas,C=US"
* Remove any references to ``advertisedServices`` (including notaries).
.. sourcecode:: shell
advertisedServices = ["corda.notary.validating"]
* Add an explicit notary definition in the Notary node configuration only:
.. sourcecode:: shell
notary = [validating : true]
Testing
~~~~~~~
^^^^^^^
Contract tests
~~~~~~~~~~~~~~
* You must now create a ``MockServices`` object.
``MockServices`` provides a mock identity, key and storage service. ``MockServices`` takes as its first argument a
list of the CorDapp packages to scan:
.. sourcecode:: kotlin
private val ledgerServices = MockServices(listOf("net.corda.examples.obligation", "net.corda.testing.contracts"))
``MockServices`` replaces the use of ``setCordappPackages`` and ``unsetCordappPackages``.
* ``ledger`` is now defined as a ``MockServices`` method. This means that:
.. sourcecode:: kotlin
ledger {
Becomes:
.. sourcecode:: kotlin
ledgerServices.ledger {
* Within a mock ledger transaction, ``ContractState`` instances are passed to ``input`` and ``output`` as objects
rather than lambdas. For example:
.. sourcecode:: kotlin
ledgerServices.ledger {
transaction {
input(OBLIGATION_CONTRACT_ID, DummyState())
output(OBLIGATION_CONTRACT_ID, oneDollarObligation)
}
}
* Within a mock ledger transaction, ``CommandData`` instances are passed to ``input`` and ``output`` as objects
rather than lambdas, and the public keys must be passed as a list if there is more than one. For example:
.. sourcecode:: kotlin
ledgerServices.ledger {
transaction {
command(alice.publicKey, ObligationContract.Commands.Issue())
command(listOf(alice.publicKey, bob.publicKey), ObligationContract.Commands.Issue())
}
}
* The predefined test identities (e.g. ``ALICE`` and ``MINI_CORP``) have been removed.
You must now define the test identities explicitly. For example:
.. sourcecode:: kotlin
val alice = TestIdentity(CordaX500Name(organisation = "Alice", locality = "TestLand", country = "GB"))
``TestIdentity`` exposes methods to get the ``name``, ``keyPair``, ``publicKey``, ``party`` and ``identity`` of the
underlying ``TestIdentity``
* Explicit invocation of transaction transformation (ie. using ``TransactionBuilder``) requires serialization engine
to be initialized. In unit test this can be achieved by using the following jUnit rule:
.. sourcecode:: kotlin
@Rule
@JvmField
val testSerialization = SerializationEnvironmentRule()
Flow tests
~~~~~~~~~~
* The registration mechanism for CorDapps in ``MockNetwork`` unit tests has changed.
It is now done via the ``cordappPackages`` constructor parameter of MockNetwork.
This takes a list of `String` values which should be the
package names of the CorDapps containing the contract verification code you wish to load.
It is now done via the ``cordappPackages`` constructor parameter of MockNetwork. This takes a list of ``String``
values which should be the package names of the CorDapps containing the contract verification code you wish to load.
The ``unsetCordappPackages`` method is now redundant and has been removed.
* Creation of Notaries in ``MockNetwork`` unit tests has changed.
Previously the API call ``createNotaryNode(legalName = CordaX500ame(...))`` would be used to create a notary:
.. sourcecode:: kotlin
val notary = mockNetwork.createNotaryNode(legalName = CordaX500Name("Notary", "London", "UK"))
Notaries are now defined as part of ``MockNetwork`` creation using ``notarySpecs``, as in the following example:
.. sourcecode:: kotlin
mockNetwork = MockNetwork(notarySpecs = listOf(MockNetwork.NotarySpec(CordaX500Name("Notary","London","UK"))))
* A notary is no longer specified when creating a standard node using the ``createPartyNode`` API call.
Previously:
.. sourcecode:: kotlin
mockNetwork.createPartyNode(notary.network.myAddress, CordaX500Name("Node", "Madrid", "ES"))
Becomes:
.. sourcecode:: kotlin
mockNetwork.createPartyNode(CordaX500Name("Node", "Madrid", "ES"))
* Utility node creation API method ``createSomeNodes(...)`` has been removed, and nodes must be created individually.
Previously:
.. sourcecode:: java
MockNetwork.BasketOfNodes nodes = net.createSomeNodes(3);
nodeA = nodes.getPartyNodes().get(0);
nodeB = nodes.getPartyNodes().get(1);
nodeC = nodes.getPartyNodes().get(2);
Becomes:
.. sourcecode:: java
nodeA = net.createNode(new MockNodeParameters());
nodeB = net.createNode(new MockNodeParameters());
nodeC = net.createNode(new MockNodeParameters());
List<StartedNode<MockNode>> nodes = Arrays.asList(nodeA, nodeB, nodeC);
* Flow framework instantiation of a flow has a slight variation in start syntax:
Previously:
.. sourcecode:: java
CordaFuture<SignedTransaction> future = nodeA.getServices().startFlow(flow).getResultFuture();
Becomes:
.. sourcecode:: java
CordaFuture<SignedTransaction> future = startFlow(nodeA.getServices(), flow).getResultFuture();
* ``StartedNodeServices.startFlow`` must now be imported from ``net.corda.testing.node``
* Do not use ``node.internals`` to register flows:
Previous code would often look as follows:
.. sourcecode:: kotlin
protected fun registerFlowsAndServices(node: StartedNode<MockNetwork.MockNode>) {
val mockNode = node.internals
mockNode.registerInitiatedFlow(MyCustomFlow::class.java)
}
Becomes:
.. sourcecode:: kotlin
protected fun registerFlowsAndServices(mockNode: StartedNode<MockNetwork.MockNode>) {
mockNode.registerInitiatedFlow(MyCustomFlow::class.java)
}
* Do not use ``node.internals`` to register Corda services
Previously:
.. sourcecode:: kotlin
node.internals.installCordaService(CustomService::class.java)
Becomes:
.. sourcecode:: kotlin
node.services.cordaService(CustomService::class.java)
Better yet, use node factory to organize both register flows and services, for example, create class as follows:
.. sourcecode:: kotlin
class PrimesOracleNode(args: MockNodeArgs) : MockNetwork.MockNode(args) {
override fun start() = super.start().apply {
registerInitiatedFlow(QueryHandler::class.java)
registerInitiatedFlow(SignHandler::class.java)
services.cordaService(net.corda.examples.oracle.service.service.Oracle::class.java)
}
}
and then pass it to ``createNode``:
.. sourcecode:: kotlin
val oracle = mockNet.createNode(MockNodeParameters(legalName = CordaX500Name("Oracle", "New York", "US")), ::PrimesOracleNode)
Node driver
~~~~~~~~~~~
* ``User`` has been moved from ``net.corda.nodeapi.User`` to ``net.corda.nodeapi.internal.config.User``
* Notaries are defined by passing a list of ``NotarySpec`` objects to ``driver`` using the ``notarySpecs`` argument,
instead of being defined manually in the driver block.
``notarySpecs`` defaults to providing a single validating notary
* The ``waitForAllNodesToFinish`` function has been removed. It has been replaced with a ``waitForAllNodesToFinish``
argument to ``driver``
* No longer specify advertised services to the ``DriverDSL`` when starting nodes:
Previously:
.. sourcecode:: kotlin
driver {
startNode(providedName = CordaX500Name("Controller", "London", "GB"), advertisedServices = setOf(ServiceInfo(ValidatingNotaryService.type)))
Becomes:
.. sourcecode:: kotlin
driver {
startNode(providedName = CordaX500Name("Controller", "London", "GB")),
Finance
^^^^^^^
* ``CASH_PROGRAM_ID`` has been moved to ``Cash.PROGRAM_ID``, where ``Cash`` is defined in the
``import net.corda.finance.contracts.asset`` package
V1.0 to V2.0
------------
@ -63,14 +394,14 @@ Build
* MockNetwork has moved.
A new test driver module dependency needs to be including in your project: `corda-node-driver`. To continue using the
A new test driver module dependency needs to be including in your project: ``corda-node-driver``. To continue using the
mock network for testing, add the following entry to your gradle build file:
.. sourcecode:: shell
testCompile "net.corda:corda-node-driver:$corda_release_version"
testCompile "$corda_release_distribution:corda-node-driver:$corda_release_version"
.. note:: you may only need `testCompile "net.corda:corda-test-utils:$corda_release_version"` if not using the Driver DSL.
.. note:: you may only need `testCompile "$corda_release_distribution:corda-test-utils:$corda_release_version"` if not using the Driver DSL.
Configuration
^^^^^^^^^^^^^
@ -91,7 +422,7 @@ Use the automatic imports feature of IntelliJ to intelligently resolve the new i
* Missing imports for contract types.
CommercialPaper and Cash are now contained within the `finance` module, as are associated helpers functions.
CommercialPaper and Cash are now contained within the ``finance`` module, as are associated helpers functions.
For example:
``import net.corda.contracts.ICommercialPaperState`` becomes ``import net.corda.finance.contracts.ICommercialPaperState``
@ -105,7 +436,7 @@ Use the automatic imports feature of IntelliJ to intelligently resolve the new i
* Missing imports for utility functions.
Many common types and helper methods have been consolidated into `net.corda.core.utilities` package.
Many common types and helper methods have been consolidated into ``net.corda.core.utilities`` package.
For example:
``import net.corda.core.crypto.commonName`` becomes ``import net.corda.core.utilities.commonName``
@ -170,7 +501,7 @@ Flow framework
Node services (ServiceHub)
^^^^^^^^^^^^^^^^^^^^^^^^^^
* VaultQueryService: unresolved reference to `vaultQueryService`.
* VaultQueryService: unresolved reference to ``vaultQueryService``.
Replace all references to ``<services>.vaultQueryService`` with ``<services>.vaultService``.
Previously there were two vault APIs. Now there is a single unified API with the same functions: ``VaultService``.
@ -191,7 +522,7 @@ Node services (ServiceHub)
RPC Client
^^^^^^^^^^
* Missing API methods on `CordaRPCOps` interface.
* Missing API methods on ``CordaRPCOps`` interface.
* Calls to ``verifiedTransactionsFeed()`` and ``verifiedTransactions()`` have been replaced with:
``internalVerifiedTransactionsSnapshot()`` and ``internalVerifiedTransactionsFeed()`` respectively
@ -199,7 +530,7 @@ RPC Client
This is in preparation for the planned integration of Intel SGX™, which will encrypt the transactions feed.
Apps that use this API will not work on encrypted ledgers: you should probably be using the vault query API instead.
* Accessing the `networkMapCache` via ``services.nodeInfo().legalIdentities`` returns a list of identities.
* Accessing the ``networkMapCache`` via ``services.nodeInfo().legalIdentities`` returns a list of identities.
The first element in the list is the Party object referring to a node's single identity.
This is in preparation for allowing a node to host multiple separate identities in future.
@ -207,15 +538,15 @@ RPC Client
Testing
^^^^^^^
Please note that `Clauses` have been removed completely as of V1.0.
Please note that ``Clauses`` have been removed completely as of V1.0.
We will be revisiting this capability in a future release.
* CorDapps must be explicitly registered in ``MockNetwork`` unit tests.
This is done by calling ``setCordappPackages``, an extension helper function in the ``net.corda.testing`` package,
on the first line of your `@Before` method. This takes a variable number of `String` arguments which should be the
on the first line of your ``@Before`` method. This takes a variable number of ``String`` arguments which should be the
package names of the CorDapps containing the contract verification code you wish to load.
You should unset CorDapp packages in your `@After` method by using ``unsetCordappPackages()`` after `stopNodes()`.
You should unset CorDapp packages in your ``@After`` method by using ``unsetCordappPackages()`` after ``stopNodes()``.
* CorDapps must be explicitly registered in ``DriverDSL`` and ``RPCDriverDSL`` integration tests.
@ -225,7 +556,7 @@ We will be revisiting this capability in a future release.
Finance
^^^^^^^
* `FungibleAsset` interface simplification.
* ``FungibleAsset`` interface simplification.
The ``FungibleAsset`` interface has been made simpler. The ``Commands`` grouping interface
that included the ``Move``, ``Issue`` and ``Exit`` interfaces have all been removed, while the ``move`` function has
@ -233,10 +564,10 @@ Finance
The following errors may be reported:
* override nothing (FungibleAsset): `move`
* not a subtype of overridden FungibleAsset: `withNewOwner`
* no longer need to override `override val contractHash: SecureHash? = null`
* need to override `override val contract: Class<out Contract>? = null`
* override nothing (FungibleAsset): ``move``
* not a subtype of overridden FungibleAsset: ``withNewOwner``
* no longer need to override ``override val contractHash: SecureHash? = null``
* need to override ``override val contract: Class<out Contract>? = null``
Miscellaneous
@ -266,7 +597,7 @@ Gotchas
Core data structures
^^^^^^^^^^^^^^^^^^^^
* `TransactionBuilder` changes.
* ``TransactionBuilder`` changes.
Use convenience class ``StateAndContract`` instead of ``TransactionBuilder.withItems()`` for passing
around a state and its contract.
@ -274,7 +605,7 @@ Core data structures
* Transaction building DSL changes:
* now need to explicitly pass the ContractClassName into all inputs and outputs.
* `ContractClassName` refers to the class containing the “verifier” method.
* ``ContractClassName`` refers to the class containing the “verifier” method.
* Contract verify method signature change.
@ -344,15 +675,15 @@ Build
* Gradle dependency reference changes.
Module name has changed to include `corda` in the artifacts jar name:
Module name has changed to include ``corda`` in the artifacts jar name:
.. sourcecode:: shell
compile "net.corda:core:$corda_release_version" -> compile "net.corda:corda-core:$corda_release_version"
compile "net.corda:finance:$corda_release_version" -> compile "net.corda:corda-finance:$corda_release_version"
compile "net.corda:jackson:$corda_release_version" -> compile "net.corda:corda-jackson:$corda_release_version"
compile "net.corda:node:$corda_release_version" -> compile "net.corda:corda-node:$corda_release_version"
compile "net.corda:rpc:$corda_release_version" -> compile "net.corda:corda-rpc:$corda_release_version"
compile "$corda_release_distribution:core:$corda_release_version" -> compile "$corda_release_distribution:corda-core:$corda_release_version"
compile "$corda_release_distribution:finance:$corda_release_version" -> compile "$corda_release_distribution:corda-finance:$corda_release_version"
compile "$corda_release_distribution:jackson:$corda_release_version" -> compile "$corda_release_distribution:corda-jackson:$corda_release_version"
compile "$corda_release_distribution:node:$corda_release_version" -> compile "$corda_release_distribution:corda-node:$corda_release_version"
compile "$corda_release_distribution:rpc:$corda_release_version" -> compile "$corda_release_distribution:corda-rpc:$corda_release_version"
Node services (ServiceHub)
^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -367,10 +698,10 @@ Finance
* Financial asset contracts (Cash, CommercialPaper, Obligations) are now a standalone CorDapp within the **finance** module.
Need to import from respective package within `finance` module:
Need to import from respective package within ``finance`` module:
eg. ``net.corda.finance.contracts.asset.Cash``
Likewise, need to import associated asset flows from respective package within `finance` module:
Likewise, need to import associated asset flows from respective package within ``finance`` module:
eg. ``net.corda.finance.flows.CashIssueFlow``
``net.corda.finance.flows.CashIssueAndPaymentFlow``
``net.corda.finance.flows.CashExitFlow``
@ -378,4 +709,4 @@ Finance
* Moved ``finance`` gradle project files into a ``net.corda.finance`` package namespace.
This may require adjusting imports of Cash flow references and also of ``StartFlow`` permission in ``gradle.build`` files.
Associated flows (`Cash*Flow`, `TwoPartyTradeFlow`, `TwoPartyDealFlow`) must now be imported from this package.
Associated flows (``Cash*Flow``, ``TwoPartyTradeFlow``, ``TwoPartyDealFlow``) must now be imported from this package.

View File

@ -60,9 +60,9 @@ class CordappPlugin : Plugin<Project> {
excludes.none { exclude -> (exclude["group"] == dep.group) && (exclude["name"] == dep.name) }
}
filteredDeps.forEach {
// net.corda or com.r3.corda.enterprise may be a core dependency which shouldn't be included in this cordapp so give a warning
// net.corda or com.r3.corda may be a core dependency which shouldn't be included in this cordapp so give a warning
val group = it.group?.toString() ?: ""
if (group.startsWith("net.corda.") || group.startsWith("com.r3.corda.enterprise.")) {
if (group.startsWith("net.corda.") || group.startsWith("com.r3.corda.")) {
project.logger.warn("You appear to have included a Corda platform component ($it) using a 'compile' or 'runtime' dependency." +
"This can cause node stability problems. Please use 'corda' instead." +
"See http://docs.corda.net/cordapp-build-systems.html")

View File

@ -55,7 +55,8 @@ class Cordformation : Plugin<Project> {
override fun apply(project: Project) {
Utils.createCompileConfiguration("cordapp", project)
Utils.createRuntimeConfiguration(CORDFORMATION_TYPE, project)
val jolokiaVersion = project.rootProject.ext<String>("jolokia_version")
// TODO: improve how we re-use existing declared external variables from root gradle.build
val jolokiaVersion = try { project.rootProject.ext<String>("jolokia_version") } catch (e: Exception) { "1.3.7" }
project.dependencies.add(CORDFORMATION_TYPE, "org.jolokia:jolokia-jvm:$jolokiaVersion:agent")
}
}

View File

@ -128,7 +128,8 @@ class Node(private val project: Project) : CordformNode() {
* Installs the jolokia monitoring agent JAR to the node/drivers directory
*/
private fun installAgentJar() {
val jolokiaVersion = project.rootProject.ext<String>("jolokia_version")
// TODO: improve how we re-use existing declared external variables from root gradle.build
val jolokiaVersion = try { project.rootProject.ext<String>("jolokia_version") } catch (e: Exception) { "1.3.7" }
val agentJar = project.configuration("runtime").files {
(it.group == "org.jolokia") &&
(it.name == "jolokia-jvm") &&

View File

@ -28,12 +28,12 @@ To run the HSM signing server:
```
cd network-management
java -jar capsule-hsm/build/libs/hsm-<version>.jar --config-file hsm.conf
java -jar capsule-hsm/build/libs/hsm-<version>.jar --configFile hsm.conf
```
For a list of options the HSM signing server takes, run with the `--help` option:
```
java -jar capsule-hsm/build/libs/hsm-3.0-<version>.jar --help
java -jar capsule-hsm/build/libs/hsm-<version>.jar --help
```
#Configuring network management service

View File

@ -145,7 +145,7 @@ class PersistentCertificateRequestStorage(private val database: CordaPersistence
// Also, at the moment we assume that once the CSR is approved it cannot be rejected.
// What if we approved something by mistake.
val duplicates = session.createQuery(query).resultList.filter {
it.status != RequestStatus.REJECTED
it.status !=RequestStatus.REJECTED
}
return Pair(legalName, if (duplicates.isEmpty()) null else "Duplicate legal name")

View File

@ -41,7 +41,9 @@ class NodeInfoWebService(private val nodeInfoStorage: NodeInfoStorage,
private val networkMapCache: LoadingCache<Boolean, Pair<SignedNetworkMap?, NetworkParameters?>> = CacheBuilder.newBuilder()
.expireAfterWrite(config.cacheTimeout, TimeUnit.MILLISECONDS)
.build(CacheLoader.from { _ -> Pair(networkMapStorage.getCurrentNetworkMap(), networkMapStorage.getCurrentNetworkParameters()) })
.build(CacheLoader.from { _ ->
Pair(networkMapStorage.getCurrentNetworkMap()
, networkMapStorage.getCurrentNetworkParameters()) })
@POST
@Path("publish")
@ -60,7 +62,7 @@ class NodeInfoWebService(private val nodeInfoStorage: NodeInfoStorage,
is NetworkMapNotInitialisedException -> status(Response.Status.SERVICE_UNAVAILABLE).entity(e.message)
is InvalidPlatformVersionException -> status(Response.Status.BAD_REQUEST).entity(e.message)
is IllegalArgumentException, is InvalidKeyException, is SignatureException -> status(Response.Status.UNAUTHORIZED).entity(e.message)
// Rethrow e if its not one of the expected exception, the server will return http 500 internal error.
// Rethrow e if its not one of the expected exception, the server will return http 500 internal error.
else -> throw e
}
}.build()

View File

@ -61,7 +61,7 @@ data class Parameters(val dataSourceProperties: Properties,
fun parseParameters(vararg args: String): Parameters {
val argConfig = args.toConfigWithOptions {
accepts("basedir", "Overriding configuration filepath, default to current directory.").withRequiredArg().defaultsTo(".").describedAs("filepath")
accepts("config-file", "Overriding configuration file.").withRequiredArg().describedAs("filepath")
accepts("configFile", "Overriding configuration file.").withRequiredArg().defaultsTo("node.conf").describedAs("filepath")
}
// The config-file option is changed to configFile

View File

@ -40,6 +40,7 @@
<constraints nullable="false"/>
</column>
<column name="certificate_path_bytes" type="BLOB"/>
<column name="certificate_status" type="INT"/>
<column name="public_key_hash" type="NVARCHAR(64)"/>
<column name="certificate_signing_request" type="NVARCHAR(64)">

View File

@ -12,6 +12,7 @@ import net.corda.nodeapi.internal.crypto.CertificateAndKeyPair
import net.corda.nodeapi.internal.network.NetworkMap
import net.corda.nodeapi.internal.network.SignedNetworkMap
import net.corda.testing.common.internal.testNetworkParameters
import net.corda.testing.common.internal.testNetworkParameters
import net.corda.testing.internal.createDevIntermediateCaCertPath
import org.junit.Before
import org.junit.Test

View File

@ -7,8 +7,6 @@ import net.corda.core.crypto.random63BitValue
import net.corda.core.internal.CertRole
import net.corda.core.internal.reader
import net.corda.core.internal.writer
import net.corda.core.identity.CordaX500Name
import net.corda.core.internal.*
import net.corda.core.utilities.days
import net.corda.core.utilities.millis
import org.bouncycastle.asn1.*

View File

@ -96,7 +96,7 @@ class EvolutionSerializer(
old.fields.forEach {
val returnType = it.getTypeAsClass(factory.classloader)
oldArgs[it.name] = OldParam(
returnType, idx++, PropertySerializer.make(it.name, null, returnType, factory))
returnType, idx++, PropertySerializer.make(it.name, PublicPropertyReader(null), returnType, factory))
}
val readers = constructor.parameters.map {

View File

@ -26,6 +26,8 @@ open class ObjectSerializer(val clazz: Type, factory: SerializerFactory) : AMQPS
propertiesForSerialization(kotlinConstructor, clazz, factory)
}
fun getPropertySerializers() = propertySerializers
private val typeName = nameForType(clazz)
override val typeDescriptor = Symbol.valueOf("$DESCRIPTOR_DOMAIN:${fingerprintForType(type, factory)}")

View File

@ -1,17 +1,74 @@
package net.corda.nodeapi.internal.serialization.amqp
import net.corda.core.utilities.contextLogger
import net.corda.core.utilities.loggerFor
import org.apache.qpid.proton.amqp.Binary
import org.apache.qpid.proton.codec.Data
import java.lang.reflect.Method
import java.lang.reflect.Type
import java.lang.reflect.Field
import kotlin.reflect.full.memberProperties
import kotlin.reflect.jvm.javaGetter
import kotlin.reflect.jvm.kotlinProperty
abstract class PropertyReader {
abstract fun read(obj: Any?): Any?
abstract fun isNullable(): Boolean
}
class PublicPropertyReader(private val readMethod: Method?) : PropertyReader() {
init {
readMethod?.isAccessible = true
}
private fun Method.returnsNullable(): Boolean {
try {
val returnTypeString = this.declaringClass.kotlin.memberProperties.firstOrNull { it.javaGetter == this }?.returnType?.toString() ?: "?"
return returnTypeString.endsWith('?') || returnTypeString.endsWith('!')
} catch (e: kotlin.reflect.jvm.internal.KotlinReflectionInternalError) {
// This might happen for some types, e.g. kotlin.Throwable? - the root cause of the issue is: https://youtrack.jetbrains.com/issue/KT-13077
// TODO: Revisit this when Kotlin issue is fixed.
loggerFor<PropertySerializer>().error("Unexpected internal Kotlin error", e)
return true
}
}
override fun read(obj: Any?): Any? {
return readMethod!!.invoke(obj)
}
override fun isNullable(): Boolean = readMethod?.returnsNullable() ?: false
}
class PrivatePropertyReader(val field: Field, parentType: Type) : PropertyReader() {
init {
loggerFor<PropertySerializer>().warn("Create property Serializer for private property '${field.name}' not "
+ "exposed by a getter on class '$parentType'\n"
+ "\tNOTE: This behaviour will be deprecated at some point in the future and a getter required")
}
override fun read(obj: Any?): Any? {
field.isAccessible = true
val rtn = field.get(obj)
field.isAccessible = false
return rtn
}
override fun isNullable() = try {
field.kotlinProperty?.returnType?.isMarkedNullable ?: false
} catch (e: kotlin.reflect.jvm.internal.KotlinReflectionInternalError) {
// This might happen for some types, e.g. kotlin.Throwable? - the root cause of the issue is: https://youtrack.jetbrains.com/issue/KT-13077
// TODO: Revisit this when Kotlin issue is fixed.
loggerFor<PropertySerializer>().error("Unexpected internal Kotlin error", e)
true
}
}
/**
* Base class for serialization of a property of an object.
*/
sealed class PropertySerializer(val name: String, val readMethod: Method?, val resolvedType: Type) {
sealed class PropertySerializer(val name: String, val propertyReader: PropertyReader, val resolvedType: Type) {
abstract fun writeClassInfo(output: SerializationOutput)
abstract fun writeProperty(obj: Any?, data: Data, output: SerializationOutput)
abstract fun readProperty(obj: Any?, schemas: SerializationSchemas, input: DeserializationInput): Any?
@ -44,25 +101,11 @@ sealed class PropertySerializer(val name: String, val readMethod: Method?, val r
}
private fun generateMandatory(): Boolean {
return isJVMPrimitive || readMethod?.returnsNullable() == false
}
private fun Method.returnsNullable(): Boolean {
try {
val returnTypeString = this.declaringClass.kotlin.memberProperties.firstOrNull { it.javaGetter == this }?.returnType?.toString() ?: "?"
return returnTypeString.endsWith('?') || returnTypeString.endsWith('!')
} catch (e: kotlin.reflect.jvm.internal.KotlinReflectionInternalError) {
// This might happen for some types, e.g. kotlin.Throwable? - the root cause of the issue is: https://youtrack.jetbrains.com/issue/KT-13077
// TODO: Revisit this when Kotlin issue is fixed.
logger.error("Unexpected internal Kotlin error", e)
return true
}
return isJVMPrimitive || !(propertyReader.isNullable())
}
companion object {
private val logger = contextLogger()
fun make(name: String, readMethod: Method?, resolvedType: Type, factory: SerializerFactory): PropertySerializer {
readMethod?.isAccessible = true
fun make(name: String, readMethod: PropertyReader, resolvedType: Type, factory: SerializerFactory): PropertySerializer {
if (SerializerFactory.isPrimitive(resolvedType)) {
return when (resolvedType) {
Char::class.java, Character::class.java -> AMQPCharPropertySerializer(name, readMethod)
@ -78,7 +121,8 @@ sealed class PropertySerializer(val name: String, val readMethod: Method?, val r
* A property serializer for a complex type (another object).
*/
class DescribedTypePropertySerializer(
name: String, readMethod: Method?,
name: String,
readMethod: PropertyReader,
resolvedType: Type,
private val lazyTypeSerializer: () -> AMQPSerializer<*>) : PropertySerializer(name, readMethod, resolvedType) {
// This is lazy so we don't get an infinite loop when a method returns an instance of the class.
@ -90,12 +134,15 @@ sealed class PropertySerializer(val name: String, val readMethod: Method?, val r
}
}
override fun readProperty(obj: Any?, schemas: SerializationSchemas, input: DeserializationInput): Any? = ifThrowsAppend({ nameForDebug }) {
override fun readProperty(
obj: Any?,
schemas: SerializationSchemas,
input: DeserializationInput): Any? = ifThrowsAppend({ nameForDebug }) {
input.readObjectOrNull(obj, schemas, resolvedType)
}
override fun writeProperty(obj: Any?, data: Data, output: SerializationOutput) = ifThrowsAppend({ nameForDebug }) {
output.writeObjectOrNull(readMethod!!.invoke(obj), data, resolvedType)
output.writeObjectOrNull(propertyReader.read(obj), data, resolvedType)
}
private val nameForDebug = "$name(${resolvedType.typeName})"
@ -104,7 +151,10 @@ sealed class PropertySerializer(val name: String, val readMethod: Method?, val r
/**
* A property serializer for most AMQP primitive type (Int, String, etc).
*/
class AMQPPrimitivePropertySerializer(name: String, readMethod: Method?, resolvedType: Type) : PropertySerializer(name, readMethod, resolvedType) {
class AMQPPrimitivePropertySerializer(
name: String,
readMethod: PropertyReader,
resolvedType: Type) : PropertySerializer(name, readMethod, resolvedType) {
override fun writeClassInfo(output: SerializationOutput) {}
override fun readProperty(obj: Any?, schemas: SerializationSchemas, input: DeserializationInput): Any? {
@ -112,7 +162,7 @@ sealed class PropertySerializer(val name: String, val readMethod: Method?, val r
}
override fun writeProperty(obj: Any?, data: Data, output: SerializationOutput) {
val value = readMethod!!.invoke(obj)
val value = propertyReader.read(obj)
if (value is ByteArray) {
data.putObject(Binary(value))
} else {
@ -126,7 +176,7 @@ sealed class PropertySerializer(val name: String, val readMethod: Method?, val r
* value of the character is stored in numeric UTF-16 form and on deserialisation requires explicit
* casting back to a char otherwise it's treated as an Integer and a TypeMismatch occurs
*/
class AMQPCharPropertySerializer(name: String, readMethod: Method?) :
class AMQPCharPropertySerializer(name: String, readMethod: PropertyReader) :
PropertySerializer(name, readMethod, Character::class.java) {
override fun writeClassInfo(output: SerializationOutput) {}
@ -135,7 +185,7 @@ sealed class PropertySerializer(val name: String, val readMethod: Method?, val r
}
override fun writeProperty(obj: Any?, data: Data, output: SerializationOutput) {
val input = readMethod!!.invoke(obj)
val input = propertyReader.read(obj)
if (input != null) data.putShort((input as Char).toShort()) else data.putNull()
}
}

View File

@ -20,6 +20,7 @@ import kotlin.reflect.full.findAnnotation
import kotlin.reflect.full.primaryConstructor
import kotlin.reflect.jvm.isAccessible
import kotlin.reflect.jvm.javaType
import kotlin.reflect.jvm.kotlinProperty
/**
* Annotation indicating a constructor to be used to reconstruct instances of a class during deserialization.
@ -29,8 +30,8 @@ import kotlin.reflect.jvm.javaType
annotation class ConstructorForDeserialization
data class ConstructorDestructorMethods(
val getters : Collection<PropertySerializer>,
val setters : Collection<Method?>)
val getters: Collection<PropertySerializer>,
val setters: Collection<Method?>)
/**
* Code for finding the constructor we will use for deserialization.
@ -100,26 +101,31 @@ private fun <T : Any> propertiesForSerializationFromConstructor(
for (param in kotlinConstructor.parameters) {
val name = param.name ?: throw NotSerializableException("Constructor parameter of $clazz has no name.")
val matchingProperty = properties[name] ?:
try {
clazz.getDeclaredField(param.name)
throw NotSerializableException("Property '$name' or its getter is non public, this renders class '$clazz' unserializable")
} catch (e: NoSuchFieldException) {
throw NotSerializableException("No property matching constructor parameter named '$name' of '$clazz'. " +
"If using Java, check that you have the -parameters option specified in the Java compiler. " +
"Alternately, provide a proxy serializer (SerializationCustomSerializer) if recompiling isn't an option")
}
if (name in properties) {
val matchingProperty = properties[name]!!
// Check that the method has a getter in java.
val getter = matchingProperty.readMethod ?: throw NotSerializableException("Property has no getter method for $name of $clazz. " +
"If using Java and the parameter name looks anonymous, check that you have the -parameters option specified in the Java compiler." +
"Alternately, provide a proxy serializer (SerializationCustomSerializer) if recompiling isn't an option")
val returnType = resolveTypeVariables(getter.genericReturnType, type)
if (constructorParamTakesReturnTypeOfGetter(returnType, getter.genericReturnType, param)) {
rc += PropertySerializer.make(name, getter, returnType, factory)
// Check that the method has a getter in java.
val getter = matchingProperty.readMethod ?: throw NotSerializableException("Property has no getter method for $name of $clazz. " +
"If using Java and the parameter name looks anonymous, check that you have the -parameters option specified in the Java compiler." +
"Alternately, provide a proxy serializer (SerializationCustomSerializer) if recompiling isn't an option")
val returnType = resolveTypeVariables(getter.genericReturnType, type)
if (constructorParamTakesReturnTypeOfGetter(returnType, getter.genericReturnType, param)) {
rc += PropertySerializer.make(name, PublicPropertyReader(getter), returnType, factory)
} else {
throw NotSerializableException("Property type $returnType for $name of $clazz differs from constructor parameter type ${param.type.javaType}")
}
} else {
throw NotSerializableException("Property type $returnType for $name of $clazz differs from constructor parameter type ${param.type.javaType}")
try {
val field = (clazz.getDeclaredField(param.name))
rc += PropertySerializer.make(name, PrivatePropertyReader(field, type), field.genericType, factory)
} catch (e: NoSuchFieldException) {
throw NotSerializableException("No property matching constructor parameter named '$name' of '$clazz'. " +
"If using Java, check that you have the -parameters option specified in the Java compiler. " +
"Alternately, provide a proxy serializer (SerializationCustomSerializer) if recompiling isn't an option")
}
}
}
return ConstructorDestructorMethods(rc, emptyList())
@ -130,11 +136,11 @@ private fun <T : Any> propertiesForSerializationFromConstructor(
* and use those
*/
private fun propertiesForSerializationFromSetters(
properties : Map<String, PropertyDescriptor>,
properties: Map<String, PropertyDescriptor>,
type: Type,
factory: SerializerFactory): ConstructorDestructorMethods {
val getters : MutableList<PropertySerializer> = ArrayList(properties.size)
val setters : MutableList<Method?> = ArrayList(properties.size)
val getters: MutableList<PropertySerializer> = ArrayList(properties.size)
val setters: MutableList<Method?> = ArrayList(properties.size)
properties.forEach { property ->
val getter: Method? = property.value.readMethod
@ -146,8 +152,8 @@ private fun propertiesForSerializationFromSetters(
// the getter / setter vs property as if there is a difference then that property isn't reported
// by the BEAN inspector and thus we don't consider that case here
getters += PropertySerializer.make(property.key, getter, resolveTypeVariables(getter.genericReturnType, type),
factory)
getters += PropertySerializer.make(property.key, PublicPropertyReader(getter),
resolveTypeVariables(getter.genericReturnType, type), factory)
setters += setter
}
@ -159,15 +165,22 @@ private fun constructorParamTakesReturnTypeOfGetter(getterReturnType: Type, rawG
return typeToken.isSupertypeOf(getterReturnType) || typeToken.isSupertypeOf(rawGetterReturnType)
}
private fun propertiesForSerializationFromAbstract(clazz: Class<*>, type: Type, factory: SerializerFactory): ConstructorDestructorMethods {
private fun propertiesForSerializationFromAbstract(
clazz: Class<*>,
type: Type,
factory: SerializerFactory): ConstructorDestructorMethods {
// Kotlin reflection doesn't work with Java getters the way you might expect, so we drop back to good ol' beans.
val properties = Introspector.getBeanInfo(clazz).propertyDescriptors.filter { it.name != "class" }.sortedBy { it.name }.filterNot { it is IndexedPropertyDescriptor }
val properties = Introspector.getBeanInfo(clazz).propertyDescriptors
.filter { it.name != "class" }
.sortedBy { it.name }
.filterNot { it is IndexedPropertyDescriptor }
val rc: MutableList<PropertySerializer> = ArrayList(properties.size)
for (property in properties) {
// Check that the method has a getter in java.
val getter = property.readMethod ?: throw NotSerializableException("Property has no getter method for ${property.name} of $clazz.")
val getter = property.readMethod ?: throw NotSerializableException(
"Property has no getter method for ${property.name} of $clazz.")
val returnType = resolveTypeVariables(getter.genericReturnType, type)
rc += PropertySerializer.make(property.name, getter, returnType, factory)
rc += PropertySerializer.make(property.name, PublicPropertyReader(getter), returnType, factory)
}
return ConstructorDestructorMethods(rc, emptyList())
}

View File

@ -40,6 +40,7 @@ open class SerializerFactory(val whitelist: ClassWhitelist, cl: ClassLoader) {
val transformsCache = ConcurrentHashMap<String, EnumMap<TransformTypes, MutableList<Transform>>>()
open val classCarpenter = ClassCarpenter(cl, whitelist)
val classloader: ClassLoader
get() = classCarpenter.classloader
@ -381,3 +382,4 @@ open class SerializerFactory(val whitelist: ClassWhitelist, cl: ClassLoader) {
override fun toString(): String = "?"
}
}

View File

@ -25,7 +25,7 @@ class ThrowableSerializer(factory: SerializerFactory) : CustomSerializer.Proxy<T
val constructor = constructorForDeserialization(obj.javaClass)
val props = propertiesForSerialization(constructor, obj.javaClass, factory)
for (prop in props.getters) {
extraProperties[prop.name] = prop.readMethod!!.invoke(obj)
extraProperties[prop.name] = prop.propertyReader.read(obj)
}
} catch (e: NotSerializableException) {
logger.warn("Unexpected exception", e)

View File

@ -0,0 +1,79 @@
package net.corda.nodeapi.internal.serialization.amqp;
import net.corda.nodeapi.internal.serialization.AllWhitelist;
import org.junit.Test;
import static org.junit.Assert.*;
import java.io.NotSerializableException;
import java.lang.reflect.Field;
import java.util.concurrent.ConcurrentHashMap;
public class JavaPrivatePropertyTests {
static class C {
private String a;
C(String a) { this.a = a; }
}
static class C2 {
private String a;
C2(String a) { this.a = a; }
public String getA() { return a; }
}
@Test
public void singlePrivateWithConstructor() throws NotSerializableException, NoSuchFieldException, IllegalAccessException {
SerializerFactory factory = new SerializerFactory(AllWhitelist.INSTANCE, ClassLoader.getSystemClassLoader());
SerializationOutput ser = new SerializationOutput(factory);
DeserializationInput des = new DeserializationInput(factory);
C c = new C("dripping taps");
C c2 = des.deserialize(ser.serialize(c), C.class);
assertEquals (c.a, c2.a);
//
// Now ensure we actually got a private property serializer
//
Field f = SerializerFactory.class.getDeclaredField("serializersByDescriptor");
f.setAccessible(true);
ConcurrentHashMap<Object, AMQPSerializer<Object>> serializersByDescriptor =
(ConcurrentHashMap<Object, AMQPSerializer<Object>>) f.get(factory);
assertEquals(1, serializersByDescriptor.size());
ObjectSerializer cSerializer = ((ObjectSerializer)serializersByDescriptor.values().toArray()[0]);
assertEquals(1, cSerializer.getPropertySerializers().component1().size());
Object[] propertyReaders = cSerializer.getPropertySerializers().component1().toArray();
assertTrue (((PropertySerializer)propertyReaders[0]).getPropertyReader() instanceof PrivatePropertyReader);
}
@Test
public void singlePrivateWithConstructorAndGetter()
throws NotSerializableException, NoSuchFieldException, IllegalAccessException {
SerializerFactory factory = new SerializerFactory(AllWhitelist.INSTANCE, ClassLoader.getSystemClassLoader());
SerializationOutput ser = new SerializationOutput(factory);
DeserializationInput des = new DeserializationInput(factory);
C2 c = new C2("dripping taps");
C2 c2 = des.deserialize(ser.serialize(c), C2.class);
assertEquals (c.a, c2.a);
//
// Now ensure we actually got a private property serializer
//
Field f = SerializerFactory.class.getDeclaredField("serializersByDescriptor");
f.setAccessible(true);
ConcurrentHashMap<Object, AMQPSerializer<Object>> serializersByDescriptor =
(ConcurrentHashMap<Object, AMQPSerializer<Object>>) f.get(factory);
assertEquals(1, serializersByDescriptor.size());
ObjectSerializer cSerializer = ((ObjectSerializer)serializersByDescriptor.values().toArray()[0]);
assertEquals(1, cSerializer.getPropertySerializers().component1().size());
Object[] propertyReaders = cSerializer.getPropertySerializers().component1().toArray();
assertTrue (((PropertySerializer)propertyReaders[0]).getPropertyReader() instanceof PublicPropertyReader);
}
}

View File

@ -1,10 +1,8 @@
package net.corda.nodeapi.internal.serialization.amqp
import net.corda.core.serialization.SerializedBytes
import org.apache.qpid.proton.codec.Data
import net.corda.nodeapi.internal.serialization.AllWhitelist
import net.corda.nodeapi.internal.serialization.EmptyWhitelist
import java.io.NotSerializableException
fun testDefaultFactory() = SerializerFactory(AllWhitelist, ClassLoader.getSystemClassLoader())
fun testDefaultFactoryWithWhitelist() = SerializerFactory(EmptyWhitelist, ClassLoader.getSystemClassLoader())

View File

@ -0,0 +1,117 @@
package net.corda.nodeapi.internal.serialization.amqp
import junit.framework.TestCase.assertTrue
import junit.framework.TestCase.assertEquals
import org.junit.Test
import org.apache.qpid.proton.amqp.Symbol
import java.util.concurrent.ConcurrentHashMap
class PrivatePropertyTests {
private val factory = testDefaultFactory()
@Test
fun testWithOnePrivateProperty() {
data class C(private val b: String)
val c1 = C("Pants are comfortable sometimes")
val c2 = DeserializationInput(factory).deserialize(SerializationOutput(factory).serialize(c1))
assertEquals(c1, c2)
}
@Test
fun testWithOnePrivatePropertyNullableNotNull() {
data class C(private val b: String?)
val c1 = C("Pants are comfortable sometimes")
val c2 = DeserializationInput(factory).deserialize(SerializationOutput(factory).serialize(c1))
assertEquals(c1, c2)
}
@Test
fun testWithOnePrivatePropertyNullableNull() {
data class C(private val b: String?)
val c1 = C(null)
val c2 = DeserializationInput(factory).deserialize(SerializationOutput(factory).serialize(c1))
assertEquals(c1, c2)
}
@Test
fun testWithOnePublicOnePrivateProperty() {
data class C(val a: Int, private val b: Int)
val c1 = C(1, 2)
val c2 = DeserializationInput(factory).deserialize(SerializationOutput(factory).serialize(c1))
assertEquals(c1, c2)
}
@Test
fun testWithOnePublicOnePrivateProperty2() {
data class C(val a: Int, private val b: Int)
val c1 = C(1, 2)
val schemaAndBlob = SerializationOutput(factory).serializeAndReturnSchema(c1)
assertEquals(1, schemaAndBlob.schema.types.size)
val field = SerializerFactory::class.java.getDeclaredField("serializersByDescriptor")
field.isAccessible = true
@Suppress("UNCHECKED_CAST")
val serializersByDescriptor = field.get(factory) as ConcurrentHashMap<Any, AMQPSerializer<Any>>
val schemaDescriptor = schemaAndBlob.schema.types.first().descriptor.name
serializersByDescriptor.filterKeys { (it as Symbol) == schemaDescriptor }.values.apply {
assertEquals(1, this.size)
assertTrue(this.first() is ObjectSerializer)
val propertySerializers = (this.first() as ObjectSerializer).propertySerializers.getters.toList()
assertEquals(2, propertySerializers.size)
// a was public so should have a synthesised getter
assertTrue(propertySerializers[0].propertyReader is PublicPropertyReader)
// b is private and thus won't have teh getter so we'll have reverted
// to using reflection to remove the inaccessible property
assertTrue(propertySerializers[1].propertyReader is PrivatePropertyReader)
}
}
@Test
fun testGetterMakesAPublicReader() {
data class C(val a: Int, private val b: Int) {
@Suppress("UNUSED")
fun getB() = b
}
val c1 = C(1, 2)
val schemaAndBlob = SerializationOutput(factory).serializeAndReturnSchema(c1)
assertEquals(1, schemaAndBlob.schema.types.size)
val field = SerializerFactory::class.java.getDeclaredField("serializersByDescriptor")
field.isAccessible = true
@Suppress("UNCHECKED_CAST")
val serializersByDescriptor = field.get(factory) as ConcurrentHashMap<Any, AMQPSerializer<Any>>
val schemaDescriptor = schemaAndBlob.schema.types.first().descriptor.name
serializersByDescriptor.filterKeys { (it as Symbol) == schemaDescriptor }.values.apply {
assertEquals(1, this.size)
assertTrue(this.first() is ObjectSerializer)
val propertySerializers = (this.first() as ObjectSerializer).propertySerializers.getters.toList()
assertEquals(2, propertySerializers.size)
// as before, a is public so we'll use the getter method
assertTrue(propertySerializers[0].propertyReader is PublicPropertyReader)
// the getB() getter explicitly added means we should use the "normal" public
// method reader rather than the private oen
assertTrue(propertySerializers[1].propertyReader is PublicPropertyReader)
}
}
@Test
fun testNested() {
data class Inner(private val a: Int)
data class Outer(private val i: Inner)
val c1 = Outer(Inner(1010101))
val c2 = DeserializationInput(factory).deserialize(SerializationOutput(factory).serialize(c1))
assertEquals(c1, c2)
}
}

View File

@ -35,9 +35,16 @@ fun Schema.mangleNames(names: List<String>): Schema {
return Schema(types = newTypes)
}
/**
* Custom implementation of a [SerializerFactory] where we need to give it a class carpenter
* rather than have it create its own
*/
class SerializerFactoryExternalCarpenter(override val classCarpenter: ClassCarpenter)
: SerializerFactory (classCarpenter.whitelist, classCarpenter.classloader)
open class AmqpCarpenterBase(whitelist: ClassWhitelist) {
var cc = ClassCarpenter(whitelist = whitelist)
var factory = SerializerFactory(AllWhitelist, cc.classloader)
var factory = SerializerFactoryExternalCarpenter(cc)
fun serialise(clazz: Any) = SerializationOutput(factory).serialize(clazz)
fun testName(): String = Thread.currentThread().stackTrace[2].methodName

View File

@ -6,7 +6,7 @@ apply plugin: 'net.corda.plugins.publish-utils'
apply plugin: 'us.kirchmeier.capsule'
apply plugin: 'com.jfrog.artifactory'
description 'Corda Enterprise'
description 'R3 Corda'
configurations {
runtimeArtifacts.extendsFrom runtime
@ -28,7 +28,7 @@ targetCompatibility = 1.6
task buildCordaJAR(type: FatCapsule, dependsOn: project(':node').compileJava) {
applicationClass 'net.corda.node.Corda'
archiveName "corda-enterprise-${corda_release_version}.jar"
archiveName "corda-r3-${corda_release_version}.jar"
applicationSource = files(
project(':node').configurations.runtime,
project(':node').jar,

View File

@ -17,9 +17,10 @@ import net.corda.node.services.config.NotaryConfig
import net.corda.nodeapi.internal.DevIdentityGenerator
import net.corda.nodeapi.internal.network.NetworkParametersCopier
import net.corda.nodeapi.internal.network.NotaryInfo
import net.corda.testing.*
import net.corda.testing.chooseIdentity
import net.corda.testing.common.internal.testNetworkParameters
import net.corda.testing.contracts.DummyContract
import net.corda.testing.dummyCommand
import net.corda.testing.internal.IntegrationTest
import net.corda.testing.internal.IntegrationTestSchemas
import net.corda.testing.node.MockNetwork
@ -27,7 +28,10 @@ import net.corda.testing.node.MockNodeParameters
import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties
import net.corda.testing.node.inMemoryH2DataSourceConfig
import net.corda.testing.node.startFlow
import org.junit.*
import org.junit.After
import org.junit.Before
import org.junit.ClassRule
import org.junit.Test
import java.math.BigInteger
import java.util.*
import kotlin.test.assertEquals

View File

@ -17,6 +17,11 @@ import net.corda.node.internal.StartedNode
import net.corda.node.services.messaging.MessagingService
import net.corda.node.services.messaging.ReceivedMessage
import net.corda.node.services.messaging.send
import net.corda.node.services.transactions.RaftValidatingNotaryService
import net.corda.testing.*
import net.corda.node.services.messaging.*
import net.corda.testing.ALICE_NAME
import net.corda.testing.chooseIdentity
import net.corda.testing.ALICE_NAME
import net.corda.testing.chooseIdentity
import net.corda.testing.driver.DriverDSL

View File

@ -187,7 +187,7 @@ abstract class AbstractNode(val configuration: NodeConfiguration,
initCertificate()
val schemaService = NodeSchemaService(cordappLoader.cordappSchemas)
val (identity, identityKeyPair) = obtainIdentity(notaryConfig = null)
return initialiseDatabasePersistence(schemaService, makeIdentityService(identity.certificate)) { database ->
return initialiseDatabasePersistence(schemaService, makeIdentityService(identity.certificate)) { database ->
// TODO The fact that we need to specify an empty list of notaries just to generate our node info looks like
// a code smell.
val persistentNetworkMapCache = PersistentNetworkMapCache(database, notaries = emptyList())

View File

@ -265,6 +265,9 @@ open class Node(configuration: NodeConfiguration,
printBasicNodeInfo("Database connection url is", "jdbc:h2:$url/node")
}
}
else if (databaseUrl != null) {
printBasicNodeInfo("Database connection url is", databaseUrl)
}
return super.initialiseDatabasePersistence(schemaService, identityService, insideTransaction)
}

View File

@ -1,8 +1,5 @@
package net.corda.node.services.config
import com.typesafe.config.*
import net.corda.core.crypto.Crypto
import net.corda.core.crypto.SignatureScheme
import com.typesafe.config.Config
import com.typesafe.config.ConfigFactory
import com.typesafe.config.ConfigParseOptions

View File

@ -36,13 +36,8 @@ fun freshCertificate(identityService: IdentityServiceInternal,
require(issuerRole == CertRole.LEGAL_IDENTITY) { "Confidential identities can only be issued from well known identities, provided issuer ${issuer.name} has role $issuerRole" }
val issuerCert = issuer.certificate
val window = X509Utilities.getCertificateValidityWindow(Duration.ZERO, 3650.days, issuerCert)
val ourCertificate = X509Utilities.createCertificate(
CertificateType.CONFIDENTIAL_LEGAL_IDENTITY,
issuerCert.subjectX500Principal,
issuerSigner,
issuer.name.x500Principal,
subjectPublicKey,
window)
val ourCertificate = X509Utilities.createCertificate(CertificateType.CONFIDENTIAL_LEGAL_IDENTITY, issuerCert.subjectX500Principal,
issuerSigner, issuer.name.x500Principal, subjectPublicKey, window)
val ourCertPath = X509CertificateFactory().generateCertPath(listOf(ourCertificate) + issuer.certPath.certificates)
val anonymisedIdentity = PartyAndCertificate(ourCertPath)
identityService.justVerifyAndRegisterIdentity(anonymisedIdentity)

View File

@ -8,7 +8,8 @@ import net.corda.nodeapi.internal.persistence.NODE_DATABASE_PREFIX
import org.hibernate.Session
import java.text.SimpleDateFormat
import java.time.Duration
import java.util.Date
import java.time.LocalDateTime
import java.time.temporal.ChronoField
import java.util.concurrent.ScheduledExecutorService
import java.util.concurrent.TimeUnit
import java.util.concurrent.atomic.AtomicBoolean
@ -47,7 +48,7 @@ class RunOnceService(private val database: CordaPersistence, private val machine
@Entity
@Table(name = TABLE)
class MutualExclusion(machineNameInit: String, pidInit: String) {
class MutualExclusion(machineNameInit: String, pidInit: String, timeStampInit: LocalDateTime) {
@Column(name = ID, insertable = false, updatable = false)
@Id
val id: Char = 'X'
@ -59,8 +60,7 @@ class RunOnceService(private val database: CordaPersistence, private val machine
val pid = pidInit
@Column(name = TIMESTAMP)
@Temporal(TemporalType.TIMESTAMP)
val timestamp: Date? = null
val timestamp = timeStampInit
}
fun start() {
@ -130,7 +130,7 @@ class RunOnceService(private val database: CordaPersistence, private val machine
}
private fun updateTimestamp(session: Session, mutualExclusion: MutualExclusion): Boolean {
val minWaitTime = simpleDateFormat.format(Date(mutualExclusion.timestamp!!.time + waitInterval))
val minWaitTime = mutualExclusion.timestamp.plus(waitInterval, ChronoField.MILLI_OF_SECOND.baseUnit)
val query = session.createNativeQuery("UPDATE $TABLE SET $MACHINE_NAME = :machineName, $TIMESTAMP = CURRENT_TIMESTAMP, $PID = :pid " +
"WHERE $ID = 'X' AND " +

View File

@ -167,11 +167,7 @@ class InMemoryIdentityServiceTests {
val issuerKeyPair = generateKeyPair()
val issuer = getTestPartyAndCertificate(x500Name, issuerKeyPair.public)
val txKeyPair = Crypto.generateKeyPair()
val txCert = X509Utilities.createCertificate(
CertificateType.CONFIDENTIAL_LEGAL_IDENTITY,
issuer.certificate,
issuerKeyPair,
x500Name.x500Principal,
val txCert = X509Utilities.createCertificate(CertificateType.CONFIDENTIAL_LEGAL_IDENTITY, issuer.certificate, issuerKeyPair, x500Name.x500Principal,
txKeyPair.public)
val txCertPath = X509CertificateFactory().generateCertPath(listOf(txCert) + issuer.certPath.certificates)
return Pair(issuer, PartyAndCertificate(txCertPath))

View File

@ -14,7 +14,7 @@ import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.contrib.java.lang.system.ExpectedSystemExit
import java.util.*
import java.time.LocalDateTime
import java.util.concurrent.ScheduledExecutorService
import java.util.concurrent.ScheduledFuture
import javax.persistence.Query
@ -64,7 +64,9 @@ class RunOnceServiceTest {
fun `row updated when change of master node`() {
runOnceServiceMachine1.start()
var firstTimestamp: Date? = null
var secondTimestamp = LocalDateTime.now()
var firstTimestamp = LocalDateTime.now()
database.transaction {
val query = session.createNativeQuery(selectQuery, RunOnceService.MutualExclusion::class.java)
val result = machine1RowCheck(query)
@ -76,14 +78,13 @@ class RunOnceServiceTest {
runOnceServiceMachine2.start()
var secondTimestamp: Date? = null
database.transaction {
val query = session.createNativeQuery(selectQuery, RunOnceService.MutualExclusion::class.java)
val result = machine2RowCheck(query)
secondTimestamp = result.timestamp
}
assertTrue(secondTimestamp!!.toInstant().isAfter(firstTimestamp!!.toInstant()))
assertTrue(secondTimestamp.isAfter(firstTimestamp))
}
@Test
@ -105,7 +106,9 @@ class RunOnceServiceTest {
fun `row updated when last run was same machine`() {
runOnceServiceMachine1.start()
var firstTimestamp: Date? = null
var secondTimestamp = LocalDateTime.now()
var firstTimestamp = LocalDateTime.now()
database.transaction {
val query = session.createNativeQuery(selectQuery, RunOnceService.MutualExclusion::class.java)
val result = machine1RowCheck(query)
@ -117,14 +120,13 @@ class RunOnceServiceTest {
runOnceServiceMachine1.start()
var secondTimestamp: Date? = null
database.transaction {
val query = session.createNativeQuery(selectQuery, RunOnceService.MutualExclusion::class.java)
val result = machine1RowCheck(query)
secondTimestamp = result.timestamp
}
assertTrue(secondTimestamp!!.toInstant().isAfter(firstTimestamp!!.toInstant()))
assertTrue(secondTimestamp.isAfter(firstTimestamp))
}
@Test
@ -132,7 +134,9 @@ class RunOnceServiceTest {
whenever(mockUpdateExecutor.scheduleAtFixedRate(any(), any(), any(), any())).thenAnswer { invocation ->
val runnable = invocation.arguments[0] as Runnable
var firstTimestamp: Date? = null
var secondTimestamp = LocalDateTime.now()
var firstTimestamp = LocalDateTime.now()
database.transaction {
val query = session.createNativeQuery(selectQuery, RunOnceService.MutualExclusion::class.java)
val result = machine1RowCheck(query)
@ -141,14 +145,13 @@ class RunOnceServiceTest {
runnable.run()
var secondTimestamp: Date? = null
database.transaction {
val query = session.createNativeQuery(selectQuery, RunOnceService.MutualExclusion::class.java)
val result = machine1RowCheck(query)
secondTimestamp = result.timestamp
}
assertTrue(secondTimestamp!!.toInstant().isAfter(firstTimestamp!!.toInstant()))
assertTrue(secondTimestamp.isAfter(firstTimestamp))
mock<ScheduledFuture<*>>()
}
@ -228,7 +231,7 @@ class RunOnceServiceTest {
assertEquals('X', result.id)
assertEquals("machine1", result.machineName)
assertEquals("123", result.pid)
assertTrue(result.timestamp is Date)
assertTrue(result.timestamp is LocalDateTime)
return result
}
@ -238,7 +241,7 @@ class RunOnceServiceTest {
assertEquals('X', result.id)
assertEquals("machine2", result.machineName)
assertEquals("789", result.pid)
assertTrue(result.timestamp is Date)
assertTrue(result.timestamp is LocalDateTime)
return result
}
}

View File

@ -8,9 +8,9 @@ import net.corda.core.utilities.NetworkHostAndPort
import net.corda.node.services.config.NotaryConfig
import net.corda.node.services.config.RaftConfig
import net.corda.nodeapi.internal.DevIdentityGenerator
import net.corda.testing.node.internal.demorun.*
import net.corda.testing.ALICE_NAME
import net.corda.testing.BOB_NAME
import net.corda.testing.node.internal.demorun.*
import java.nio.file.Paths
fun main(args: Array<String>) = RaftNotaryCordform().deployNodes()

View File

@ -52,7 +52,7 @@ allprojects {
systemProperties["attestation.home"] = "$buildDir/logs"
}
group 'com.r3.corda.enterprise'
group 'com.r3.corda'
version '1.0-SNAPSHOT'
repositories {

View File

@ -5,7 +5,6 @@ import com.google.common.jimfs.Jimfs
import com.nhaarman.mockito_kotlin.doReturn
import com.nhaarman.mockito_kotlin.whenever
import net.corda.core.DoNotImplement
import net.corda.core.crypto.entropyToKeyPair
import net.corda.core.crypto.Crypto
import net.corda.core.crypto.random63BitValue
import net.corda.core.identity.CordaX500Name

View File

@ -110,6 +110,9 @@ Now choose `File/Open` from the main menu, and select the profile that you have
just saved. DemoBench should close the two existing tabs and then relaunch the
Notary and Bank nodes.
If you want to edit the content, please make sure that when zipping content back,
the notary node is the first directory (ZIP files content is ordered)
## Exiting DemoBench
Close DemoBench as a normal application on your platform; it should close any

View File

@ -10,6 +10,7 @@ import net.corda.nodeapi.internal.config.User
import net.corda.nodeapi.internal.config.toConfig
import java.nio.file.Path
import java.nio.file.StandardCopyOption
import java.util.Properties
/**
* This is a subset of FullNodeConfiguration, containing only those configs which we need. The node uses reference.conf
@ -25,7 +26,10 @@ data class NodeConfig(
val h2port: Int,
val rpcUsers: List<User> = listOf(defaultUser),
/** This is an extra config used by the Cash app. */
val issuableCurrencies: List<String> = emptyList()
val issuableCurrencies: List<String> = emptyList(),
/** Pass-through for generating node.conf with external DB */
val dataSourceProperties: Properties? = null,
val database: Properties? = null
) {
companion object {
val renderOptions: ConfigRenderOptions = ConfigRenderOptions.defaults().setOriginComments(false)

View File

@ -10,10 +10,10 @@ import net.corda.core.internal.noneOrSingle
import net.corda.core.utilities.NetworkHostAndPort
import net.corda.demobench.plugin.CordappController
import net.corda.demobench.pty.R3Pty
import net.corda.nodeapi.internal.DevIdentityGenerator
import net.corda.nodeapi.internal.network.NetworkParameters
import net.corda.nodeapi.internal.network.NetworkParametersCopier
import net.corda.nodeapi.internal.network.NotaryInfo
import net.corda.nodeapi.internal.DevIdentityGenerator
import tornadofx.*
import java.io.IOException
import java.lang.management.ManagementFactory

View File

@ -34,16 +34,44 @@ The Participant nodes are only able to spend cash (eg. move cash).
**These Corda nodes will be created on the following port on localhost.**
* Notary -> 20003 (Does not accept logins)
* Alice -> 20006
* Bob -> 20009
* UK Bank Plc -> 20012 (*Issuer node*)
* USA Bank Corp -> 20015 (*Issuer node*)
* Notary -> 20001 (Does not accept logins)
* Alice -> 20004
* Bob -> 20007
* UK Bank Plc -> 20010 (*Issuer node*)
* USA Bank Corp -> 20013 (*Issuer node*)
Explorer login credentials to the Issuer nodes are defaulted to ``manager`` and ``test``.
Explorer login credentials to the Participants nodes are defaulted to ``user1`` and ``test``.
Explorer login credentials to the Participant nodes are defaulted to ``user1`` and ``test``.
Please note you are not allowed to login to the notary.
## Running Simulation Nodes
Building on the demonstration Corda network topology described above, simulation mode performs continuous
issue, move and exit cash operations across all participant nodes.
**Windows:**
gradlew.bat tools:explorer:runSimulationNodes
**Other:**
./gradlew tools:explorer:runSimulationNodes
## Running Flow Triage scenario
Once again, building on the demonstration Corda network topology described above, this scenario mode triggers
an exception within a flow which can then be visualized using the "Flow Triage" panel within the Explorer.
The "Flow Triage" panel will be enhanced in the future to enable operators to take corrective actions upon flow failures
(eg. retry, terminate, amend and replay)
**Windows:**
gradlew.bat tools:explorer:runFlowTriageNodes
**Other:**
./gradlew tools:explorer:runFlowTriageNodes
## Business Network reference implementation
An additional "IOU" panel is now visible in the main Explorer dashboard to demonstrate the new Business Networks concept using a sample IOU product.
@ -71,4 +99,4 @@ Please note Business Networks functionality only applies to the same IOU CorDapp
- Support other contract types.
More information can be found in the [Project website](https://corda.net) and [Documentation](https://docs.corda.net).
More information can be found in the [Project website](https://corda.net) and [Documentation](https://docs.corda.net).

View File

@ -65,10 +65,10 @@ class Main : App(MainView::class) {
if (!isLoggedIn) {
stage.hide()
loginView.login()
addOptionalViews()
(find(primaryView) as MainView).initializeControls()
stage.show()
}
addOptionalViews()
(find(primaryView) as MainView).initializeControls()
stage.show()
}
private fun addOptionalViews() {