mirror of
https://github.com/corda/corda.git
synced 2025-02-20 09:26:41 +00:00
CORDA-2684 Added new guide on CorDapp Constraints Migration procedures. (#4837)
* Added new guide on CorDapp Constraints Migration procedures. * Apply formatting and upper/lowercase changes. * Updated following PR review feedback from RGB and MH. * Minor clarification and cleanup. * Clarify step to ensure there is only one version ("signed") of the same Contracts CorDapp in the nodes /cordapp folder * Incorporating feedback from SS. * Replaced "propagate" with "transition". Adjust terminology to be consistent. * Removed confusing statement.
This commit is contained in:
parent
3bb996d22f
commit
f6ba9a0819
@ -24,7 +24,7 @@ to it.
|
||||
There are several types of constraint:
|
||||
|
||||
1. Hash constraint: exactly one version of the app can be used with this state.
|
||||
2. Zone whitelist constraint: the compatibility zone operator lists the hashes of the versions that can be used with this contract class name.
|
||||
2. Compatibility zone whitelisted (or CZ whitelisted) constraint: the compatibility zone operator lists the hashes of the versions that can be used with this contract class name.
|
||||
3. Signature constraint: any version of the app signed by the given ``CompositeKey`` can be used.
|
||||
4. Always accept constraint: any app can be used at all. This is insecure but convenient for testing.
|
||||
|
||||
@ -46,6 +46,8 @@ to issue the states was signed by Alice and Bob, every transaction must use an a
|
||||
the constraint used by equivalent output states (i.e. output states that use the same contract class name) must match the
|
||||
input state, so it can't be changed and you can't combine states with incompatible constraints together in the same transaction.
|
||||
|
||||
.. _implicit_vs_explicit_upgrades:
|
||||
|
||||
**Implicit vs explicit.** Constraints are not the only way to manage upgrades to transactions. There are two ways of handling
|
||||
upgrades to a smart contract in Corda:
|
||||
|
||||
@ -136,15 +138,15 @@ From there it's suspended waiting to be retried on node restart.
|
||||
This gives the node operator the opportunity to recover from those errors, which in the case of constraint violations means
|
||||
adding the right cordapp jar to the ``cordapps`` folder.
|
||||
|
||||
.. _relax_hash_constraints_checking_ref:
|
||||
|
||||
Hash constrained states in private networks
|
||||
-------------------------------------------
|
||||
|
||||
Where private networks started life using CorDapps with hash constrained states, we have introduced a mechanism to relax the checking of
|
||||
these hash constrained states when upgrading to signed CorDapps using signature constraints.
|
||||
|
||||
The following java system property may be set to relax the hash constraint checking behaviour:
|
||||
|
||||
-Dnet.corda.node.disableHashConstraints="true"
|
||||
The Java system property ``-Dnet.corda.node.disableHashConstraints="true"`` may be set to relax the hash constraint checking behaviour.
|
||||
|
||||
This mode should only be used upon "out of band" agreement by all participants in a network.
|
||||
|
||||
@ -191,58 +193,11 @@ During transaction building the ``AutomaticPlaceholderConstraint`` for output st
|
||||
will be selected based on a variety of factors so that the above holds true. If it can't find attachments in storage or there are no
|
||||
possible constraints, the ``TransactionBuilder`` will throw an exception.
|
||||
|
||||
Constraints migration to Corda 4
|
||||
--------------------------------
|
||||
|
||||
.. _constraints_whitelist_to_signature_ref:
|
||||
|
||||
How to use the ``SignatureAttachmentConstraint`` if states were already created on the network with the ``WhitelistedByZoneAttachmentConstraint``
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
1. As the original developer of the corDapp, the first step is to sign the latest version of the JAR that was released (see :doc:`cordapp-build-systems`).
|
||||
The key used for signing will be used to sign all subsequent releases, so it should be stored appropriately. The JAR can be signed by multiple keys owned
|
||||
by different parties and it will be expressed as a ``CompositeKey`` in the ``SignatureAttachmentConstraint`` (See :doc:`api-core-types`).
|
||||
Use `JAR signing and verification tool <https://docs.oracle.com/javase/tutorial/deployment/jar/verify.html>`_ to sign the existing JAR.
|
||||
The signing capability of :ref:`corda-gradle-plugins <cordapp_build_system_signing_cordapp_jar_ref>` cannot be used in this context as it signs the JAR while building it from source.
|
||||
|
||||
2. Whitelist this newly signed JAR with the Zone operator. The Zone operator should check that the JAR is signed and not allow any
|
||||
more versions of it to be whitelisted in the future. From now on the developer(s) who signed the JAR are responsible for new versions.
|
||||
|
||||
3. Any flows that build transactions using this Cordapp will have the responsibility of transitioning states to the ``SignatureAttachmentConstraint``.
|
||||
This is done explicitly in the code by setting the constraint of the output states to signers of the latest version of the whitelisted jar.
|
||||
In the near future we will make this transition automatic if we detect that the previous 2 steps were executed.
|
||||
|
||||
4. As a node operator you need to add the new signed version of the contracts cordapp to the "cordapps" folder together with the latest version of the flows jar
|
||||
that will contain code like:
|
||||
|
||||
.. container:: codeset
|
||||
|
||||
.. sourcecode:: kotlin
|
||||
|
||||
// This will read the signers for the deployed cordapp.
|
||||
val attachment = this.serviceHub.cordappProvider.getContractAttachmentID(contractClass)
|
||||
val signers = this.serviceHub.attachments.openAttachment(attachment!!)!!.signerKeys
|
||||
|
||||
// Create the key that will have to pass for all future versions.
|
||||
val ownersKey = signers.first()
|
||||
|
||||
val txBuilder = TransactionBuilder(notary)
|
||||
// Set the Signature constraint on the new state to migrate away from the WhitelistConstraint.
|
||||
.addOutputState(outputState, constraint = SignatureAttachmentConstraint(ownersKey))
|
||||
...
|
||||
|
||||
.. sourcecode:: java
|
||||
|
||||
// This will read the signers for the deployed cordapp.
|
||||
SecureHash attachment = this.getServiceHub().getCordappProvider().getContractAttachmentID(contractClass);
|
||||
List<PublicKey> signers = this.getServiceHub().getAttachments().openAttachment(attachment).getSignerKeys();
|
||||
|
||||
// Create the key that will have to pass for all future versions.
|
||||
PublicKey ownersKey = signers.get(0);
|
||||
|
||||
TransactionBuilder txBuilder = new TransactionBuilder(notary)
|
||||
// Set the Signature constraint on the new state to migrate away from the WhitelistConstraint.
|
||||
.addOutputState(outputState, myContract, new SignatureAttachmentConstraint(ownersKey))
|
||||
...
|
||||
|
||||
Please read :doc:`cordapp-constraint-migration` to understand how to consume and evolve pre-Corda 4 issued hash or CZ whitelisted constrained states
|
||||
using a Corda 4 signed CorDapp (using signature constraints).
|
||||
|
||||
Debugging
|
||||
---------
|
||||
@ -278,7 +233,7 @@ The same example in Java:
|
||||
});
|
||||
|
||||
|
||||
Staring a node missing CorDapp(s)
|
||||
Starting a node missing CorDapp(s)
|
||||
*********************************
|
||||
|
||||
When running the Corda node ensure all CordDapp JARs are placed in ``cordapps`` directory of each node.
|
||||
|
@ -376,6 +376,9 @@ automatically use them if your application JAR is signed. **We recommend all JAR
|
||||
with developer certificates is deployed to a production node, the node will refuse to start. Therefore to deploy apps built for Corda 4
|
||||
to production you will need to generate signing keys and integrate them with the build process.
|
||||
|
||||
.. note:: Please read the :doc:`cordapp-constraint-migration` guide to understand how to upgrade CorDapps to use Corda 4 signature constraints and consume
|
||||
existing states on ledger issued with older constraint types (e.g. Corda 3.x states issued with **hash** or **CZ whitelisted** constraints).
|
||||
|
||||
Step 10. Security: Package namespace handling
|
||||
---------------------------------------------
|
||||
|
||||
|
185
docs/source/cordapp-constraint-migration.rst
Normal file
185
docs/source/cordapp-constraint-migration.rst
Normal file
@ -0,0 +1,185 @@
|
||||
.. highlight:: kotlin
|
||||
.. role:: kotlin(code)
|
||||
:language: kotlin
|
||||
.. raw:: html
|
||||
|
||||
|
||||
<script type="text/javascript" src="_static/jquery.js"></script>
|
||||
<script type="text/javascript" src="_static/codesets.js"></script>
|
||||
|
||||
CorDapp constraints migration
|
||||
=============================
|
||||
|
||||
.. note:: Before reading this page, you should be familiar with the key concepts of :doc:`Contract Constraints <api-contract-constraints>`.
|
||||
|
||||
Corda 4 introduces and recommends building signed CorDapps that issue states with signature constraints.
|
||||
Existing on ledger states issued before Corda 4 are not automatically transitioned to new signature constraints when building transactions in Corda 4.
|
||||
This document explains how to modify existing CorDapp flows to explicitly consume and evolve pre Corda 4 states, and outlines a future mechanism
|
||||
where such states will transition automatically (without explicit migration code).
|
||||
|
||||
Faced with the exercise of upgrading an existing Corda 3.x CorDapp to Corda 4, you need to consider the following:
|
||||
|
||||
* What existing unconsumed states have been issued on ledger by a previous version of this CorDapp and using other constraint types?
|
||||
|
||||
If you have existing **hash** constrained states see :ref:`Migrating hash constraints<hash_constraint_migration>`.
|
||||
|
||||
If you have existing **CZ whitelisted** constrained states see :ref:`Migrating CZ whitelisted constraints<cz_whitelisted_constraint_migration>`.
|
||||
|
||||
If you have existing **always accept** constrained states these are not consumable nor evolvable as they offer no security and should only
|
||||
be used in test environments.
|
||||
|
||||
* What type of contract states does my CorDapp use?
|
||||
|
||||
**Linear states** typically evolve over an extended period of time (defined by the lifecycle of the associated business use case), and
|
||||
thus are prime candidates for constraints migration.
|
||||
|
||||
**Fungible states** are created by an issuer and transferred around a Corda network until explicitly exited (by the same issuer).
|
||||
They do not evolve as linear states, but are transferred between participants on a network. Their consumption may produce additional new
|
||||
output states to represent adjustments to the original state (e.g. change when spending cash). For the purposes of constraints migration,
|
||||
it is desirable that any new output states are produced using the new Corda 4 signature constraint types.
|
||||
|
||||
Where you have long transaction chains of fungible states, it may be advisable to send them back to the issuer for re-issuance (this is
|
||||
called "chain snipping" and has performance advantages as well as simplifying constraints type migration).
|
||||
|
||||
* Should I use the **implicit** or **explicit** upgrade path?
|
||||
|
||||
The general recommendation for Corda 4 is to use **implicit** upgrades for the reasons described :ref:`here <implicit_vs_explicit_upgrades>`.
|
||||
|
||||
**Implicit** upgrades allow pre-authorising multiple implementations of the contract ahead of time.
|
||||
They do not require additional coding and do not incur a complex choreographed operational upgrade process.
|
||||
|
||||
.. warning:: The steps outlined in this page assume you are using the same CorDapp Contract (eg. same state definition, commands and verification code) and
|
||||
wish to use that CorDapp to leverage the upgradeability benefits of Corda 4 signature constraints. If you are looking to upgrade code within an existing
|
||||
Contract CorDapp please read :ref:`Contract and state versioning<contract_upgrading_ref>` and :doc:`cordapp-upgradeability` to understand your options.
|
||||
|
||||
Please also remember that *states are always consumable if the version of the CorDapp that issued (created) them is installed*.
|
||||
In the simplest of scenarios it may be easier to re-issue existing hash or CZ whitelist constrained states (eg. exit them from the ledger using
|
||||
the original unsigned CorDapp and re-issuing them using the new signed CorDapp).
|
||||
|
||||
.. _hash_constraint_migration:
|
||||
|
||||
Hash constraints migration
|
||||
--------------------------
|
||||
|
||||
.. note:: These instructions only apply to CorDapp Contract JARs (unless otherwise stated).
|
||||
|
||||
Corda 4.0
|
||||
~~~~~~~~~
|
||||
|
||||
Corda 4.0 requires some additional steps to consume and evolve pre-existing on-ledger **hash** constrained states:
|
||||
|
||||
1. All Corda Nodes in the same CZ or business network that may encounter a transaction chain with a hash constrained state must be started using
|
||||
relaxed hash constraint checking mode as described in :ref:`relax_hash_constraints_checking_ref`.
|
||||
|
||||
2. CorDapp flows that build transactions using pre-existing *hash-constrained* states must explicitly set output states to use *signature constraints*
|
||||
and specify the related public key(s) used in signing the associated CorDapp Contract JAR:
|
||||
|
||||
.. container:: codeset
|
||||
|
||||
.. sourcecode:: kotlin
|
||||
|
||||
// This will read the signers for the deployed CorDapp.
|
||||
val attachment = this.serviceHub.cordappProvider.getContractAttachmentID(contractClass)
|
||||
val signers = this.serviceHub.attachments.openAttachment(attachment!!)!!.signerKeys
|
||||
|
||||
// Create the key that will have to pass for all future versions.
|
||||
val ownersKey = signers.first()
|
||||
|
||||
val txBuilder = TransactionBuilder(notary)
|
||||
// Set the Signature constraint on the new state to migrate away from the hash constraint.
|
||||
.addOutputState(outputState, constraint = SignatureAttachmentConstraint(ownersKey))
|
||||
|
||||
.. sourcecode:: java
|
||||
|
||||
// This will read the signers for the deployed CorDapp.
|
||||
SecureHash attachment = this.getServiceHub().getCordappProvider().getContractAttachmentID(contractClass);
|
||||
List<PublicKey> signers = this.getServiceHub().getAttachments().openAttachment(attachment).getSignerKeys();
|
||||
|
||||
// Create the key that will have to pass for all future versions.
|
||||
PublicKey ownersKey = signers.get(0);
|
||||
|
||||
TransactionBuilder txBuilder = new TransactionBuilder(notary)
|
||||
// Set the Signature constraint on the new state to migrate away from the hash constraint.
|
||||
.addOutputState(outputState, myContract, new SignatureAttachmentConstraint(ownersKey))
|
||||
|
||||
3. As a node operator you need to add the new signed version of the contracts CorDapp to the ``/cordapps`` folder together with the latest version of the flows jar.
|
||||
Please also ensure that the original unsigned contracts CorDapp is removed from the ``/cordapps`` folder (this will already be present in the
|
||||
nodes attachments store) to ensure the lookup code in step 2 retrieves the correct signed contract CorDapp JAR.
|
||||
|
||||
Later releases
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
The next version of Corda will provide automatic transition of *hash constrained* states. This means that signed CorDapps running on a Corda 4.x node will
|
||||
automatically propagate any pre-existing on-ledger *hash-constrained* states (and generate *signature-constrained* outputs) when the system property
|
||||
to break constraints is set.
|
||||
|
||||
.. _cz_whitelisted_constraint_migration:
|
||||
|
||||
CZ whitelisted constraints migration
|
||||
-------------------------------------
|
||||
|
||||
.. note:: These instructions only apply to CorDapp Contract JARs (unless otherwise stated).
|
||||
|
||||
Corda 4.0
|
||||
~~~~~~~~~
|
||||
|
||||
Corda 4.0 requires some additional steps to consume and evolve pre-existing on-ledger **CZ whitelisted** constrained states:
|
||||
|
||||
1. As the original developer of the CorDapp, the first step is to sign the latest version of the JAR that was released (see :doc:`cordapp-build-systems`).
|
||||
The key used for signing will be used to sign all subsequent releases, so it should be stored appropriately. The JAR can be signed by multiple keys owned
|
||||
by different parties and it will be expressed as a ``CompositeKey`` in the ``SignatureAttachmentConstraint`` (See :doc:`api-core-types`).
|
||||
|
||||
2. The new Corda 4 signed CorDapp JAR must be registered with the CZ network operator (as whitelisted in the network parameters which are distributed
|
||||
to all nodes in that CZ). The CZ network operator should check that the JAR is signed and not allow any more versions of it to be whitelisted in the future.
|
||||
From now on the development organisation that signed the JAR is responsible for signing new versions.
|
||||
|
||||
The process of CZ network CorDapp whitelisting depends on how the Corda network is configured:
|
||||
|
||||
- if using a hosted CZ network (such as `The Corda Network <https://docs.corda.net/head/corda-network/index.html>`_ or
|
||||
`UAT Environment <https://docs.corda.net/head/corda-network/UAT.html>`_ ) running an Identity Operator (formerly known as Doorman) and
|
||||
Network Map Service, you should manually send the hashes of the two JARs to the CZ network operator and request these be added using
|
||||
their network parameter update process.
|
||||
|
||||
- if using a local network created using the Network Bootstrapper tool, please follow the instructions in
|
||||
:ref:`Updating the contract whitelist for bootstrapped networks <bootstrapper_updating_whitelisted_contracts>` to can add both CorDapp Contract JAR hashes.
|
||||
|
||||
3. Any flows that build transactions using this CorDapp will have the responsibility of transitioning states to the ``SignatureAttachmentConstraint``.
|
||||
This is done explicitly in the code by setting the constraint of the output states to signers of the latest version of the whitelisted jar:
|
||||
|
||||
.. container:: codeset
|
||||
|
||||
.. sourcecode:: kotlin
|
||||
|
||||
// This will read the signers for the deployed CorDapp.
|
||||
val attachment = this.serviceHub.cordappProvider.getContractAttachmentID(contractClass)
|
||||
val signers = this.serviceHub.attachments.openAttachment(attachment!!)!!.signerKeys
|
||||
|
||||
// Create the key that will have to pass for all future versions.
|
||||
val ownersKey = signers.first()
|
||||
|
||||
val txBuilder = TransactionBuilder(notary)
|
||||
// Set the Signature constraint on the new state to migrate away from the WhitelistConstraint.
|
||||
.addOutputState(outputState, constraint = SignatureAttachmentConstraint(ownersKey))
|
||||
|
||||
.. sourcecode:: java
|
||||
|
||||
// This will read the signers for the deployed CorDapp.
|
||||
SecureHash attachment = this.getServiceHub().getCordappProvider().getContractAttachmentID(contractClass);
|
||||
List<PublicKey> signers = this.getServiceHub().getAttachments().openAttachment(attachment).getSignerKeys();
|
||||
|
||||
// Create the key that will have to pass for all future versions.
|
||||
PublicKey ownersKey = signers.get(0);
|
||||
|
||||
TransactionBuilder txBuilder = new TransactionBuilder(notary)
|
||||
// Set the Signature constraint on the new state to migrate away from the WhitelistConstraint.
|
||||
.addOutputState(outputState, myContract, new SignatureAttachmentConstraint(ownersKey))
|
||||
|
||||
4. As a node operator you need to add the new signed version of the contracts CorDapp to the ``/cordapps`` folder together with the latest version of the flows jar.
|
||||
Please also ensure that the original unsigned contracts CorDapp is removed from the ``/cordapps`` folder (this will already be present in the
|
||||
nodes attachments store) to ensure the lookup code in step 3 retrieves the correct signed contract CorDapp JAR.
|
||||
|
||||
Later releases
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
The next version of Corda will provide automatic transition of *CZ whitelisted* constrained states. This means that signed CorDapps running on a Corda 4.x node will
|
||||
automatically propagate any pre-existing on-ledger *CZ whitelisted* constrained states (and generate *signature* constrained outputs).
|
@ -20,10 +20,10 @@ The following guarantees are made for CorDapps running on Corda 4.0
|
||||
|
||||
- CorDapp Contract states generated on ledger using hash constraints are not directly migratable to signature constraints in this release.
|
||||
Your compatibility zone operator may whitelist a JAR previously used to issue hash constrained states, and then you can follow the manual
|
||||
process described in the paragraph below to migrate these to signature constraints.
|
||||
process described in the paragraph below to migrate these to signature constraints. See :doc:`cordapp-constraint-migration` for more information.
|
||||
|
||||
- CorDapp Contract states generated on ledger using CZ whitelisted constraints are migratable to signature constraints using a manual process
|
||||
that requires programmatic code changes. See :ref:`constraints_whitelist_to_signature_ref` for more information.
|
||||
that requires programmatic code changes. See :ref:`cz_whitelisted_constraint_migration` for more information.
|
||||
|
||||
- Explicit Contract Upgrades are only supported for hash and CZ whitelisted constraint types. See :ref:`explicit_contract_upgrades_ref` for more information.
|
||||
|
||||
|
@ -76,6 +76,8 @@ alongside the config files. For example, if your directory has this structure:
|
||||
The ``cordapp-a.jar`` and ``cordapp-b.jar`` will be installed in each node directory, and any contracts within them will be
|
||||
added to the Contract Whitelist (see below).
|
||||
|
||||
.. _bootstrapper_whitelisting_contracts:
|
||||
|
||||
Whitelisting contracts
|
||||
----------------------
|
||||
|
||||
@ -191,6 +193,8 @@ such a generated keys, will be unaffected.
|
||||
the same machine. If a network needs to be updated using the Bootstrapper once deployed, the nodes will need
|
||||
collecting back together.
|
||||
|
||||
.. _bootstrapper_updating_whitelisted_contracts:
|
||||
|
||||
Updating the contract whitelist for bootstrapped networks
|
||||
---------------------------------------------------------
|
||||
|
||||
|
@ -259,7 +259,7 @@ a drain is complete there should be no outstanding checkpoints or running flows.
|
||||
A node can be drained or undrained via RPC using the ``setFlowsDrainingModeEnabled`` method, and via the shell using
|
||||
the standard ``run`` command to invoke the RPC. See :doc:`shell` to learn more.
|
||||
|
||||
.. _explicit_contract_upgrades_ref:
|
||||
.. _contract_upgrading_ref:
|
||||
|
||||
Contract and state versioning
|
||||
-----------------------------
|
||||
@ -271,7 +271,12 @@ There are two types of contract/state upgrade:
|
||||
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 *explicit* upgrades.
|
||||
The general recommendation for Corda 4 is to use **implicit** upgrades for the reasons described :ref:`here <implicit_vs_explicit_upgrades>`.
|
||||
|
||||
.. _explicit_contract_upgrades_ref:
|
||||
|
||||
Performing explicit contract and state 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:
|
||||
@ -280,9 +285,6 @@ participants agree to the proposed upgrade. The following combinations of upgrad
|
||||
* A state is upgraded while the contract stays the same
|
||||
* The state and the contract are updated simultaneously
|
||||
|
||||
Performing explicit contract and state upgrades
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
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
|
||||
|
Loading…
x
Reference in New Issue
Block a user