Merge remote-tracking branch 'open/master' into kat-merge-180412

This commit is contained in:
Katelyn Baker 2018-04-12 20:34:28 +01:00
commit 5bb9514582
11 changed files with 406 additions and 226 deletions

View File

@ -1895,7 +1895,7 @@ public @interface net.corda.core.messaging.RPCReturnsObservables
@org.jetbrains.annotations.NotNull public final List getNotaries() @org.jetbrains.annotations.NotNull public final List getNotaries()
@org.jetbrains.annotations.NotNull public final Map getWhitelistedContractImplementations() @org.jetbrains.annotations.NotNull public final Map getWhitelistedContractImplementations()
public int hashCode() public int hashCode()
public String toString() @org.jetbrains.annotations.NotNull public String toString()
## ##
@net.corda.core.serialization.CordaSerializable public final class net.corda.core.node.NodeInfo extends java.lang.Object @net.corda.core.serialization.CordaSerializable public final class net.corda.core.node.NodeInfo extends java.lang.Object
public <init>(List, List, int, long) public <init>(List, List, int, long)

View File

@ -47,6 +47,20 @@ data class NetworkParameters(
require(maxMessageSize > 0) { "maxMessageSize must be at least 1" } require(maxMessageSize > 0) { "maxMessageSize must be at least 1" }
require(maxTransactionSize > 0) { "maxTransactionSize must be at least 1" } require(maxTransactionSize > 0) { "maxTransactionSize must be at least 1" }
} }
override fun toString(): String {
return """NetworkParameters {
minimumPlatformVersion=$minimumPlatformVersion
notaries=$notaries
maxMessageSize=$maxMessageSize
maxTransactionSize=$maxTransactionSize
whitelistedContractImplementations {
${whitelistedContractImplementations.entries.joinToString("\n ")}
}
modifiedTime=$modifiedTime
epoch=$epoch
}"""
}
} }
/** /**

View File

@ -5,7 +5,22 @@ Here's a summary of what's changed in each Corda release. For guidance on how to
release, see :doc:`upgrade-notes`. release, see :doc:`upgrade-notes`.
Unreleased Unreleased
---------- ==========
* Fix CORDA-1229. Setter-based serialization was broken with generic types when the property was stored
as the raw type, List for example.
* java.security.cert.CRLReason added to the default Whitelist.
* java.security.cert.X509CRL serialization support added.
* Upgraded H2 to v1.4.197.
* Shell (embedded available only in dev mode or via SSH) connects to the node via RPC instead of using the ``CordaRPCOps`` object directly.
To enable RPC connectivity ensure nodes ``rpcSettings.address`` and ``rpcSettings.adminAddress`` settings are present.
* The network bootstrapper uses the existing network parameters file to update the current contracts whitelist, and no longer
needs the whitelist.txt file.
* Errors thrown by a Corda node will now reported to a calling RPC client with attention to serialization and obfuscation of internal data. * Errors thrown by a Corda node will now reported to a calling RPC client with attention to serialization and obfuscation of internal data.
@ -19,16 +34,12 @@ Unreleased
only once when it was created. Whilst registering serializers that already exist is essentially a no-op, it's a performance overhead for only once when it was created. Whilst registering serializers that already exist is essentially a no-op, it's a performance overhead for
a very frequent operation that hits a synchronisation point (and is thus flagged as contended by our perfomance suite) a very frequent operation that hits a synchronisation point (and is thus flagged as contended by our perfomance suite)
* Update the fast-classpath-scanner dependent library version from 2.0.21 to 2.12.3
.. note:: Whilst this is not the latest version of this library, that being 2.18.1 at time of writing, versions later
than 2.12.3 (including 2.12.4) exhibit a different issue.
* Node can be shut down abruptly by ``shutdown`` function in `CordaRPCOps` or gracefully (draining flows first) through ``gracefulShutdown`` command from shell. * Node can be shut down abruptly by ``shutdown`` function in `CordaRPCOps` or gracefully (draining flows first) through ``gracefulShutdown`` command from shell.
* Carpenter Exceptions will be caught internally by the Serializer and rethrown as a ``NotSerializableException`` * Carpenter Exceptions will be caught internally by the Serializer and rethrown as a ``NotSerializableException``
* Specific details of the error encountered are logged to the node's log file. More information can be enabled by setting the debug level to ``trace`` ; this will cause the full stack trace of the error to be dumped into the log. * Specific details of the error encountered are logged to the node's log file. More information can be enabled by setting the debug level to
``trace`` ; this will cause the full stack trace of the error to be dumped into the log.
* Parsing of ``NodeConfiguration`` will now fail if unknown configuration keys are found. * Parsing of ``NodeConfiguration`` will now fail if unknown configuration keys are found.
@ -40,51 +51,22 @@ Unreleased
* java.math.BigInteger serialization support added. * java.math.BigInteger serialization support added.
* java.security.cert.CRLReason added to the default Whitelist. * Update the fast-classpath-scanner dependent library version from 2.0.21 to 2.12.3
* java.security.cert.X509CRL serialization support added. .. note:: Whilst this is not the latest version of this library, that being 2.18.1 at time of writing, versions later
than 2.12.3 (including 2.12.4) exhibit a different issue.
* Upgraded H2 to v1.4.197. * Updated the api scanner gradle plugin to work the same way as the version in master. These changes make the api scanner more
accurate and fix a couple of bugs, and change the format of the api-current.txt file slightly. Backporting these changes
to the v3 branch will make it easier for us to ensure that apis are stable for future versions. These changes are
released in gradle plugins version 3.0.10. For more information on the api scanner see
the `documentation <https://github.com/corda/corda-gradle-plugins/tree/master/api-scanner>`_.
* Per CorDapp configuration is now exposed. ``CordappContext`` now exposes a ``CordappConfig`` object that is populated * Fixed security vulnerability when using the ``HashAttachmentConstraint``. Added strict check that the contract JARs
at CorDapp context creation time from a file source during runtime. referenced in a transaction were deployed on the node.
* Introduced Flow Draining mode, in which a node continues executing existing flows, but does not start new. This is to support graceful node shutdown/restarts. * Fixed node's behaviour on startup when there is no connectivity to network map. Node continues to work normally if it has
In particular, when this mode is on, new flows through RPC will be rejected, scheduled flows will be ignored, and initial session messages will not be consumed. all the needed network data, waiting in the background for network map to become available.
This will ensure that the number of checkpoints will strictly diminish with time, allowing for a clean shutdown.
* Make the serialisation finger-printer a pluggable entity rather than hard wiring into the factory
* Removed blacklisted word checks in Corda X.500 name to allow "Server" or "Node" to be use as part of the legal name.
* Separated our pre-existing Artemis broker into an RPC broker and a P2P broker.
* Refactored ``NodeConfiguration`` to expose ``NodeRpcOptions`` (using top-level "rpcAddress" property still works with warning).
* Modified ``CordaRPCClient`` constructor to take a ``SSLConfiguration?`` additional parameter, defaulted to ``null``.
* Introduced ``CertificateChainCheckPolicy.UsernameMustMatchCommonName`` sub-type, allowing customers to optionally enforce username == CN condition on RPC SSL certificates.
* Modified ``DriverDSL`` and sub-types to allow specifying RPC settings for the Node.
* Modified the ``DriverDSL`` to start Cordformation nodes allowing automatic generation of "rpcSettings.adminAddress" in case "rcpSettings.useSsl" is ``false`` (the default).
* Introduced ``UnsafeCertificatesFactory`` allowing programmatic generation of X509 certificates for test purposes.
* JPA Mapping annotations for States extending ``CommonSchemaV1.LinearState`` and ``CommonSchemaV1.FungibleState`` on the
`participants` collection need to be moved to the actual class. This allows to properly specify the unique table name per a collection.
See: DummyDealStateSchemaV1.PersistentDummyDealState
* JPA Mapping annotations for States extending ``CommonSchemaV1.LinearState`` and ``CommonSchemaV1.FungibleState`` on the
`participants` collection need to be moved to the actual State class. This allows developers to properly specify
the table name for the `participants` collection.
For an example on how the mapping can be done, see: DummyDealStateSchemaV1.PersistentDummyDealState
* JDBC drivers for SQL server and PostgresSQL are no longer bundled as part of Corda releases. If you are running a node
on such databases you need to provide the associated driver as described in :doc:`node-database`.
* Shell (embedded shell available only in dev mode or via SSH) connects to the node via RPC instead of using the ``CordaRPCOps`` object directly.
To enable RPC connectivity ensure nodes ``rpcSettings.address`` and ``rpcSettings.adminAddress`` settings are present.
R3 Corda 3.0 Developer Preview R3 Corda 3.0 Developer Preview
------------------------------ ------------------------------

View File

@ -1,20 +1,6 @@
Release notes Release notes
============= =============
Unreleased
----------
* **Enum Class Evolution**
With the addition of AMQP serialization Corda now supports enum constant evolution.
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.
* 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
found at :doc:`permissioning`.
R3 Corda 3.0 Developer Preview R3 Corda 3.0 Developer Preview
------------------------------ ------------------------------
This Developer Preview takes us towards the launch of R3 Corda, R3's commercially supported enterprise blockchain platform. This Developer Preview takes us towards the launch of R3 Corda, R3's commercially supported enterprise blockchain platform.
@ -168,8 +154,62 @@ Please note this release is distributed under license and should not be used in
We look forward to hearing your feedback on this Developer Preview. We look forward to hearing your feedback on this Developer Preview.
<<<<<<< HEAD
Corda 2.0 Corda 2.0
--------- ---------
=======
Documentation can be found in :doc:`cordapp-custom-serializers`
Security Auditing
~~~~~~~~~~~~~~~~~
This version of Corda is the first to have had select components subjected to the newly established security review process
by R3's internal security team. Security review will be an on-going process that seeks to provide assurance that the
security model of Corda has been implemented to the highest standard, and is in line with industry best practice.
As part of this security review process, an independent external security audit of the HTTP based components of the code
was undertaken and its recommendations were acted upon. The security assurance process will develop in parallel to the
Corda platform and will combine code review, automated security testing and secure development practices to ensure Corda
fulfils its security guarantees.
Security fixes
~~~~~~~~~~~~~~
* Due to a potential privacy leak, there has been a breaking change in the error object returned by the
notary service when trying to consume the same state twice: `NotaryError.Conflict` no longer contains the identity
of the party that initiated the first spend of the state, and specifies the hash of the consuming transaction id for
a state instead of the id itself.
Without this change, knowing the reference of a particular state, an attacker could construct an invalid
double-spend transaction, and obtain the information on the transaction and the party that consumed it. It could
repeat this process with the newly obtained transaction id by guessing its output indexes to obtain the forward
transaction graph with associated identities. When anonymous identities are used, this could also reveal the identity
of the owner of an asset.
Minor Changes
~~~~~~~~~~~~~
* Upgraded gradle to 4.4.1.
.. note:: To avoid potential incompatibility issues we recommend you also upgrade your CorDapp's gradle
plugin to match. Details on how to do this can be found on the official
`gradle website <https://docs.gradle.org/current/userguide/gradle_wrapper.html#sec:upgrading_wrapper>`_
* Cash Spending now allows for sending multiple amounts to multiple parties with a single API call
- documentation can be found within the JavaDocs on ``TwoPartyTradeFlow``.
* Overall improvements to error handling (RPC, Flows, Network Client).
* TLS authentication now supports mixed RSA and ECDSA keys.
* PrivacySalt computation is faster as it does not depend on the OS's entropy pool directly.
* Numerous bug fixes and documentation tweaks.
* Removed dependency on Jolokia WAR file.
.. _release_notes_v2_0:
Release 2.0
-----------
>>>>>>> open/master
Following quickly on the heels of the release of Corda 1.0, Corda version 2.0 consolidates 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. 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. This was absent from version 1 but based on user feedback its re-introduction removes the need for complicated "isRelevant()" checks.
@ -187,8 +227,15 @@ Adds the facility for transparent forwarding of transactions to some third party
that entity simply run an Observer node they can simply receive 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. can be used for reporting.
<<<<<<< HEAD
Corda 1.0 Corda 1.0
--------- ---------
=======
.. _release_notes_v1_0:
Release 1.0
-----------
>>>>>>> open/master
Corda 1.0 is finally here! 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. This critical step in the Corda journey enables the developer community, clients, and partners to build on Corda with confidence.

View File

@ -93,34 +93,16 @@ If you want to create a *Zone whitelist* (see :doc:`api-contract-constraints`),
``java -jar network-bootstrapper.jar <nodes-root-dir> <path-to-first-corDapp> <path-to-second-corDapp> ..`` ``java -jar network-bootstrapper.jar <nodes-root-dir> <path-to-first-corDapp> <path-to-second-corDapp> ..``
The CorDapp jars will be hashed and scanned for ``Contract`` classes. The CorDapp jars will be hashed and scanned for ``Contract`` classes. These contract class implementations will become part
By default the tool would generate a file named ``whitelist.txt`` containing an entry for each contract with the hash of the jar. of the whitelisted contracts in the network parameters (see ``NetworkParameters.whitelistedContractImplementations`` :doc:`network-map`).
If the network already has a set of network parameters defined (i.e. the node directories all contain the same network-parameters
file) then the new set of contracts will be appended to the current whitelist.
For example: .. note:: The whitelist can only ever be appended to. Once added a contract implementation can never be removed.
.. sourcecode:: none By default the bootstrapper tool will whitelist all the contracts found in all the CorDapp jars. To prevent certain
contracts from being whitelisted, add their fully qualified class name in the ``exclude_whitelist.txt``. These will instead
net.corda.finance.contracts.asset.Obligation:decd098666b9657314870e192ced0c3519c2c9d395507a238338f8d003929de8 use the more restrictive ``HashAttachmentConstraint``.
net.corda.finance.contracts.asset.Cash:decd098666b9657314870e192ced0c3519c2c9d395507a238338f8d003929de9
These will be added to the ``NetworkParameters.whitelistedContractImplementations``. See :doc:`network-map`.
This means that by default the Network bootstrapper tool will whitelist all contracts found in all passed CorDapps.
In case there is a ``whitelist.txt`` file in the root dir already, the tool will append the new jar hashes or contracts to it.
The zone operator will maintain this whitelist file, and, using the tool, will append new versions of CorDapps to it.
.. warning::
- The zone operator must ensure that this file is *append only*.
- If the operator removes hashes from the list, all transactions pointing to that version will suddenly fail the constraint verification, and the entire chain is compromised.
- If a contract is removed from the whitelist, then all states created from that moment on will be constrained by the HashAttachmentConstraint.
Note: In future releases, we will provider a tamper-proof way of maintaining the contract whitelist.
For fine-grained control of constraints, in case multiple contracts live in the same jar, the tool reads from another file:
``exclude_whitelist.txt``, which contains a list of contracts that should not be whitelisted, and thus default to the very restrictive:
``HashAttachmentConstraint``
For example: For example:
@ -129,7 +111,6 @@ For example:
net.corda.finance.contracts.asset.Cash net.corda.finance.contracts.asset.Cash
net.corda.finance.contracts.asset.CommercialPaper net.corda.finance.contracts.asset.CommercialPaper
Starting the nodes Starting the nodes
~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~

View File

@ -140,6 +140,85 @@ Applies to both gradle deployNodes tasks and/or corda node configuration (node.c
notary = [validating : true] notary = [validating : true]
<<< Fill this in >>>
v3.0 to v3.1
------------
Gradle Plugin Version
^^^^^^^^^^^^^^^^^^^^^
Corda 3.1 uses version 3.1.0 of the gradle plugins and your ``build.gradle`` file should be updated to reflect this.
.. sourcecode:: shell
ext.corda_gradle_plugins_version = '3.1.0'
You will also need to update the ``corda_release_version`` identifier in your project gradle file.
.. sourcecode:: shell
ext.corda_release_version = '3.1-corda'
V2.0 to V3.0
------------
Gradle Plugin Version
^^^^^^^^^^^^^^^^^^^^^
Corda 3.0 uses version 3.0.9 of the gradle plugins and your ``build.gradle`` file should be updated to reflect this.
.. sourcecode:: shell
ext.corda_gradle_plugins_version = '3.0.9'
You will also need to update the ``corda_release_version`` identifier in your project gradle file.
.. sourcecode:: shell
ext.corda_release_version = 'corda-3.0'
Network Map Service
^^^^^^^^^^^^^^^^^^^
With the re-designed network map service the following changes need to be made:
* The network map is no longer provided by a node and thus the ``networkMapService`` config is ignored. Instead the
network map is either provided by the compatibility zone (CZ) operator (who operates the doorman) and available
using the ``compatibilityZoneURL`` config, or is provided using signed node info files which are copied locally.
See :doc:`network-map` for more details, and :doc:`setting-up-a-corda-network.rst` on how to use the network
bootstrapper for deploying a local network.
* Configuration for a notary has been simplified. ``extraAdvertisedServiceIds``, ``notaryNodeAddress``, ``notaryClusterAddresses``
and ``bftSMaRt`` configs have been replaced by a single ``notary`` config object. See :doc:`corda-configuration-file`
for more details.
* The advertisement of the notary to the rest of the network, and its validation type, is no longer determined by the
``extraAdvertisedServiceIds`` config. Instead it has been moved to the control of the network operator via
the introduction of network parameters. The network bootstrapper automatically includes the configured notaries
when generating the network parameters file for a local deployment.
* Any nodes defined in a ``deployNodes`` gradle task performing the function of the network map can be removed, or the
``NetworkMap`` parameter can be removed for any "controller" node which is both the network map and a notary.
* For registering a node with the doorman the ``certificateSigningService`` config has been replaced by ``compatibilityZoneURL``.
Corda Plugins
^^^^^^^^^^^^^
* Corda plugins have been modularised further so the following additional gradle entries are necessary:
For example:
.. sourcecode:: groovy
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
* For existing contract ORM schemas that extend from ``CommonSchemaV1.LinearState`` or ``CommonSchemaV1.FungibleState``, * 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 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 changes are to: in the superclass, but that makes it impossible to properly configure the table name. The required changes are to:
@ -163,84 +242,66 @@ Applies to both gradle deployNodes tasks and/or corda node configuration (node.c
JoinColumn(name = "transaction_id", referencedColumnName = "transaction_id"))) JoinColumn(name = "transaction_id", referencedColumnName = "transaction_id")))
override var participants: MutableSet<AbstractParty>? = null, override var participants: MutableSet<AbstractParty>? = null,
* Shell - to use Shell ensure ``rpcSettings.address`` and ``rpcSettings.adminAddress`` settings are present. AMQP
^^^^
Whilst the enablement of AMQP is a transparent change, as noted in the :doc:`serialization` documentation
the way classes, and states in particular, should be written to work with this new library may require some
alteration to your current implementation.
* With AMQP enabled Java classes must be compiled with the -parameter flag.
* If they aren't, then the error message will complain about ``arg<N>`` being an unknown parameter.
* If recompilation is not viable, a custom serializer can be written as per :doc:`cordapp-custom-serializers`
* It is important to bear in mind that with AMQP there must be an implicit mapping between constructor
parameters and properties you wish included in the serialized form of a class.
* See :doc:`serialization` for more information
* Error messages of the form
``Constructor parameter - "<some parameter of a constructor>" - doesn't refer to a property of "class <some.class.being.serialized>"``
indicate that a class, in the above example ``some.class.being.serialized``, has a parameter on its primary constructor that
doesn't correlate to a property of the class. This is a problem because the Corda AMQP serialization library uses a class's
constructor (default, primary, or annotated) as the means by which instances of the serialized form are reconstituted.
See the section "Mismatched Class Properties / Constructor Parameters" in the :doc:`serialization` documentation
Database schema changes
^^^^^^^^^^^^^^^^^^^^^^^
An H2 database instance (represented on the filesystem as a file called `persistence.mv.db`) used in Corda 1.0 or 2.0
cannot be directly reused with Corda 3.0 due to minor improvements and additions to stabilise the underlying schemas.
Configuration
^^^^^^^^^^^^^
Nodes that do not require SSL to be enabled for RPC clients now need an additional port to be specified as part of their configuration.
To do this, add a block as follows to the nodes configuraiton:
.. sourcecode:: script
rpcSettings {
adminAddress "localhost:10007"
}
to `node.conf` files.
Also, the property `rpcPort` is now deprecated, so it would be preferable to substitute properties specified that way e.g., `rpcPort=10006` with a block as follows:
.. sourcecode:: script
rpcSettings {
address "localhost:10006"
adminAddress "localhost:10007"
}
Equivalent changes should be performed on classes extending `CordformDefinition`.
Testing 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: * The registration mechanism for CorDapps in ``MockNetwork`` unit tests has changed:
* CorDapp registration is now done via the ``cordappPackages`` constructor parameter of MockNetwork. * CorDapp registration is now done via the ``cordappPackages`` constructor parameter of MockNetwork.
@ -399,6 +460,47 @@ Finance
* ``CASH_PROGRAM_ID`` has been moved to ``Cash.PROGRAM_ID``, where ``Cash`` is defined in the * ``CASH_PROGRAM_ID`` has been moved to ``Cash.PROGRAM_ID``, where ``Cash`` is defined in the
``import net.corda.finance.contracts.asset`` package ``import net.corda.finance.contracts.asset`` package
* Many classes have been moved between packages, so you will need to update your imports
.. tip:: We have provided a several scripts (depending upon your operating system of choice) to smooth the upgrade
process for existing projects. This can be found at ``tools\scripts\update-test-packages.sh`` for the Bash shell and
``tools/scripts/upgrade-test-packages.ps1`` for Windows Power Shell users in the source tree
* setCordappPackages and unsetCordappPackages have been removed from the ledger/transaction DSL and the flow test framework,
and are now set via a constructor parameter or automatically when constructing the MockServices or MockNetwork object
* Key constants e.g. ``ALICE_KEY`` have been removed; you can now use TestIdentity to make your own
* The ledger/transaction DSL must now be provided with MockServices as it no longer makes its own
* In transaction blocks, input and output take their arguments as ContractStates rather than lambdas
* Also in transaction blocks, command takes its arguments as CommandDatas rather than lambdas
* The MockServices API has changed; please refer to its API documentation
* TestDependencyInjectionBase has been retired in favour of a JUnit Rule called SerializationEnvironmentRule
* This replaces the initialiseSerialization parameter of ledger/transaction and verifierDriver
* The withTestSerialization method is obsoleted by SerializationEnvironmentRule and has been retired
* MockNetwork now takes a MockNetworkParameters builder to make it more Java-friendly, like driver's DriverParameters
* Similarly, the MockNetwork.createNode methods now take a MockNodeParameters builder
* MockNode constructor parameters are now aggregated in MockNodeArgs for easier subclassing
* MockNetwork.Factory has been retired as you can simply use a lambda
* testNodeConfiguration has been retired, please use a mock object framework of your choice instead
* MockNetwork.createSomeNodes and IntegrationTestCategory have been retired with no replacement
* Starting a flow can now be done directly from a node object. Change calls of the form ``node.getServices().startFlow(...)``
to ``node.startFlow(...)``
* Similarly a tranaction can be executed directly from a node object. Change calls of the form ``node.getDatabase().transaction({ it -> ... })``
to ``node.transaction({() -> ... })``
* ``startFlow`` now returns a ``CordaFuture``, there is no need to call ``startFlow(...).getResultantFuture()``
V1.0 to V2.0 V1.0 to V2.0
------------ ------------
@ -745,4 +847,4 @@ Finance
* Adjust imports of Cash flow references * Adjust imports of Cash flow references
* Adjust the ``StartFlow`` permission in ``gradle.build`` files * Adjust the ``StartFlow`` permission in ``gradle.build`` files
* Adjust imports of the associated flows (``Cash*Flow``, ``TwoPartyTradeFlow``, ``TwoPartyDealFlow``) * Adjust imports of the associated flows (``Cash*Flow``, ``TwoPartyTradeFlow``, ``TwoPartyDealFlow``)

View File

@ -12,7 +12,6 @@ package net.corda.nodeapi.internal.network
import com.typesafe.config.ConfigFactory import com.typesafe.config.ConfigFactory
import net.corda.cordform.CordformNode import net.corda.cordform.CordformNode
import net.corda.core.crypto.SecureHash.Companion.parse
import net.corda.core.identity.Party import net.corda.core.identity.Party
import net.corda.core.internal.* import net.corda.core.internal.*
import net.corda.core.internal.concurrent.fork import net.corda.core.internal.concurrent.fork
@ -21,10 +20,13 @@ import net.corda.core.node.NodeInfo
import net.corda.core.node.NotaryInfo import net.corda.core.node.NotaryInfo
import net.corda.core.node.services.AttachmentId import net.corda.core.node.services.AttachmentId
import net.corda.core.serialization.SerializationContext import net.corda.core.serialization.SerializationContext
import net.corda.core.serialization.SerializedBytes
import net.corda.core.serialization.deserialize
import net.corda.core.serialization.internal.SerializationEnvironmentImpl import net.corda.core.serialization.internal.SerializationEnvironmentImpl
import net.corda.core.serialization.internal._contextSerializationEnv import net.corda.core.serialization.internal._contextSerializationEnv
import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.getOrThrow
import net.corda.core.utilities.seconds import net.corda.core.utilities.seconds
import net.corda.nodeapi.internal.DEV_ROOT_CA
import net.corda.nodeapi.internal.SignedNodeInfo import net.corda.nodeapi.internal.SignedNodeInfo
import net.corda.nodeapi.internal.scanJarForContracts import net.corda.nodeapi.internal.scanJarForContracts
import net.corda.nodeapi.internal.serialization.AMQP_P2P_CONTEXT import net.corda.nodeapi.internal.serialization.AMQP_P2P_CONTEXT
@ -33,11 +35,10 @@ import net.corda.nodeapi.internal.serialization.SerializationFactoryImpl
import net.corda.nodeapi.internal.serialization.amqp.AMQPServerSerializationScheme import net.corda.nodeapi.internal.serialization.amqp.AMQPServerSerializationScheme
import net.corda.nodeapi.internal.serialization.kryo.AbstractKryoSerializationScheme import net.corda.nodeapi.internal.serialization.kryo.AbstractKryoSerializationScheme
import net.corda.nodeapi.internal.serialization.kryo.kryoMagic import net.corda.nodeapi.internal.serialization.kryo.kryoMagic
import java.io.PrintStream
import java.nio.file.Files import java.nio.file.Files
import java.nio.file.Path import java.nio.file.Path
import java.nio.file.Paths import java.nio.file.Paths
import java.nio.file.StandardCopyOption import java.nio.file.StandardCopyOption.REPLACE_EXISTING
import java.time.Instant import java.time.Instant
import java.util.concurrent.Executors import java.util.concurrent.Executors
import java.util.concurrent.TimeoutException import java.util.concurrent.TimeoutException
@ -57,12 +58,11 @@ class NetworkBootstrapper {
) )
private const val LOGS_DIR_NAME = "logs" private const val LOGS_DIR_NAME = "logs"
private const val WHITELIST_FILE_NAME = "whitelist.txt"
private const val EXCLUDE_WHITELIST_FILE_NAME = "exclude_whitelist.txt" private const val EXCLUDE_WHITELIST_FILE_NAME = "exclude_whitelist.txt"
@JvmStatic @JvmStatic
fun main(args: Array<String>) { fun main(args: Array<String>) {
val baseNodeDirectory = args.firstOrNull() ?: throw IllegalArgumentException("Expecting first argument which is the nodes' parent directory") val baseNodeDirectory = requireNotNull(args.firstOrNull()) { "Expecting first argument which is the nodes' parent directory" }
val cordapps = if (args.size > 1) args.toList().drop(1) else null val cordapps = if (args.size > 1) args.toList().drop(1) else null
NetworkBootstrapper().bootstrap(Paths.get(baseNodeDirectory).toAbsolutePath().normalize(), cordapps) NetworkBootstrapper().bootstrap(Paths.get(baseNodeDirectory).toAbsolutePath().normalize(), cordapps)
} }
@ -80,15 +80,17 @@ class NetworkBootstrapper {
try { try {
println("Waiting for all nodes to generate their node-info files...") println("Waiting for all nodes to generate their node-info files...")
val nodeInfoFiles = gatherNodeInfoFiles(processes, nodeDirs) val nodeInfoFiles = gatherNodeInfoFiles(processes, nodeDirs)
println("Distributing all node info-files to all nodes") println("Distributing all node-info files to all nodes")
distributeNodeInfos(nodeDirs, nodeInfoFiles) distributeNodeInfos(nodeDirs, nodeInfoFiles)
print("Loading existing network parameters... ")
val existingNetParams = loadNetworkParameters(nodeDirs)
println(existingNetParams ?: "none found")
println("Gathering notary identities") println("Gathering notary identities")
val notaryInfos = gatherNotaryInfos(nodeInfoFiles) val notaryInfos = gatherNotaryInfos(nodeInfoFiles)
println("Notary identities to be used in network parameters: ${notaryInfos.joinToString("; ") { it.prettyPrint() }}") println("Generating contract implementations whitelist")
val mergedWhiteList = generateWhitelist(directory / WHITELIST_FILE_NAME, directory / EXCLUDE_WHITELIST_FILE_NAME, cordapps?.distinct()) val newWhitelist = generateWhitelist(existingNetParams, directory / EXCLUDE_WHITELIST_FILE_NAME, cordapps?.distinct())
println("Updating whitelist") val netParams = installNetworkParameters(notaryInfos, newWhitelist, existingNetParams, nodeDirs)
overwriteWhitelist(directory / WHITELIST_FILE_NAME, mergedWhiteList) println("${if (existingNetParams == null) "New" else "Updated"} $netParams")
installNetworkParameters(notaryInfos, nodeDirs, mergedWhiteList)
println("Bootstrapping complete!") println("Bootstrapping complete!")
} finally { } finally {
_contextSerializationEnv.set(null) _contextSerializationEnv.set(null)
@ -106,15 +108,15 @@ class NetworkBootstrapper {
val nodeName = confFile.fileName.toString().removeSuffix("_node.conf") val nodeName = confFile.fileName.toString().removeSuffix("_node.conf")
println("Generating directory for $nodeName") println("Generating directory for $nodeName")
val nodeDir = (directory / nodeName).createDirectories() val nodeDir = (directory / nodeName).createDirectories()
confFile.moveTo(nodeDir / "node.conf", StandardCopyOption.REPLACE_EXISTING) confFile.moveTo(nodeDir / "node.conf", REPLACE_EXISTING)
webServerConfFiles.firstOrNull { directory.relativize(it).toString().removeSuffix("_web-server.conf") == nodeName }?.moveTo(nodeDir / "web-server.conf", StandardCopyOption.REPLACE_EXISTING) webServerConfFiles.firstOrNull { directory.relativize(it).toString().removeSuffix("_web-server.conf") == nodeName }?.moveTo(nodeDir / "web-server.conf", REPLACE_EXISTING)
Files.copy(cordaJar, (nodeDir / "corda.jar"), StandardCopyOption.REPLACE_EXISTING) cordaJar.copyToDirectory(nodeDir, REPLACE_EXISTING)
} }
Files.delete(cordaJar) Files.delete(cordaJar)
} }
private fun extractCordaJarTo(directory: Path): Path { private fun extractCordaJarTo(directory: Path): Path {
val cordaJarPath = (directory / "corda.jar") val cordaJarPath = directory / "corda.jar"
if (!cordaJarPath.exists()) { if (!cordaJarPath.exists()) {
println("No corda jar found in root directory. Extracting from jar") println("No corda jar found in root directory. Extracting from jar")
Thread.currentThread().contextClassLoader.getResourceAsStream("corda.jar").copyTo(cordaJarPath) Thread.currentThread().contextClassLoader.getResourceAsStream("corda.jar").copyTo(cordaJarPath)
@ -147,7 +149,7 @@ class NetworkBootstrapper {
} }
return try { return try {
future.getOrThrow(60.seconds) future.getOrThrow(timeout = 60.seconds)
} catch (e: TimeoutException) { } catch (e: TimeoutException) {
println("...still waiting. If this is taking longer than usual, check the node logs.") println("...still waiting. If this is taking longer than usual, check the node logs.")
future.getOrThrow() future.getOrThrow()
@ -158,7 +160,7 @@ class NetworkBootstrapper {
for (nodeDir in nodeDirs) { for (nodeDir in nodeDirs) {
val additionalNodeInfosDir = (nodeDir / CordformNode.NODE_INFO_DIRECTORY).createDirectories() val additionalNodeInfosDir = (nodeDir / CordformNode.NODE_INFO_DIRECTORY).createDirectories()
for (nodeInfoFile in nodeInfoFiles) { for (nodeInfoFile in nodeInfoFiles) {
nodeInfoFile.copyToDirectory(additionalNodeInfosDir, StandardCopyOption.REPLACE_EXISTING) nodeInfoFile.copyToDirectory(additionalNodeInfosDir, REPLACE_EXISTING)
} }
} }
} }
@ -178,25 +180,67 @@ class NetworkBootstrapper {
}.distinct() // We need distinct as nodes part of a distributed notary share the same notary identity }.distinct() // We need distinct as nodes part of a distributed notary share the same notary identity
} }
private fun installNetworkParameters(notaryInfos: List<NotaryInfo>, nodeDirs: List<Path>, whitelist: Map<String, List<AttachmentId>>) { private fun loadNetworkParameters(nodeDirs: List<Path>): NetworkParameters? {
// TODO Add config for minimumPlatformVersion, maxMessageSize and maxTransactionSize val netParamsFilesGrouped = nodeDirs.mapNotNull {
val copier = NetworkParametersCopier(NetworkParameters( val netParamsFile = it / NETWORK_PARAMS_FILE_NAME
minimumPlatformVersion = 1, if (netParamsFile.exists()) netParamsFile else null
notaries = notaryInfos, }.groupBy { SerializedBytes<SignedNetworkParameters>(it.readAll()) }
modifiedTime = Instant.now(),
maxMessageSize = 10485760,
maxTransactionSize = Int.MAX_VALUE,
epoch = 1,
whitelistedContractImplementations = whitelist
), overwriteFile = true)
nodeDirs.forEach { copier.install(it) } when (netParamsFilesGrouped.size) {
0 -> return null
1 -> return netParamsFilesGrouped.keys.first().deserialize().verifiedNetworkMapCert(DEV_ROOT_CA.certificate)
}
val msg = StringBuilder("Differing sets of network parameters were found. Make sure all the nodes have the same " +
"network parameters by copying over the correct $NETWORK_PARAMS_FILE_NAME file.\n\n")
netParamsFilesGrouped.forEach { bytes, netParamsFiles ->
netParamsFiles.map { it.parent.fileName }.joinTo(msg, ", ")
msg.append(":\n")
val netParamsString = try {
bytes.deserialize().verifiedNetworkMapCert(DEV_ROOT_CA.certificate).toString()
} catch (e: Exception) {
"Invalid network parameters file: $e"
}
msg.append(netParamsString)
msg.append("\n\n")
}
throw IllegalStateException(msg.toString())
} }
private fun generateWhitelist(whitelistFile: Path, excludeWhitelistFile: Path, cordapps: List<String>?): Map<String, List<AttachmentId>> { private fun installNetworkParameters(notaryInfos: List<NotaryInfo>,
val existingWhitelist = if (whitelistFile.exists()) readContractWhitelist(whitelistFile) else emptyMap() whitelist: Map<String, List<AttachmentId>>,
existingNetParams: NetworkParameters?,
nodeDirs: List<Path>): NetworkParameters {
val networkParameters = if (existingNetParams != null) {
existingNetParams.copy(
notaries = notaryInfos,
modifiedTime = Instant.now(),
whitelistedContractImplementations = whitelist,
epoch = existingNetParams.epoch + 1
)
} else {
NetworkParameters(
minimumPlatformVersion = 1,
notaries = notaryInfos,
modifiedTime = Instant.now(),
maxMessageSize = 10485760,
maxTransactionSize = Int.MAX_VALUE,
whitelistedContractImplementations = whitelist,
epoch = 1
)
}
// TODO Add config for minimumPlatformVersion, maxMessageSize and maxTransactionSize
val copier = NetworkParametersCopier(networkParameters, overwriteFile = true)
nodeDirs.forEach { copier.install(it) }
return networkParameters
}
println(if (existingWhitelist.isEmpty()) "No existing whitelist file found." else "Found existing whitelist: $whitelistFile") private fun generateWhitelist(networkParameters: NetworkParameters?,
excludeWhitelistFile: Path,
cordapps: List<String>?): Map<String, List<AttachmentId>> {
val existingWhitelist = networkParameters?.whitelistedContractImplementations ?: emptyMap()
val excludeContracts = if (excludeWhitelistFile.exists()) readExcludeWhitelist(excludeWhitelistFile) else emptyList() val excludeContracts = if (excludeWhitelistFile.exists()) readExcludeWhitelist(excludeWhitelistFile) else emptyList()
if (excludeContracts.isNotEmpty()) { if (excludeContracts.isNotEmpty()) {
@ -210,38 +254,15 @@ class NetworkBootstrapper {
} }
}?.filter { (contractClassName, _) -> contractClassName !in excludeContracts }?.toMap() ?: emptyMap() }?.filter { (contractClassName, _) -> contractClassName !in excludeContracts }?.toMap() ?: emptyMap()
println("Calculating whitelist for current installed CorDapps..") return (newWhiteList.keys + existingWhitelist.keys).map { contractClassName ->
val merged = (newWhiteList.keys + existingWhitelist.keys).map { contractClassName ->
val existing = existingWhitelist[contractClassName] ?: emptyList() val existing = existingWhitelist[contractClassName] ?: emptyList()
val newHash = newWhiteList[contractClassName] val newHash = newWhiteList[contractClassName]
contractClassName to (if (newHash == null || newHash in existing) existing else existing + newHash) contractClassName to (if (newHash == null || newHash in existing) existing else existing + newHash)
}.toMap() }.toMap()
println("CorDapp whitelist " + (if (existingWhitelist.isEmpty()) "generated" else "updated") + " in $whitelistFile")
return merged
}
private fun overwriteWhitelist(whitelistFile: Path, mergedWhiteList: Map<String, List<AttachmentId>>) {
PrintStream(whitelistFile.toFile().outputStream()).use { out ->
mergedWhiteList.forEach { (contract, attachments) ->
out.println("$contract:${attachments.joinToString(",")}")
}
}
}
private fun readContractWhitelist(file: Path): Map<String, List<AttachmentId>> {
return file.readAllLines()
.map { line -> line.split(":") }
.map { (contract, attachmentIds) ->
contract to (attachmentIds.split(",").map(::parse))
}.toMap()
} }
private fun readExcludeWhitelist(file: Path): List<String> = file.readAllLines().map(String::trim) private fun readExcludeWhitelist(file: Path): List<String> = file.readAllLines().map(String::trim)
private fun NotaryInfo.prettyPrint(): String = "${identity.name} (${if (validating) "" else "non-"}validating)"
private fun NodeInfo.notaryIdentity(): Party { private fun NodeInfo.notaryIdentity(): Party {
return when (legalIdentities.size) { return when (legalIdentities.size) {
// Single node notaries have just one identity like all other nodes. This identity is the notary identity // Single node notaries have just one identity like all other nodes. This identity is the notary identity

View File

@ -315,7 +315,7 @@ fun propertiesForSerializationFromSetters(
"takes too many arguments") "takes too many arguments")
} }
val setterType = setter.parameterTypes[0]!! val setterType = setter.genericParameterTypes[0]!!
if ((property.value.field != null) && if ((property.value.field != null) &&
(!(TypeToken.of(property.value.field?.genericType!!).isSupertypeOf(setterType)))) { (!(TypeToken.of(property.value.field?.genericType!!).isSupertypeOf(setterType)))) {
@ -324,11 +324,11 @@ fun propertiesForSerializationFromSetters(
"${property.value.field?.genericType!!}") "${property.value.field?.genericType!!}")
} }
// make sure the setter returns the same type (within inheritance bounds) the getter accepts // Make sure the getter returns the same type (within inheritance bounds) the setter accepts.
if (!(TypeToken.of (setterType).isSupertypeOf(getter.returnType))) { if (!(TypeToken.of (getter.genericReturnType).isSupertypeOf(setterType))) {
throw NotSerializableException("Defined setter for parameter ${property.value.field?.name} " + throw NotSerializableException("Defined setter for parameter ${property.value.field?.name} " +
"takes parameter of type $setterType yet the defined getter returns a value of type " + "takes parameter of type $setterType yet the defined getter returns a value of type " +
"${getter.returnType}") "${getter.returnType} [${getter.genericReturnType}]")
} }
this += PropertyAccessorGetterSetter( this += PropertyAccessorGetterSetter(
idx++, idx++,

View File

@ -17,6 +17,8 @@ import org.junit.Test;
import static org.junit.Assert.*; import static org.junit.Assert.*;
import java.io.NotSerializableException; import java.io.NotSerializableException;
import java.util.ArrayList;
import java.util.List;
public class SetterConstructorTests { public class SetterConstructorTests {
@ -74,6 +76,13 @@ public class SetterConstructorTests {
public void setC(int c) { this.c = c; } public void setC(int c) { this.c = c; }
} }
static class CIntList {
private List<Integer> l;
public List getL() { return l; }
public void setL(List<Integer> l) { this.l = l; }
}
static class Inner1 { static class Inner1 {
private String a; private String a;
@ -325,4 +334,29 @@ public class SetterConstructorTests {
Assertions.assertThatThrownBy(() -> new SerializationOutput(factory1).serialize(tm)).isInstanceOf( Assertions.assertThatThrownBy(() -> new SerializationOutput(factory1).serialize(tm)).isInstanceOf(
NotSerializableException.class); NotSerializableException.class);
} }
// This not blowing up means it's working
@Test
public void intList() throws NotSerializableException {
CIntList cil = new CIntList();
List<Integer> l = new ArrayList<>();
l.add(1);
l.add(2);
l.add(3);
cil.setL(l);
EvolutionSerializerGetterBase evolutionSerialiserGetter = new EvolutionSerializerGetter();
FingerPrinter fingerPrinter = new SerializerFingerPrinter();
SerializerFactory factory1 = new SerializerFactory(
AllWhitelist.INSTANCE,
ClassLoader.getSystemClassLoader(),
evolutionSerialiserGetter,
fingerPrinter);
// if we've got super / sub types on the setter vs the underlying type the wrong way around this will
// explode. See CORDA-1229 (https://r3-cev.atlassian.net/browse/CORDA-1229)
new DeserializationInput(factory1).deserialize(new SerializationOutput(factory1).serialize(cil), CIntList.class);
}
} }

View File

@ -1320,6 +1320,5 @@ class SerializationOutputTests(private val compression: CordaSerializationEncodi
C(12).serializeE() C(12).serializeE()
}.withMessageContaining("has synthetic fields and is likely a nested inner class") }.withMessageContaining("has synthetic fields and is likely a nested inner class")
} }
} }