Update Contract Key docs (#5923)

* Update Contract Key docs

CHANGELOG_BEGIN
CHANGELOG_END

* Also update the Updates section

CHANGELOG_BEGIN
CHANGELOG_END

* Update docs/source/daml/reference/contract-keys.rst

Co-authored-by: Moritz Kiefer <moritz.kiefer@purelyfunctional.org>

* Incorporate feedback

CHANGELOG_BEGIN
CHANGELOG_END

Co-authored-by: Moritz Kiefer <moritz.kiefer@purelyfunctional.org>
This commit is contained in:
Bernhard Elsner 2020-05-11 14:18:19 +02:00 committed by GitHub
parent c3f6d85192
commit 1615a79aea
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 32 additions and 35 deletions

View File

@ -19,76 +19,72 @@ Here's an example of setting up a contract key for a bank account, to act as a b
What can be a contract key
**************************
The key can be an arbitrary expression that does **not** contain contract IDs. However, it **must** include every party that you want to use as a ``maintainer`` (see `Specifying maintainers`_ below).
The key can be an arbitrary serializable expression that does **not** contain contract IDs. However, it **must** include every party that you want to use as a ``maintainer`` (see `Specifying maintainers`_ below).
It's best to use simple types for your keys like ``Text`` or ``Int``, rather than a list or more complex type.
Specifying maintainers
**********************
If you specify a contract key for a template, you must also specify a ``maintainer`` or maintainers, in a similar way to specifying signatories or observers. However, maintainers are computed from the ``key`` instead of the template arguments. In the example above, the ``bank`` is ultimately the maintainer of the key. Maintainers are the parties that know about all of the keys that they are party to, and are used by the engine to guarantee uniqueness of contract keys. The maintainers **must** be signatories of the contract.
If you specify a contract key for a template, you must also specify a ``maintainer`` or maintainers, in a similar way to specifying signatories or observers. The maintainers "own" the key in the same way the signatories "own" a contract. Just like signatories of contracts prevent double spends or use of false contract data, maintainers of keys prevent double allocation or incorrect lookups. Since the key is part of the contract, the maintainers **must** be signatories of the contract. However, maintainers are computed from the ``key`` instead of the template arguments. In the example above, the ``bank`` is ultimately the maintainer of the key.
Keys are unique to their maintainers. For example, say you have a key that you're using as the identifer for a ``BankAccount`` contract. You might have ``key (bank, accountId) : (Party, Text)``. When you create a new bank account, the contract key ensures that no-one else can have an account with the same ``accountID`` at that bank. But that doesn't apply to other banks: for a contract with a different bank as maintainer, you could happily re-use that ``accountID``.
Uniqueness of keys is guaranteed per template. Since multiple templates may use the same key type, some key-related function must be annotated using the ``@ContractType`` as shown in the examples below.
When you're writing DAML models, the maintainers matter since they affect authorization -- much like signatories and observers. You don't need to do anything to "maintain" the keys.
When you are writing DAML models, the maintainers matter since they affect authorization -- much like signatories and observers. You don't need to do anything to "maintain" the keys. In the above example, it is guaranteed, there there can only be one ``Account`` with a given ``number`` at a given ``bank``.
Checking of the keys is done automatically at execution time, by the DAML exeuction engine: if someone tries to create a new contract that duplicates an existing contract key, the execution engine will cause that creation to fail.
Contract keys functions
***********************
Contract Lookups
****************
Contract keys introduce several new functions.
The primary purpose of contract keys is to provide a stable, and possibly meaningful, identifier for contracts that can be used in DAML to fetch contracts. There are two functions to perform such lookups: :ref:`fetchbykey` and :ref:`lookupbykey`. Both types of lookup are performed at interpretation time on the submitting Partipant Node, on a best-effort basis. Currently, that best-effort means lookups only return contracts if the submitting Party is a stakeholder of that contract.
In particular, the above means that if multiple commands are submitted simultaneously, all using contract lookups to find and consume a given contract, there will be contention between these commands, and at most one will succeed.
.. _fetchbykey:
``fetchByKey``
==============
fetchByKey
==========
``(fetchedContractId, fetchedContract) <- fetchByKey @ContractType contractKey``
Use ``fetchByKey`` to fetch the ID and data of the contract with the specified key. It is an alternative to the currently-used ``fetch``.
Use ``fetchByKey`` to fetch the ID and data of the contract with the specified key. It is an alternative to ``fetch`` and behaves the same in most ways.
It returns a tuple of the ID and the contract object (containing all its data).
``fetchByKey`` is authorized like ``fetch`` so it needs to be
authorized by at least one stakeholder. There are no restrictions on
the submitter.
Like ``fetch``, ``fetchByKey`` needs to be authorized by at least one stakeholder.
``fetchByKey`` fails and aborts the transaction if:
- Missing authorization, i.e., no authorization from a
stakeholder of the contract you are trying to fetch.
- The submitting Party is not a stakeholder on a contract with the given key, or
- A contract was found, but the ``fetchByKey`` violates the authorization rule, meaning no stakeholder authorized the ``fetch``..
This means that if it fails, it doesn't guarantee that a contract with that key doesn't exist, just that you can't see one.
Because different templates can use the same key type, you need to specify the type of the contract you are trying to fetch using the ``@ContractType`` syntax.
This means that if it fails, it doesn't guarantee that a contract with that key doesn't exist, just that the submitting Party doesn't know about it, or there are issues with authorization.
.. _lookupbykey:
``lookupByKey``
===============
lookupByKey
===========
``optionalContractId <- lookupByKey @ContractType contractKey``
Use ``lookupByKey`` to check whether a contract with the specified key exists. If it does exist, ``lookupByKey`` returns the ``Some contractId``, where ``contractId`` is the ID of the contract; otherwise, it returns ``None``.
``lookupByKey`` needs to be authorized by **all** maintainers of the
contract you are trying to lookup. There are no restrictions on the
submitter.
``lookupByKey`` needs to be authorized by **all** maintainers of the contract you are trying to lookup. The reason for that is denial of service protection. Without this restriction, a malicious Party could legitimately request another Party to validate any number of negative contract key lookups without that other Party ever having signed anything.
If the lookup fails (ie returns ``None``), this guarantees that no contract has this key.
Unlike ``fetchByKey``, the transaction **does not fail** if no contract with the given key is found. Instead, ``lookupByKey`` just returns ``None``. However, that does not guarantee that no such contract exists. The submitting Party may simply not know about it, in which case the transaction will be rejected during validation.
``lookupByKey`` fails and aborts the transaction if:
- Authorization from at least one maintainer is missing. This check fails at interpretation time.
- The lookup is incorrect. This can happen either due to contention, or because the submitter didn't know of the contract. This check fails at validation time.
Unlike ``fetchByKey``, the transaction **does not fail** if a contract with the key doesn't exist: instead, ``lookupByKey`` just returns ``None``.
To get the data from the contract once you've confirmed it exists, you'll still need to use ``fetch``.
Because different templates can use the same key type, you need to specify the type of the contract you are trying to fetch using the ``@ContractType`` syntax.
``exerciseByKey``
=================
exerciseByKey
*************
``exerciseByKey @ContractType contractKey``
Use ``exerciseByKey`` to exercise a choice on a contract identified by its ``key`` (compared to ``exercise``, which lets you exercise a contract identified by its ``ContractId``). To run ``exerciseByKey`` you need authorization from the controllers of the choice and at least one of the key maintainers.
Because different templates can use the same key type, you need to specify the type of the contract you are trying to fetch using the ``@ContractType`` syntax.
Use ``exerciseByKey`` to exercise a choice on a contract identified by its ``key`` (compared to ``exercise``, which lets you exercise a contract identified by its ``ContractId``). To run ``exerciseByKey`` you need authorization from the controllers of the choice and at least one stakeholder. This is equivalent to the authorization needed to fo a ``fetchByKey`` followed by an ``exercise``.

View File

@ -118,7 +118,8 @@ fetchByKey
- ``fetchByKey`` function.
- The same as ``fetch``, but fetches the contract instance with that :doc:`contract key </daml/reference/contract-keys>`, instead of the contract ID.
- As well as the authorization that ``fetch`` requires, you also need authorization from one of the ``maintainers`` of the key.
- Like ``fetch``, ``fetchByKey`` needs to be authorized by at least one stakeholder of the contract.
- Fails if no contract can be found.
.. _daml-ref-lookup-by-key:
@ -131,8 +132,8 @@ lookupByKey
- ``lookupByKey`` function.
- Use this to confirm that a contract with the given :doc:`contract key </daml/reference/contract-keys>` exists.
- If it does exist, ``lookupByKey`` returns the ``ContractId`` of the contract; otherwise, it returns ``None``. If it returns ``None``, this guarantees that no contract has this key. This does **not** cause the transaction to abort.
- **All** of the maintainers of the key must authorize the lookup (by either being signatories or by submitting the command to lookup), otherwise this will fail.
- If the submitting party is a stakeholder of a matching contract, ``lookupByKey`` returns the ``ContractId`` of the contract; otherwise, it returns ``None``. Transactions may fail due to contention because the key changes between the lookup and committing the transaction, or becasue the submitter didn't know about the existence of a matching contract.
- **All** of the maintainers of the key must authorize the lookup (by either being signatories or by submitting the command to lookup).
.. _daml-ref-abort: