diff --git a/core/src/main/kotlin/net/corda/core/contracts/AttachmentConstraint.kt b/core/src/main/kotlin/net/corda/core/contracts/AttachmentConstraint.kt index b2d86bc012..a5e5e7cd7d 100644 --- a/core/src/main/kotlin/net/corda/core/contracts/AttachmentConstraint.kt +++ b/core/src/main/kotlin/net/corda/core/contracts/AttachmentConstraint.kt @@ -108,7 +108,7 @@ object AutomaticPlaceholderConstraint : AttachmentConstraint { /** * An [AttachmentConstraint] that verifies that the attachment has signers that fulfil the provided [PublicKey]. - * See: [Signature Constraints](https://docs.corda.net/design/data-model-upgrades/signature-constraints.html) + * See: [Signature Constraints](https://docs.corda.net/api-contract-constraints.html#signature-constraints) * * @property key A [PublicKey] that must be fulfilled by the owning keys of the attachment's signing parties. */ diff --git a/core/src/main/kotlin/net/corda/core/internal/JarSignatureCollector.kt b/core/src/main/kotlin/net/corda/core/internal/JarSignatureCollector.kt index 38435916d5..5357cfbae6 100644 --- a/core/src/main/kotlin/net/corda/core/internal/JarSignatureCollector.kt +++ b/core/src/main/kotlin/net/corda/core/internal/JarSignatureCollector.kt @@ -52,7 +52,7 @@ object JarSignatureCollector { """ Mismatch between signers ${firstSignerSet.toOrderedPublicKeys()} for file $firstFile and signers ${otherSignerSet.toOrderedPublicKeys()} for file ${otherFile}. - See https://docs.corda.net/design/data-model-upgrades/signature-constraints.html for details of the + See https://docs.corda.net/api-contract-constraints.html#signature-constraints for details of the constraints applied to attachment signatures. """.trimIndent().replace('\n', ' ')) } diff --git a/docs/source/api-contract-constraints.rst b/docs/source/api-contract-constraints.rst index 7b2b039764..5b6a280346 100644 --- a/docs/source/api-contract-constraints.rst +++ b/docs/source/api-contract-constraints.rst @@ -1,4 +1,10 @@ .. highlight:: kotlin +.. role:: kotlin(code) + :language: kotlin +.. raw:: html + + + API: Contract Constraints ========================= @@ -7,6 +13,9 @@ API: Contract Constraints .. contents:: +Reasons for Contract Constraints +-------------------------------- + *Contract constraints* solve two problems faced by any decentralised ledger that supports evolution of data and code: 1. Controlling and agreeing upon upgrades @@ -21,51 +30,127 @@ This constraint specifies which versions of an application can be used to provid New versions released after a transaction is signed and finalised won't affect prior transactions because the old code is attached to it. -There are several types of constraint: - -1. Hash constraint: exactly one version of the app can be used with this state. -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. - -The actual app version used is defined by the attachments on a transaction that consumes a state: the JAR containing the state and contract classes, and optionally -its dependencies, are all attached to the transaction. Other nodes will download these JARs from a node if they haven't seen them before, -so they can be used for verification. The ``TransactionBuilder`` will manage the details of constraints for you, by selecting both constraints -and attachments to ensure they line up correctly. Therefore you only need to have a basic understanding of this topic unless you are -doing something sophisticated. - -The best kind of constraint to use is the **signature constraint**. If you sign your application it will be used automatically. -We recommend signature constraints because they let you smoothly migrate existing data to new versions of your application. -Hash and zone whitelist constraints are left over from earlier Corda versions before signature constraints were -implemented. They make it harder to upgrade applications than when using signature constraints, so they're best avoided. -Signature constraints can specify flexible threshold policies, but if you use the automatic support then a state will -require the attached app to be signed by every key that the first attachment was signed by. Thus if the app that was used -to issue the states was signed by Alice and Bob, every transaction must use an attachment signed by Alice and Bob. - -**Constraint propagation.** Constraints are picked when a state is created for the first time in an issuance transaction. Once created, -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 +Implicit vs Explicit Contract upgrades +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Constraints are not the only way to manage upgrades to transactions. There are two ways of handling upgrades to a smart contract in Corda: -1. *Implicit:* By pre-authorising multiple implementations of the contract ahead of time, using constraints. -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 article focuses on the first approach. To learn about the second please see :doc:`upgrading-cordapps`. +* **Implicit**: By pre-authorising multiple implementations of the contract ahead of time, using constraints. +* **Explicit**: By creating a special *contract upgrade transaction* and getting all participants of a state to sign it using the + contract upgrade flows. The advantage of pre-authorising upgrades using constraints is that you don't need the heavyweight process of creating upgrade transactions for every state on the ledger. The disadvantage is that you place more faith in third parties, who could potentially change the app in ways you did not expect or agree with. The advantage of using the explicit upgrade approach is that you can upgrade states regardless of their constraint, including in cases where you didn't -anticipate a need to do so. But it requires everyone to sign, requires everyone to manually authorise the upgrade, +anticipate a need to do so. But it requires everyone to sign, manually authorise the upgrade, consumes notary and ledger resources, and is just in general more complex. +This article focuses on the first approach. To learn about the second please see :doc:`upgrading-cordapps`. + .. _implicit_constraint_types: +Types of Contract Constraints +----------------------------- + +Corda supports several types of constraints to cover a wide set of client requirements: + +* **Hash constraint**: Exactly one version of the app can be used with this state. This prevents the app from being upgraded in the future while still + making use of the state created with the original version. +* **Compatibility zone whitelisted (or CZ whitelisted) constraint**: The compatibility zone operator lists the hashes of the versions that can be used with a contract class name. +* **Signature constraint**: Any version of the app signed by the given ``CompositeKey`` can be used. This allows app issuers to express the + complex social and business relationships that arise around code ownership. For example, a Signature Constraint allows a new version of an + app to be produced and applied to an existing state as long as it has been signed by the same key(s) as the original version. +* **Always accept constraint**: Any version of the app can be used. This is insecure but convenient for testing. + +.. _signature_constraints: + +Signature Constraints +--------------------- + +The best kind of constraint to use is the **Signature Constraint**. If you sign your application it will be used automatically. +We recommend signature constraints because they let you express complex social and business relationships while allowing +smooth migration of existing data to new versions of your application. + +Signature constraints can specify flexible threshold policies, but if you use the automatic support then a state will +require the attached app to be signed by every key that the first attachment was signed by. Thus if the app that was used +to issue the states was signed by Alice and Bob, every transaction must use an attachment signed by Alice and Bob. Doing so allows the +app to be upgraded and changed while still remaining valid for use with the previously issued states. + +More complex policies can be expressed through Signature Constraints if required. Allowing policies where only a number of the possible +signers must sign the new version of an app that is interacting with previously issued states. Accepting different versions of apps in this +way makes it possible for multiple versions to be valid across the network as long as the majority (or possibly a minority) agree with the +logic provided by the apps. + +Hash and zone whitelist constraints are left over from earlier Corda versions before Signature Constraints were +implemented. They make it harder to upgrade applications than when using signature constraints, so they're best avoided. + +Further information into the design of Signature Constraints can be found in its :doc:`design document `. + +Signing CorDapps for use with Signature Constraints +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Expanding on the previous section, for an app to use Signature Constraints, it must be signed by a ``CompositeKey`` or a simpler ``PublicKey``. +The signers of the app can consist of a single organisation or multiple organisations. Once the app has been signed, it can be distributed +across the nodes that intend to use it. + +Each transaction received by a node will then verify that the apps attached to it have the correct signers as specified by its +Signature Constraints. This ensures that the version of each app is acceptable to the transaction's input states. + +More information on how to sign an app directly from Gradle can be found in the +:ref:`CorDapp Jar signing ` section of the documentation. + +Using Signature Constraints in transactions +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +If the app is signed, Signature Constraints will be used by default (in most situations) by the ``TransactionBuilder`` when adding output states. +This is expanded upon in :ref:`contract_constraints_in_transactions`. + +.. note:: Signature Constraints are used by default except when a new transaction contains an input state with a Hash Constraint. In this + situation the Hash Constraint is used. + +.. _app_versioning_with_signature_constraints: + +App versioning with Signature Constraints +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Signed apps require a version number to be provided, see :doc:`versioning`. You can't import two different +JARs that claim to be the same version, provide the same contract classes and which are both signed. At runtime +the node will throw a ``DuplicateContractClassException`` exception if this condition is violated. + +Hash Constraints +---------------- + +Issues when using the HashAttachmentConstraint +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +When setting up a new network, it is possible to encounter errors when states are issued with the ``HashAttachmentConstraint``, +but not all nodes have that same version of the CorDapp installed locally. + +In this case, flows will fail with a ``ContractConstraintRejection``, and are sent to the flow hospital. +From there, they are 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 Java system property ``-Dnet.corda.node.disableHashConstraints="true"`` may be set to relax the hash constraint checking behaviour. For +this to work, every participant of the network must set the property to the same value. Therefore, this mode should only be used upon +"out of band" agreement by all participants in a network. + +.. warning:: This flag should remain enabled until every hash constrained state is exited from the ledger. + +.. _contract_state_agreement: + Contract/State Agreement ------------------------ @@ -118,39 +203,64 @@ CorDapp is 4 or greater, then transaction verification will fail with a ``Transa the owning ``Contract`` *can* be identified, but the ``ContractState`` has been bundled with a different contract, then transaction verification will fail with a ``TransactionContractConflictException``. -.. _contract_downgrade_rule_ref: +.. _contract_constraints_in_transactions: -App versioning with signature constraints ------------------------------------------ +Using Contract Constraints in Transactions +------------------------------------------ -Signed apps require a version number to be provided, see :doc:`versioning`. You can't import two different -JARs that claim to be the same version, provide the same contract classes and which are both signed. At runtime -the node will throw a ``DuplicateContractClassException`` exception if this condition is violated. +The app version used by a transaction is defined by its attachments. The JAR containing the state and contract classes, and optionally its +dependencies, are all attached to the transaction. Nodes will download this JAR from other nodes if they haven't seen it before, +so it can be used for verification. -Issues when using the HashAttachmentConstraint ----------------------------------------------- +The ``TransactionBuilder`` will manage the details of constraints for you, by selecting both constraints +and attachments to ensure they line up correctly. Therefore you only need to have a basic understanding of this topic unless you are +doing something sophisticated. -When setting up a new network, it is possible to encounter errors when states are issued with the ``HashAttachmentConstraint``, -but not all nodes have that same version of the CorDapp installed locally. +By default the ``TransactionBuilder`` will use :ref:`signature_constraints` for any issuance transactions if the app attached to it is +signed. -In this case, flows will fail with a ``ContractConstraintRejection``, and the failed flow will be sent to the flow hospital. -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. +To manually define the Contract Constraint of an output state, see the example below: -.. _relax_hash_constraints_checking_ref: +.. container:: codeset -Hash constrained states in private networks -------------------------------------------- + .. sourcecode:: java -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. + TransactionBuilder transaction() { + TransactionBuilder transaction = new TransactionBuilder(notary()); + // Signature Constraint used if app is signed + transaction.addOutputState(state); + // Explicitly using a Signature Constraint + transaction.addOutputState(state, CONTRACT_ID, new SignatureAttachmentConstraint(getOurIdentity().getOwningKey())); + // Explicitly using a Hash Constraint + transaction.addOutputState(state, CONTRACT_ID, new HashAttachmentConstraint(getServiceHub().getCordappProvider().getContractAttachmentID(CONTRACT_ID))); + // Explicitly using a Whitelisted by Zone Constraint + transaction.addOutputState(state, CONTRACT_ID, WhitelistedByZoneAttachmentConstraint.INSTANCE); + // Explicitly using an Always Accept Constraint + transaction.addOutputState(state, CONTRACT_ID, AlwaysAcceptAttachmentConstraint.INSTANCE); -The Java system property ``-Dnet.corda.node.disableHashConstraints="true"`` may be set to relax the hash constraint checking behaviour. + // other transaction stuff + return transaction; + } -This mode should only be used upon "out of band" agreement by all participants in a network. -Please also beware that this flag should remain enabled until every hash constrained state is exited from the ledger. + .. sourcecode:: kotlin + + private fun transaction(): TransactionBuilder { + val transaction = TransactionBuilder(notary()) + // Signature Constraint used if app is signed + transaction.addOutputState(state) + // Explicitly using a Signature Constraint + transaction.addOutputState(state, constraint = SignatureAttachmentConstraint(ourIdentity.owningKey)) + // Explicitly using a Hash Constraint + transaction.addOutputState(state, constraint = HashAttachmentConstraint(serviceHub.cordappProvider.getContractAttachmentID(CONTRACT_ID)!!)) + // Explicitly using a Whitelisted by Zone Constraint + transaction.addOutputState(state, constraint = WhitelistedByZoneAttachmentConstraint) + // Explicitly using an Always Accept Constraint + transaction.addOutputState(state, constraint = AlwaysAcceptAttachmentConstraint) + + // other transaction stuff + return transaction + } CorDapps as attachments ----------------------- @@ -168,12 +278,11 @@ node or will be automatically fetched over the network when receiving a transact app into multiple modules: one which contains just states, contracts and core data types. And another which contains the rest of the app. See :ref:`cordapp-structure`. - Constraints propagation ----------------------- As was mentioned above, the ``TransactionBuilder`` API gives the CorDapp developer or even malicious node owner the possibility -to construct output states with a constraint of his choosing. +to construct output states with a constraint of their choosing. For the ledger to remain in a consistent state, the expected behavior is for output state to inherit the constraints of input states. This guarantees that for example, a transaction can't output a state with the ``AlwaysAcceptAttachmentConstraint`` when the diff --git a/docs/source/app-upgrade-notes.rst b/docs/source/app-upgrade-notes.rst index e9a104b0a8..956a272e7a 100644 --- a/docs/source/app-upgrade-notes.rst +++ b/docs/source/app-upgrade-notes.rst @@ -102,7 +102,7 @@ properly for future releases. future not hold true. You should know the platform version of the node releases you want to target. The new ``versionId`` number is a version code for **your** app, and is unrelated to Corda's own versions. -It is used to informative purposes only. See ":ref:`contract_downgrade_rule_ref`" for more information. +It is used to informative purposes only. See ":ref:`app_versioning_with_signature_constraints`" for more information. **Split your app into contract and workflow JARs.** The duplication between ``contract`` and ``workflow`` blocks exists because you should split your app into two separate JARs/modules, one that contains on-ledger validation code like states and contracts, and one @@ -359,13 +359,13 @@ to be governed by a contract that is either: 1. The outer class of the state class, if the state is an inner class of a contract. This is a common design pattern. 2. Annotated with ``@BelongsToContract`` which specifies the contract class explicitly. -Learn more by reading ":ref:`implicit_constraint_types`". If an app targets Corda 3 or lower (i.e. does not specify a target version), +Learn more by reading :ref:`contract_state_agreement`. If an app targets Corda 3 or lower (i.e. does not specify a target version), states that point to contracts outside their package will trigger a log warning but validation will proceed. Step 9. Learn about signature constraints and JAR signing --------------------------------------------------------- -:doc:`design/data-model-upgrades/signature-constraints` are a new data model feature introduced in Corda 4. They make it much easier to +:ref:`signature_constraints` are a new data model feature introduced in Corda 4. They make it much easier to deploy application upgrades smoothly and in a decentralised manner. Signature constraints are the new default mode for CorDapps, and the act of upgrading your app to use the version 4 Gradle plugins will result in your app being automatically signed, and new states automatically using new signature constraints selected automatically based on these signing keys. diff --git a/docs/source/versioning.rst b/docs/source/versioning.rst index f5793da47d..8068a3c54d 100644 --- a/docs/source/versioning.rst +++ b/docs/source/versioning.rst @@ -99,6 +99,6 @@ sophisticated or proprietary business logic, machine learning models, even user being Corda flows or services. .. important:: The ``versionId`` specified for the JAR manifest is checked by the platform and is used for informative purposes only. - See ":ref:`contract_downgrade_rule_ref`" for more information. + See ":ref:`app_versioning_with_signature_constraints`" for more information. .. note:: You can read the original design doc here: :doc:`design/targetversion/design`. \ No newline at end of file diff --git a/testing/test-utils/src/test/kotlin/net/corda/testing/core/JarSignatureCollectorTest.kt b/testing/test-utils/src/test/kotlin/net/corda/testing/core/JarSignatureCollectorTest.kt index aede6afef2..53ca08d444 100644 --- a/testing/test-utils/src/test/kotlin/net/corda/testing/core/JarSignatureCollectorTest.kt +++ b/testing/test-utils/src/test/kotlin/net/corda/testing/core/JarSignatureCollectorTest.kt @@ -110,7 +110,7 @@ class JarSignatureCollectorTest { """ Mismatch between signers [O=Alice Corp, L=Madrid, C=ES, O=Bob Plc, L=Rome, C=IT] for file _signable1 and signers [O=Bob Plc, L=Rome, C=IT] for file _signable2. - See https://docs.corda.net/design/data-model-upgrades/signature-constraints.html for details of the + See https://docs.corda.net/api-contract-constraints.html#signature-constraints for details of the constraints applied to attachment signatures. """.trimIndent().replace('\n', ' ') ) { dir.getJarSigners(FILENAME) }