mirror of
https://github.com/corda/corda.git
synced 2025-02-20 09:26:41 +00:00
Joel upgrade docs (#3730)
* Instructions on how to perform H2 DB upgrades without starting the node. * Formatting error. Adds required subsections in H2 docs. * Merges duplicated information about H2 DB access. * Drops script references for now. * Clarifies upgrade notes. * Better title. * Inr * Addresses review comments. * Addresses review comments.
This commit is contained in:
parent
ff298e17e1
commit
286dc7b77d
@ -1,20 +1,37 @@
|
||||
Database access when running H2
|
||||
===============================
|
||||
When running a node using the H2 database, the node can be configured to expose its internal database over socket which
|
||||
can be browsed using any tool that can use JDBC drivers.
|
||||
The JDBC URL is printed during node startup to the log and will typically look like this:
|
||||
|
||||
``jdbc:h2:tcp://localhost:31339/node``
|
||||
.. contents::
|
||||
|
||||
The username and password can be altered in the :doc:`corda-configuration-file` but default to username "sa" and a blank
|
||||
password.
|
||||
Configuring the username and password
|
||||
-------------------------------------
|
||||
|
||||
Any database browsing tool that supports JDBC can be used, but if you have IntelliJ Ultimate edition then there is
|
||||
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.
|
||||
The database (a file called ``persistence.mv.db``) is created when the node first starts up. By default, it has an
|
||||
administrator user ``sa`` and a blank password. The node requires the user with administrator permissions in order to
|
||||
creates tables upon the first startup or after deploying new CorDapps with their own tables. The database password is
|
||||
required only when the H2 database is exposed on non-localhost address (which is disabled by default).
|
||||
|
||||
By default, the node's H2 database is not exposed. This behaviour can be overridden by specifying the full network
|
||||
address (interface and port), using the new ``h2Settings`` syntax in the node configuration.
|
||||
This username and password can be changed in node configuration:
|
||||
|
||||
.. sourcecode:: groovy
|
||||
|
||||
dataSourceProperties = {
|
||||
dataSource.user = [USER]
|
||||
dataSource.password = [PASSWORD]
|
||||
}
|
||||
|
||||
Note that changing the user/password for the existing node in ``node.conf`` will not update them in the H2 database.
|
||||
You need to log into the database first to create a new user or change a user's password.
|
||||
|
||||
Connecting via a socket on a running node
|
||||
-----------------------------------------
|
||||
|
||||
Configuring the port
|
||||
^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Nodes backed by an H2 database will not expose this database by default. To configure the node to expose its internal
|
||||
database over a socket which can be browsed using any tool that can use JDBC drivers, you must specify the full network
|
||||
address (interface and port) using the ``h2Settings`` syntax in the node configuration.
|
||||
|
||||
The configuration below will restrict the H2 service to run on ``localhost``:
|
||||
|
||||
@ -32,8 +49,8 @@ If you want H2 to auto-select a port (mimicking the old ``h2Port`` behaviour), y
|
||||
address: "localhost:0"
|
||||
}
|
||||
|
||||
If remote access is required, the address can be changed to ``0.0.0.0``.
|
||||
The node requires a database password to be set when the database is exposed on the network interface to listen on.
|
||||
If remote access is required, the address can be changed to ``0.0.0.0`` to listen on all interfaces. A password must be
|
||||
set for the database user before doing so.
|
||||
|
||||
.. sourcecode:: groovy
|
||||
|
||||
@ -44,5 +61,44 @@ The node requires a database password to be set when the database is exposed on
|
||||
dataSource.password : "strongpassword"
|
||||
}
|
||||
|
||||
The previous ``h2Port`` syntax is now deprecated. ``h2Port`` will continue to work but the database
|
||||
will only be accessible on localhost.
|
||||
.. note:: The previous ``h2Port`` syntax is now deprecated. ``h2Port`` will continue to work but the database will only
|
||||
be accessible on localhost.
|
||||
|
||||
Connecting to the database
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
The JDBC URL is printed during node startup to the log and will typically look like this:
|
||||
|
||||
``jdbc:h2:tcp://localhost:31339/node``
|
||||
|
||||
Any database browsing tool that supports JDBC can be used.
|
||||
|
||||
Connecting using the H2 Console
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
* Download the **last stable** `h2 platform-independent zip <http://www.h2database.com/html/download.html>`_, unzip the
|
||||
zip, and navigate in a terminal window to the unzipped folder
|
||||
|
||||
* Change directories to the bin folder: ``cd h2/bin``
|
||||
|
||||
* Run the following command to open the h2 web console in a web browser tab:
|
||||
|
||||
* Unix: ``sh h2.sh``
|
||||
* Windows: ``h2.bat``
|
||||
|
||||
* Paste the node's JDBC URL into the JDBC URL field and click ``Connect``, using the default username (``sa``) and no
|
||||
password (unless configured otherwise)
|
||||
|
||||
You will be presented with a web interface that shows the contents of your node's storage and vault, and provides an
|
||||
interface for you to query them using SQL.
|
||||
|
||||
.. _h2_relative_path:
|
||||
|
||||
Connecting directly to the node's ``persistence.mv.db`` file
|
||||
------------------------------------------------------------
|
||||
|
||||
You can also use the H2 Console to connect directly to the node's ``persistence.mv.db`` file. Ensure the node is off
|
||||
before doing so, as access to the database file requires exclusive access. If the node is still running, the H2 Console
|
||||
will return the following error:
|
||||
``Database may be already in use: null. Possible solutions: close all other connection(s); use the server mode [90020-196]``.
|
||||
|
||||
``jdbc:h2:~/path/to/file/persistence``
|
||||
|
@ -3,57 +3,7 @@ Node database
|
||||
|
||||
Default in-memory database
|
||||
--------------------------
|
||||
By default, nodes store their data in an H2 database.
|
||||
The database (a file persistence.mv.db) is created at the first node startup with the administrator user 'sa' and a blank password.
|
||||
The user name and password can be changed in node configuration:
|
||||
|
||||
.. sourcecode:: groovy
|
||||
|
||||
dataSourceProperties = {
|
||||
dataSource.user = [USER]
|
||||
dataSource.password = [PASSWORD]
|
||||
}
|
||||
|
||||
Note, changing user/password for the existing node in node.conf will not update them in the H2 database,
|
||||
you need to login to the database first to create new user or change the user password.
|
||||
The database password is required only when the H2 database is exposed on non-localhost address (which is disabled by default).
|
||||
The node requires the user with administrator permissions in order to creates tables upon the first startup
|
||||
or after deplying new CorDapps with own tables.
|
||||
|
||||
You can connect directly to a running node's database to see its
|
||||
stored states, transactions and attachments as follows:
|
||||
|
||||
* Enable the H2 database access in the node configuration using the following syntax:
|
||||
|
||||
.. sourcecode:: groovy
|
||||
|
||||
h2Settings {
|
||||
address: "localhost:0"
|
||||
}
|
||||
|
||||
* Download the **last stable** `h2 platform-independent zip <http://www.h2database.com/html/download.html>`_, unzip the zip, and
|
||||
navigate in a terminal window to the unzipped folder
|
||||
* Change directories to the bin folder: ``cd h2/bin``
|
||||
|
||||
* Run the following command to open the h2 web console in a web browser tab:
|
||||
|
||||
* Unix: ``sh h2.sh``
|
||||
* Windows: ``h2.bat``
|
||||
|
||||
* Find the node's JDBC connection string. Each node outputs its connection string in the terminal
|
||||
window as it starts up. In a terminal window where a node is running, look for the following string:
|
||||
|
||||
``Database connection URL is : jdbc:h2:tcp://10.18.0.150:56736/node``
|
||||
|
||||
* Paste this string into the JDBC URL field and click ``Connect``, using the default username (``sa``) and no password.
|
||||
|
||||
You will be presented with a web interface that shows the contents of your node's storage and vault, and provides an
|
||||
interface for you to query them using SQL.
|
||||
|
||||
The default behaviour is to expose the H2 database on localhost. This can be overridden in the
|
||||
node configuration using ``h2Settings.address`` and specifying the address of the network interface to listen on,
|
||||
or simply using ``0.0.0.0:0`` to listen on all interfaces. The node requires a database password to be set when
|
||||
the database is exposed on the network interface to listen on.
|
||||
By default, nodes store their data in an H2 database. See :doc:`node-database-access-h2`.
|
||||
|
||||
PostgreSQL
|
||||
----------
|
||||
|
@ -9,24 +9,27 @@ 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:
|
||||
|
||||
.. sourcecode:: shell
|
||||
|
||||
ext.corda_release_version = '1.0.0'
|
||||
ext.corda_gradle_plugins_version = '1.0.0'
|
||||
ext.corda_release_version = 'x.y.0'
|
||||
ext.corda_gradle_plugins_version = 'x.y.0'
|
||||
|
||||
It may be necessary to update the version of major dependencies:
|
||||
* It may also be necessary to update the version of major dependencies:
|
||||
|
||||
.. sourcecode:: shell
|
||||
|
||||
ext.kotlin_version = '1.1.4'
|
||||
ext.quasar_version = '0.7.9'
|
||||
ext.kotlin_version = 'x.y.z'
|
||||
ext.quasar_version = 'x.y.z'
|
||||
|
||||
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.
|
||||
* 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
|
||||
|
||||
We also strongly recommend cross referencing with the :doc:`changelog` to confirm changes.
|
||||
* To run database upgrades against H2, you'll need to connect to the node's database without starting the node. You can
|
||||
do this by connecting directly to the node's ``persistence.mv.db`` file. See :ref:`h2_relative_path`
|
||||
|
||||
UNRELEASED
|
||||
----------
|
||||
|
@ -15,26 +15,8 @@ Upgrading a CorDapp (outside of platform version upgrades)
|
||||
CorDapp versioning
|
||||
------------------
|
||||
The Corda platform does not mandate a version number on a per-CorDapp basis. Different elements of a CorDapp are
|
||||
allowed to evolve separately:
|
||||
|
||||
* States
|
||||
* Contracts
|
||||
* Services
|
||||
* Flows
|
||||
* Utilities and library functions
|
||||
* All, or a subset, of the above
|
||||
|
||||
Sometimes, however, a change to one element will require changes to other elements. For example, changing a shared data
|
||||
structure may require flow changes that are not backwards-compatible.
|
||||
|
||||
Areas of consideration
|
||||
----------------------
|
||||
This document will consider the following types of versioning:
|
||||
|
||||
* Flow versioning
|
||||
* State and contract versioning
|
||||
* State and state schema versioning
|
||||
* Serialisation of custom types
|
||||
allowed to evolve separately. Sometimes, however, a change to one element will require changes to other elements. For
|
||||
example, changing a shared data structure may require flow changes that are not backwards-compatible.
|
||||
|
||||
Flow versioning
|
||||
---------------
|
||||
@ -48,8 +30,8 @@ The ``version`` property, which defaults to 1, specifies the flow's version. Thi
|
||||
whenever there is a release of a flow which has changes that are not backwards-compatible. A non-backwards compatible
|
||||
change is one that changes the interface of the flow.
|
||||
|
||||
What defines the interface of a flow?
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Defining a flow's interface
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
The flow interface is defined by the sequence of ``send`` and ``receive`` calls between an ``InitiatingFlow`` and an
|
||||
``InitiatedBy`` flow, including the types of the data sent and received. We can picture a flow's interface as follows:
|
||||
|
||||
@ -75,8 +57,8 @@ As long as both the ``InitiatingFlow`` and the ``InitiatedBy`` flows conform to
|
||||
be implemented in any way you see fit (including adding proprietary business logic that is not shared with other
|
||||
parties).
|
||||
|
||||
What constitutes a non-backwards compatible flow change?
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Non-backwards compatible flow changes
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
A flow can become backwards-incompatible in two main ways:
|
||||
|
||||
* The sequence of ``send`` and ``receive`` calls changes:
|
||||
@ -86,8 +68,8 @@ A flow can become backwards-incompatible in two main ways:
|
||||
|
||||
* The types of the ``send`` and ``receive`` calls changes
|
||||
|
||||
What happens when running flows with incompatible versions?
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Consequences of running flows with incompatible versions
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Pairs of ``InitiatingFlow`` flows and ``InitiatedBy`` flows that have incompatible interfaces are likely to exhibit the
|
||||
following behaviour:
|
||||
|
||||
@ -98,24 +80,8 @@ following behaviour:
|
||||
* One of the flows ends with an exception: "Counterparty flow terminated early on the other side", because one flow
|
||||
sends some data to another flow, but the latter flow has already ended
|
||||
|
||||
How do I upgrade my flows?
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
1. Update the flow and test the changes. Increment the flow version number in the ``InitiatingFlow`` annotation.
|
||||
2. Ensure that all versions of the existing flow have finished running and there are no pending ``SchedulableFlows`` on
|
||||
any of the nodes on the business network. This can be done by *draining the node* (see below).
|
||||
3. Shut down the node.
|
||||
4. Replace the existing CorDapp JAR with the CorDapp JAR containing the new flow.
|
||||
5. Start the node.
|
||||
|
||||
If you shut down all nodes and upgrade them all at the same time, any incompatible change can be made.
|
||||
|
||||
In situations where some nodes may still be using previous versions of a flow and thus new versions of your flow may
|
||||
talk to old versions, the updated flows need to be backwards-compatible. This will be the case for almost any real
|
||||
deployment in which you cannot easily coordinate the roll-out of new code across the network.
|
||||
|
||||
How do I ensure flow backwards-compatibility?
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Ensuring flow backwards-compatibility
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
The ``InitiatingFlow`` version number is included in the flow session handshake and exposed to both parties via the
|
||||
``FlowLogic.getFlowContext`` method. This method takes a ``Party`` and returns a ``FlowContext`` object which describes
|
||||
the flow running on the other side. In particular, it has a ``flowVersion`` property which can be used to
|
||||
@ -159,8 +125,8 @@ This code shows a flow that in its first version expected to receive an Int, but
|
||||
expect a String. This flow is still able to communicate with parties that are running the older CorDapp containing
|
||||
the older flow.
|
||||
|
||||
How do I deal with interface changes to inlined subflows?
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Handling interface changes to inlined subflows
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Here is an example of an in-lined subflow:
|
||||
|
||||
.. container:: codeset
|
||||
@ -254,8 +220,26 @@ Flows which are not an ``InitiatingFlow`` or ``InitiatedBy`` flow, or inlined su
|
||||
``InitiatingFlow`` or ``InitiatedBy`` flow, can be updated without consideration of backwards-compatibility. Flows of
|
||||
this type include utility flows for querying the vault and flows for reaching out to external systems.
|
||||
|
||||
Flow drains
|
||||
~~~~~~~~~~~
|
||||
Performing flow upgrades
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
1. Update the flow and test the changes. Increment the flow version number in the ``InitiatingFlow`` annotation
|
||||
2. Ensure that all versions of the existing flow have finished running and there are no pending ``SchedulableFlows`` on
|
||||
any of the nodes on the business network. This can be done by :ref:`draining_the_node`
|
||||
3. Shut down the node
|
||||
4. Replace the existing CorDapp JAR with the CorDapp JAR containing the new flow
|
||||
5. Start the node
|
||||
|
||||
If you shut down all nodes and upgrade them all at the same time, any incompatible change can be made.
|
||||
|
||||
In situations where some nodes may still be using previous versions of a flow and thus new versions of your flow may
|
||||
talk to old versions, the updated flows need to be backwards-compatible. This will be the case for almost any real
|
||||
deployment in which you cannot easily coordinate the roll-out of new code across the network.
|
||||
|
||||
.. _draining_the_node:
|
||||
|
||||
Draining the node
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
A flow *checkpoint* is a serialised snapshot of the flow's stack frames and any objects reachable from the stack.
|
||||
Checkpoints are saved to the database automatically when a flow suspends or resumes, which typically happens when
|
||||
@ -280,34 +264,36 @@ Contract and state versioning
|
||||
|
||||
There are two types of contract/state upgrade:
|
||||
|
||||
1. *Implicit:* By allowing multiple implementations of the contract ahead of time, using constraints. See :doc:`api-contract-constraints` to learn more.
|
||||
2. *Explicit:* By creating a special *contract upgrade transaction* and getting all participants of a state to sign it using the
|
||||
contract upgrade flows.
|
||||
1. *Implicit:* By allowing multiple implementations of the contract ahead of time, using constraints. See
|
||||
:doc:`api-contract-constraints` to learn more
|
||||
2. *Explicit:* By creating a special *contract upgrade transaction* and getting all participants of a state to sign it
|
||||
using the contract upgrade flows
|
||||
|
||||
This section of the documentation focuses only on the *explicit* type of upgrade.
|
||||
This section of the documentation focuses only on *explicit* upgrades.
|
||||
|
||||
In an explicit upgrade contracts and states can be changed in arbitrary ways, if and only if all of the state'
|
||||
s participants agree to the proposed upgrade. The following combinations of upgrades are possible:
|
||||
In an explicit upgrade, contracts and states can be changed in arbitrary ways, if and only if all of the state's
|
||||
participants agree to the proposed upgrade. The following combinations of upgrades are possible:
|
||||
|
||||
* A contract is upgraded while the state definition remains the same.
|
||||
* A state is upgraded while the contract stays the same.
|
||||
* The state and the contract are updated simultaneously.
|
||||
* A contract is upgraded while the state definition remains the same
|
||||
* A state is upgraded while the contract stays the same
|
||||
* The state and the contract are updated simultaneously
|
||||
|
||||
The procedure for updating a state or a contract using a flag-day approach is quite simple:
|
||||
Performing contract and state upgrades
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
* Update and test the state or contract.
|
||||
* Produce a new CorDapp JAR file and distribute it to all the relevant parties.
|
||||
* Each node operator stops their node, replaces the existing JAR with the new one, and restarts. They may wish to do
|
||||
a node drain first to avoid the definition of states or contracts changing whilst a flow is in progress.
|
||||
* Run the contract upgrade authorisation flow for each state that requires updating on every node.
|
||||
* For each state, one node should run the contract upgrade initiation flow, which will contact the rest.
|
||||
1. Preserve the existing state and contract definitions
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
Currently, all nodes must **permanently** keep **all** old state and contract definitions on their node's classpath
|
||||
|
||||
Update Process
|
||||
~~~~~~~~~~~~~~
|
||||
.. note:: Once the contract-code-as-an-attachment feature has been implemented, nodes will only be required to keep the
|
||||
old state and contract definitions on their node's classpath for the duration of the upgrade
|
||||
|
||||
Writing the new state and contract definitions
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
Start by updating the contract and/or state definitions. There are no restrictions on how states are updated. However,
|
||||
Changing a state or contract's package constitutes a definition change. If you want to move a state or contract
|
||||
definition to a new package, you must also preserve the definition in the old package
|
||||
|
||||
2. Write the new state and contract definitions
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
Update the contract and/or state definitions. There are no restrictions on how states are updated. However,
|
||||
upgraded contracts must implement the ``UpgradedContract`` interface. This interface is defined as:
|
||||
|
||||
.. sourcecode:: kotlin
|
||||
@ -337,18 +323,29 @@ For example, in case of hash constraints the hash of the legacy JAR file should
|
||||
override val legacyContractConstraint: AttachmentConstraint
|
||||
get() = HashAttachmentConstraint(SecureHash.parse("E02BD2B9B010BBCE49C0D7C35BECEF2C79BEB2EE80D902B54CC9231418A4FA0C"))
|
||||
|
||||
Authorising the upgrade
|
||||
^^^^^^^^^^^^^^^^^^^^^^^
|
||||
Once the new states and contracts are on the classpath for all the relevant nodes, the next step is for all nodes to
|
||||
run the ``ContractUpgradeFlow.Authorise`` flow. This flow takes a ``StateAndRef`` of the state to update as well as a
|
||||
reference to the new contract, which must implement the ``UpgradedContract`` interface.
|
||||
3. Create the new CorDapp JAR
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
Produce a new CorDapp JAR file and distribute it to all the relevant nodes. This JAR file should contain all the old
|
||||
contract and state definitions, plus any new contract and state definitions.
|
||||
|
||||
4. Restart the nodes
|
||||
^^^^^^^^^^^^^^^^^^^^
|
||||
Have each node operator stop their node, replace the existing CorDapp JAR with the new CorDapp JAR, and restart their
|
||||
node. They may wish to do a :ref:`node drain <draining_the_node>` first to avoid the definition of states or contracts
|
||||
changing whilst a flow is in progress.
|
||||
|
||||
5. Authorise the upgrade
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
Once the new states and contracts are on the classpath for all the relevant nodes, the nodes must all run the
|
||||
``ContractUpgradeFlow.Authorise`` flow. This flow takes a ``StateAndRef`` of the state to update as well as a reference
|
||||
to the new contract, which must implement the ``UpgradedContract`` interface.
|
||||
|
||||
At any point, a node administrator may de-authorise a contract upgrade by running the
|
||||
``ContractUpgradeFlow.Deauthorise`` flow.
|
||||
|
||||
Performing the upgrade
|
||||
6. Perform the upgrade
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
Once all nodes have performed the authorisation process, a participant must be chosen to initiate the upgrade via the
|
||||
Once all nodes have performed the authorisation process, a **single** node must initiate the upgrade via the
|
||||
``ContractUpgradeFlow.Initiate`` flow for each state object. This flow has the following signature:
|
||||
|
||||
.. sourcecode:: kotlin
|
||||
@ -375,9 +372,6 @@ Capabilities of the contract upgrade flows
|
||||
* Equally, the state doesn't have to change at all
|
||||
* If a node has not yet run the contract upgrade authorisation flow, they will not be able to upgrade the contract
|
||||
and/or state objects
|
||||
* Upgrade authorisations can subsequently be deauthorised
|
||||
* Upgrades do not have to happen immediately. For a period, the two parties can use the old states and contracts
|
||||
side-by-side
|
||||
* State schema changes are handled separately
|
||||
|
||||
Writing new states and contracts
|
||||
@ -390,64 +384,19 @@ Writing new states and contracts
|
||||
value
|
||||
* Updated state objects can use the old contract code as long as there is no requirement to update it
|
||||
|
||||
Dealing with old contract code JAR files
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
* Currently, all parties **must** keep the old state and contract definitions on their node's classpath as they will
|
||||
always be required to verify transactions involving previous versions of the state using previous versions of the
|
||||
contract
|
||||
|
||||
* This will change when the contract code as an attachment feature has been fully implemented.
|
||||
|
||||
Permissioning
|
||||
^^^^^^^^^^^^^
|
||||
* Only node administrators are able to run the contract upgrade authorisation and deauthorisation flows
|
||||
|
||||
Logistics
|
||||
^^^^^^^^^
|
||||
* All nodes need to run the contract upgrade authorisation flow
|
||||
* All nodes need to run the contract upgrade authorisation flow to upgrade the contract and/or state objects
|
||||
* Only node administrators are able to run the contract upgrade authorisation and deauthorisation flows
|
||||
* Upgrade authorisations can subsequently be deauthorised
|
||||
* Only one node should run the contract upgrade initiation flow. If multiple nodes run it for the same ``StateRef``, a
|
||||
double-spend will occur for all but the first completed upgrade
|
||||
* Upgrades do not have to happen immediately. For a period, the two parties can use the old states and contracts
|
||||
side-by-side
|
||||
* The supplied upgrade flows upgrade one state object at a time
|
||||
|
||||
Serialisation
|
||||
-------------
|
||||
|
||||
Currently, the serialisation format for everything except flow checkpoints (which uses a Kryo-based format) is based
|
||||
upon AMQP 1.0, a self-describing and controllable serialisation format. AMQP is desirable because it allows us to have
|
||||
a schema describing what has been serialized alongside the data itself. This assists with versioning and deserialising
|
||||
long-ago archived data, among other things.
|
||||
|
||||
Writing classes
|
||||
~~~~~~~~~~~~~~~
|
||||
Although not strictly related to versioning, AMQP serialisation dictates that we must write our classes in a particular way:
|
||||
|
||||
* Your class must have a constructor that takes all the properties that you wish to record in the serialized form. This
|
||||
is required in order for the serialization framework to reconstruct an instance of your class
|
||||
* If more than one constructor is provided, the serialization framework needs to know which one to use. The
|
||||
``@ConstructorForDeserialization`` annotation can be used to indicate the chosen constructor. For a Kotlin class
|
||||
without the ``@ConstructorForDeserialization`` annotation, the primary constructor is selected
|
||||
* The class must be compiled with parameter names in the .class file. This is the default in Kotlin but must be turned
|
||||
on in Java (using the ``-parameters`` command line option to ``javac``)
|
||||
* Your class must provide a Java Bean getter for each of the properties in the constructor, with a matching name. For
|
||||
example, if a class has the constructor parameter ``foo``, there must be a getter called ``getFoo()``. If ``foo`` is
|
||||
a boolean, the getter may optionally be called ``isFoo()``. This is why the class must be compiled with parameter
|
||||
names turned on
|
||||
* The class must be annotated with ``@CordaSerializable``
|
||||
* The declared types of constructor arguments/getters must be supported, and where generics are used the generic
|
||||
parameter must be a supported type, an open wildcard (*), or a bounded wildcard which is currently widened to an open
|
||||
wildcard
|
||||
* Any superclass must adhere to the same rules, but can be abstract
|
||||
* Object graph cycles are not supported, so an object cannot refer to itself, directly or indirectly
|
||||
|
||||
Writing enums
|
||||
~~~~~~~~~~~~~
|
||||
Elements cannot be added to enums in a new version of the code. Hence, enums are only a good fit for genuinely static
|
||||
data that will never change (e.g. days of the week). A ``Buy`` or ``Sell`` flag is another. However, something like
|
||||
``Trade Type`` or ``Currency Code`` will likely change. For those, it is preferable to choose another representation,
|
||||
such as a string.
|
||||
|
||||
State schemas
|
||||
-------------
|
||||
State schema versioning
|
||||
-----------------------
|
||||
By default, all state objects are serialised to the database as a string of bytes and referenced by their ``StateRef``.
|
||||
However, it is also possible to define custom schemas for serialising particular properties or combinations of
|
||||
properties, so that they can be queried from a source other than the Corda Vault. This is done by implementing the
|
||||
@ -638,4 +587,43 @@ Then, in ``generateMappedObject``, add support for the new schema:
|
||||
}
|
||||
|
||||
With this approach, whenever the state object is stored in the vault, a representation of it will be stored in two
|
||||
separate database tables where possible - one for each supported schema.
|
||||
separate database tables where possible - one for each supported schema.
|
||||
|
||||
Serialisation
|
||||
-------------
|
||||
|
||||
The Corda serialisation format
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Currently, the serialisation format for everything except flow checkpoints (which uses a Kryo-based format) is based
|
||||
on AMQP 1.0, a self-describing and controllable serialisation format. AMQP is desirable because it allows us to have
|
||||
a schema describing what has been serialized alongside the data itself. This assists with versioning and deserialising
|
||||
long-ago archived data, among other things.
|
||||
|
||||
Writing classes that meet the serialisation format requirements
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Although not strictly related to versioning, AMQP serialisation dictates that we must write our classes in a particular way:
|
||||
|
||||
* Your class must have a constructor that takes all the properties that you wish to record in the serialized form. This
|
||||
is required in order for the serialization framework to reconstruct an instance of your class
|
||||
* If more than one constructor is provided, the serialization framework needs to know which one to use. The
|
||||
``@ConstructorForDeserialization`` annotation can be used to indicate the chosen constructor. For a Kotlin class
|
||||
without the ``@ConstructorForDeserialization`` annotation, the primary constructor is selected
|
||||
* The class must be compiled with parameter names in the .class file. This is the default in Kotlin but must be turned
|
||||
on in Java (using the ``-parameters`` command line option to ``javac``)
|
||||
* Your class must provide a Java Bean getter for each of the properties in the constructor, with a matching name. For
|
||||
example, if a class has the constructor parameter ``foo``, there must be a getter called ``getFoo()``. If ``foo`` is
|
||||
a boolean, the getter may optionally be called ``isFoo()``. This is why the class must be compiled with parameter
|
||||
names turned on
|
||||
* The class must be annotated with ``@CordaSerializable``
|
||||
* The declared types of constructor arguments/getters must be supported, and where generics are used the generic
|
||||
parameter must be a supported type, an open wildcard (*), or a bounded wildcard which is currently widened to an open
|
||||
wildcard
|
||||
* Any superclass must adhere to the same rules, but can be abstract
|
||||
* Object graph cycles are not supported, so an object cannot refer to itself, directly or indirectly
|
||||
|
||||
Writing enums
|
||||
~~~~~~~~~~~~~
|
||||
Elements cannot be added to enums in a new version of the code. Hence, enums are only a good fit for genuinely static
|
||||
data that will never change (e.g. days of the week). A ``Buy`` or ``Sell`` flag is another. However, something like
|
||||
``Trade Type`` or ``Currency Code`` will likely change. For those, it is preferable to choose another representation,
|
||||
such as a string.
|
Loading…
x
Reference in New Issue
Block a user